iCLEANED UX review · 29 Sep 2026

iCLEANED web app: adversarial UI/UX review before the user-testing workshop

  • Date: 2026-09-29
  • Source reviewed: icleaned_review_src at fb8498c (CIAT/icleaned; merge of APPLITICS#16).
  • Live app: https://icleaned.alliance.cgiar.org/.
    • First pass: login page only; the reviewer may not enter credentials.
    • Second pass: a full walkthrough in your logged-in Chrome session, from New Scenario to Run (four runs). See "Live walkthrough log" below.
  • Model issues: cross-referenced to ../01_climate_ghg.md, ../02_livestock.md, ../03_landuse.md and ../04_package_code.md. 00_summary.md and 05_app_relevance_and_workshop.md did not exist when this was written.
  • Paths: every file path below is relative to icleaned_review_src/. Package paths are relative to cleaned_review_src/.

Verdict labels

  • CONFIRMED means observed live on icleaned.alliance.cgiar.org by the reviewer (login page only, first pass).
  • CONFIRMED (live) means reproduced by the reviewer in a live walkthrough on 2026-09-29, in your logged-in Chrome session, using the approved throwaway scenario ZZ_UXREVIEW_DELETE_ME (Southern Highland Tanzania Dairy DB, 2 seasons, 12 local cows + 8 calves, Rhodes grass, NPK). Four runs were made. No other scenarios were touched, and consent was declined.
  • CONFIRMED (user) means reported by you from your own live walkthrough on the same day.
  • REFUTED (live) means a code-based claim that the live app contradicted.
  • PLAUSIBLE means found in the code only.
  • PLAUSIBLE + probe means the input behaviour was also reproduced in a throwaway local Shiny/DT app. That app used the same DT 0.33 widgets, ran on localhost in Chromium with an English locale, and has since been stopped.

For every finding the reviewer searched for a refutation, meaning handling code elsewhere. The result is recorded under "Refutation" wherever it changed the verdict or the severity.

Verdict (5 sentences)

  1. A first-time user can click through Farm → Livestock → Feed Production → Livestock Feeding → Run, but the result is wrong or empty unless they override hidden defaults. In the live walkthrough, a complete-looking scenario built with the app's own defaults produced four empty charts (all indicators 0, including enteric CH₄), with no message:
    • all validation is advisory, and nothing blocks a run;
    • a comma decimal became a 10× value (200,5 → 2005);
    • "first row of the lookup" defaults silently zero model terms: manure CH₄ ("Pasture / range / paddock"), land area (removal 0), erosion (land cover "Dense forest");
    • the climate list lacks Cool Temperate and Boreal, even for the Mongolia and Uzbekistan DBs.
  2. The app has no error handling at all: tryCatch, validate and need appear nowhere. The model run is synchronous in the Shiny process. So one participant's bad input can grey-screen their session, and with ~20 people clicking "Run" together every session on the same R worker will freeze.
  3. The comparison dashboard can mislead experts as well as novices. Relative (%) charts keep the absolute unit on the y-axis. A zero baseline shows as "0 %". A negative baseline (N balance, carbon balance) flips the sign of the change. The GHG axis is literally labelled "ghg_emission".
  4. Two guide steps have no UI at all: extrapolation (§3.2) and benchmarks (§3.4). "New Parameters Database" ignores the database the guide tells users to pick. Help is hover-only and there is almost none of it: 0 "?" icons on the Farm › Area slide and none on any table column. There is no data dictionary, no "don't know" option, and no geospatial prefill. A first-time user will not finish a meaningful scenario without the PDF and a facilitator (see "Redesign directions").
  5. Recommendation: run the workshop on pre-built template scenarios, announce the known issues below, stagger the runs, and treat numeric results as illustrative. The Blocker fixes are mostly small and local (UX-01 to UX-09, UX-76, UX-77). The three "first row of lookup" defaults and the climate list are one-line data or code fixes that should go in before the session.

Findings table

Counts: 12 Blocker, 33 High, 32 Medium, 10 Low, plus 1 refuted (88 rows). CONFIRMED (live), from the reviewer's walkthrough: UX-03, 05, 08, 09, 10, 13, 16, 17, 19, 26, 36, 39, 50, 74–88. CONFIRMED (user): UX-28, 30, 61–75. CONFIRMED: UX-58/59 (login page). REFUTED (live): UX-27. All others are PLAUSIBLE (code only).

ID Sev Verdict Screen / step file:line Issue
UX-01 Blocker PLAUSIBLE Dashboard › Run; Compare; load scenario R/10_mod_board_simulation_server.R:160-355; R/20_mod_board_comparison_server.R:133-203; no tryCatch anywhere Any model or JSON error is uncaught inside an observer. The session dies (grey "disconnected" screen), or a raw R error shows in the plot area
UX-02 Blocker PLAUSIBLE Dashboard › Run (many users) R/10_mod_board_simulation_server.R:174-300 The run is synchronous, with no ExtendedTask/future. It blocks every session sharing the R process, which is fatal when a whole room clicks Run together
UX-03 Blocker CONFIRMED (live) Livestock › Add livestock R/30_mod_scenario_server.R:987,997-1005; pkg R/ghg_emission.R:185-194,237-245 Every new animal defaults to manure system "Pasture / range / paddock". IPCC tables join on "Pasture/Range/Paddock", so the join returns NA and the manure CH₄/N₂O term is silently lost Live: manure bar = 0 with default; switching stable system to "Solid storage" made it appear (41 px vs ~4 px expected for a real PRP MCF).
UX-04 Blocker PLAUSIBLE Livestock table R/30_mod_scenario_init.R:72; R/30_mod_scenario_server.R:1007 manure_onfarm_fraction is labelled "Collection of manure during off-farm grazing". It multiplies all collected manure, and its default of 0 zeroes manure N
UX-05 Blocker CONFIRMED (live) All numeric inputs and DT cells R/helpers_shiny.R:201-203; R/30_mod_scenario_server.R:2567 Blank DT cell → 0. Blank numericInput → the string "NA" in the JSON. "1,5" → 15 (probe). No min/max on any of 28 numericInputs Live: 200,5 in a Days cell → 2005; 1,5 in compost numericInput → 15; all 28 numeric fields start at 0.
UX-06 Blocker PLAUSIBLE Feed Production › edit table R/30_mod_scenario_server.R:2527-2533, 1860-1871 Duplicate feed/crop name → autosave is silently skipped (stderr only). Run then simulates the stale saved JSON, although the banner says the simulation "will fail"
UX-07 Blocker PLAUSIBLE Comparison › relative mode R/20_mod_board_comparison_ui.R:201-206; R/graphs.R:373-375; pkg R/compare_scenario.R:50-54 The y-axis keeps the absolute unit on % bars. Baseline 0 → "0 %". A negative baseline flips the sign (divides by base, not abs(base))
UX-08 Blocker CONFIRMED (live) Dashboard › Run / Compare R/30_mod_scenario_validation.R:200-257; R/10_mod_board_simulation_server.R:160-170 All validation (seasons ≠ 365 days, feeding ≠ 100 %, time fractions ≠ 1) is a banner only. Run never checks validity Live: ran with calves at 0 % feed in Short rains and 100 000 kg milk; no block.
UX-09 Blocker CONFIRMED (live) Feed Production › Add feed R/30_mod_scenario_server.R:1449,1546-1548 New feed: main_product_removal = 0. For non-residue rows, residue removal is forced to 0. So land requirement = 0 ha, with no warning Live: complete scenario with default removal = 0 produced four empty charts (GHG, N, land, water all 0, incl. enteric CH₄); setting 0.9 gave GHG ≈ 7.3, land ≈ 4 ha, N ≈ −840 kg.
UX-10 High CONFIRMED (live) Farm › Area R/30_mod_scenario_ui.R:614-630; R/30_mod_scenario_validation.R:50-91 Precipitation and rainy-season length default to 0 and have no validation. Erosivity divides by rain_length, giving NaN/Inf, then 0 Live: rainy season = 14 months and precipitation = 0 accepted, no warning.
UX-11 High PLAUSIBLE Farm › Area (soil) R/30_mod_scenario_validation.R:50-91 Soil N/C/clay/bulk density/depth are checked only for ≥ 0, and default to 0. SOC stock = 0 without a warning
UX-12 High PLAUSIBLE Livestock / Feed tables (orange columns) R/30_mod_scenario_init.R:265-305; R/helpers_shiny.R:276-303 DB-parameter columns look editable, but they are overwritten from the parameters DB on load or DB change. User edits are silently reverted
UX-13 High CONFIRMED (live) Farm › Fertilizer R/30_mod_scenario_server.R:783-784,865-878 NPK gets %N = 0 by default, with no warning. Removing a fertilizer leaves its kg/ha in the crop inputs, and the stale amounts are saved Live: NPK added with 0 % N, no warning.
UX-14 High PLAUSIBLE Deployment renv.lock:717-762; global.R:15-17 renv pins a fork (M-Emmanuel/cleaned-staging-v2, labelled 0.6.0), and global.R falls back to CIAT/cleaned@cleaned_v0.6.0. The field names the app writes match v0.7.0 only. On true 0.6.0, fertilizer N reads the hidden fraction = 0
UX-15 High PLAUSIBLE Livestock Feeding R/30_mod_scenario_validation.R:547-552; R/30_mod_scenario_server.R:2417-2453 Negative %s are accepted (−50 + 150 = 100 passes). Renaming a season, feed or animal silently resets its allocations to 0, and can error in rbind
UX-16 High CONFIRMED (live) Dashboard › Run picker R/10_mod_board_simulation_server.R:15-24 The Run picker preselects the alphabetically first scenario, not the one just edited, so participants run the wrong scenario Live: Dashboard preselected New_file.json after editing ZZ scenario.
UX-17 High CONFIRMED (live) Dashboard › GHG chart data/iCLEANED - Graphs Information.xlsx sheet 2 row 1; R/10_mod_board_simulation_ui.R:167-170; R/graphs.R:48-58 The y-axis/unit text reads "ghg_emission". The values are t CO₂e per ha, which is not stated
UX-18 High PLAUSIBLE Dashboard › Water pie R/graphs.R:303-305; R/10_mod_board_simulation_server.R:577-585 Unnamed scale_fill_manual gives alphabetical colour order, while the HTML legend uses data order. Legend colours can point to the wrong feed
UX-19 High CONFIRMED (live) Scenario › Share to pool R/30_mod_scenario_server.R:263,274-279; R/helpers_shiny.R:348-363 Share overwrites another user's pooled JSON (overwrite = TRUE). If a DB of the same name exists, the DB is not copied, so the JSON is paired with someone else's DB Live: the app's own tooltip warns "someone could overwrite your file".
UX-20 High PLAUSIBLE Scenario › Rename; DB rename R/30_mod_scenario_server.R:336-349; R/40_mod_params_db_server.R:224-240 The new name is not sanitised (../, /), which allows path traversal into other users' folders. Renaming a DB silently unbinds its scenarios (they fall back to the default DB, with a 5 s toast)
UX-21 High PLAUSIBLE Guide §3.2 / §3.4 grep: 0 hits Extrapolation and benchmarks are described in the guide but absent from the UI. The guide does not say they are manual or offline
UX-22 High PLAUSIBLE Edit Parameters › New Parameters Database R/40_mod_params_db_server.R:189-199; global.R:49 It always clones "Southern Highland Tanzania Dairy", whatever DB the user selected. The guide says to select a regional DB, then click New
UX-23 High PLAUSIBLE First load / consent www/js/bg-modal.js:164; server.R:120-134,296-305; R/helpers_shiny.R:224-238 Every visitor's IP goes to api.ipify.org before consent. Consent is re-asked every session. On "Yes", email + IP are stored together, which the consent text does not disclose
UX-24 High PLAUSIBLE Whole app, keyboard ui.R:33-35,52-66; R/30_mod_scenario_ui.R:23-29 Nav links, the logo and "Edit Parameters" are <a>/<div onclick> with no href or tabindex, so they are not keyboard-reachable. There are 0 aria-/role attributes in the app
UX-25 High PLAUSIBLE All "?" help www/sass/app.scss:255-308 Tooltips appear on CSS :hover only. They cannot be opened by touch or keyboard, and they are display:none to assistive tech
UX-26 High CONFIRMED (live) Farm/Livestock forms R/30_mod_scenario_ui.R (~45× label = NULL, e.g. :301-306, :384-389) Visible labels are h2s, not <label>s. Screen readers get unlabelled fields and ~80 fake headings Live: Add-season textbox exposes accessible name "(no name)".
UX-27 — REFUTED (live) Scenario input screens R/helpers_shiny.R:16-22 The GUIDE button is hidden on the Scenario and Edit Parameters screens, which is exactly where users need it Live: GUIDE button visible on Scenario (at load and after returning from Dashboard) and on Dashboard; may still hide on Edit Parameters (not tested).
UX-28 High CONFIRMED (user) Tables (Livestock ~50 cols, Feed ~30) R/30_mod_scenario_init.R:3-103; R/30_mod_scenario_server.R:1086-1141 Data-entry columns have no tooltips (0 on any table header). Units are missing on several columns (see UX-33). Constraints such as "fractions sum to 1" appear only after the error
UX-29 High PLAUSIBLE Livestock R/30_mod_scenario_server.R:1010-1011 Small-ruminant weaning and one-year weights default to 0. They are styled as DB parameters but not synced, and there is no warning
UX-30 Medium CONFIRMED (user) Farm › Seasons R/30_mod_scenario_server.R:648-651,734-741 A sum ≠ 365 is only warned. A new season gets max(365 − sum, 0), so a 0-day season is added silently. Duplicate names typed into a cell are accepted
UX-31 Medium PLAUSIBLE Livestock / Feed › Add R/30_mod_scenario_server.R:985,1435 A duplicate add is silently ignored, with no toast
UX-32 Medium PLAUSIBLE Livestock (pigs) R/30_mod_scenario_init.R:83; pkg R/energy_requirement.R:134 "Proportion growth piglets covered by milk (%)" is entered as 60, but the package uses it as a fraction, so CP comes out ~100×
UX-33 Medium PLAUSIBLE Tables R/30_mod_scenario_init.R:19,25,56,91-92 Units are missing or misleading: "Number" (herd), "Average N content manure" (a fraction), "Cultivation period", "Energy content milk/meat", "Trees DBH", "dry yield (t DM/ha)" without "/year"
UX-34 Medium PLAUSIBLE Feed Production › Rice R/30_mod_scenario_server.R:1535,2090 Rice inputs only apply when crop_name == "Rice" exactly. Two rice feeds are allowed, and they crash the model (pkg F16)
UX-35 Medium PLAUSIBLE Farm carousel R/30_mod_scenario_ui.R:291-910; www/sass/app.scss:795-798 The 5 Farm sub-sections are carousel slides, reached only by arrows, with no indicators or slide count. Validation banners sit on individual slides and are easy to miss
UX-36 Medium CONFIRMED (live) Tables R/30_mod_scenario_server.R:1086,1605,1910 Cells are edited by double-click only, with no KeyTable. Nothing on screen says "double-click to edit" Live: Enter does not commit a DT edit; value commits only on blur.
UX-37 Medium PLAUSIBLE Autosave R/30_mod_scenario_server.R:2516-2659 The whole JSON is rewritten on every keystroke, with no debounce and no "saved" indicator. On a slow link every edit is a round trip plus a disk write
UX-38 Medium PLAUSIBLE Switching scenario R/30_mod_scenario_server.R:2669-2736,2889 Possible race: autosave writes the new file name with the old scenario's scalar inputs. basket_data <- list() is local, so stale allocations persist
UX-39 Medium CONFIRMED (live) Disconnect / reload / back ui.R:154-156; R/helpers_shiny.R:13-27; www/js/auth-url-cleanup.js:13 No reconnect or bookmarking. A reload lands on Scenario with nothing selected. The URL never changes, so the browser Back button leaves the app Live: URL stays / across all screens.
UX-40 Medium PLAUSIBLE Two tabs, same user R/30_mod_scenario_server.R:2646-2650 Last writer wins. A stale tab overwrites newer edits on its next keystroke
UX-41 Medium PLAUSIBLE Params DB tables R/40_mod_params_db_server.R:812-829,965-995 Row delete has no confirmation. Cell edits are written to disk immediately. No undo anywhere, and JSON deletes are permanent
UX-42 Medium PLAUSIBLE Dashboard › traceability R/10_mod_board_simulation_server.R:38-48; server.R:187-194 No run timestamp, input version or DB is shown with the results. "Last scenario created" is really the autosave mtime. Comparisons go stale silently after a re-run
UX-43 Medium PLAUSIBLE Downloads R/10_mod_board_simulation_server.R:358-432; R/20_mod_board_comparison_server.R:207-264 The ZIP omits the input JSON, includes an internal .rds, and offers no CSV. The comparison ZIP/PNG names don't encode the base scenario (abs vs %)
UX-44 Medium PLAUSIBLE Dashboard (single run) R/10_mod_board_simulation_ui.R:131-317 vs guide ~L1077 The guide promises an indicator dropdown per run. The single-run view has 4 fixed charts; the 44 indicators exist only in Comparison
UX-45 Medium PLAUSIBLE N balance chart R/graphs.R:136-140 A non-interactive geom_bar drawn over the interactive layer blocks the tooltips
UX-46 Medium PLAUSIBLE Charts, colour R/graphs.R:10-15 The 20-colour palette is not colour-blind safe (red/green, near-duplicate greens and blues). More than 20 feeds gives NA colours
UX-47 Medium PLAUSIBLE Contrast www/sass/app.scss:6,48-49,422,801-810; typo.scss:86 Nav links 2.2:1, inactive tabs 2.0:1, placeholder 1.6:1, GUIDE button 3.2:1, success button 3.25:1, focus ring 1.6:1 (all WCAG AA failures)
UX-48 Medium PLAUSIBLE Small screens www/sass/app.scss:209-212,1003-1008,809; typo.scss:61 Modals are 60 % wide with 100 px side padding, tabs are 290 px wide and buttons have 80 px padding, so the layout is unusable below ~900 px. Only 2 media queries
UX-49 Medium PLAUSIBLE Onboarding server.R:88-119 The user folder is created before the modal, so a reload skips onboarding forever. Empty answers are accepted
UX-50 Medium CONFIRMED (live) Translation whole app No i18n layer. ~150 UI strings and ~125 messages are hard-coded English. LC_TIME="C" forced. No lang attribute Live: <html lang="">.
UX-51 Medium PLAUSIBLE Comparison config R/20_mod_board_comparison_server.R:238,356,391; _ui.R:126,176 indicator[5:48] is hard-coded, while the xlsx says rows may be reordered. Editing the xlsx silently breaks the charts shown
UX-52 Low PLAUSIBLE Progress bar R/10_mod_board_simulation_server.R:178 incProgress runs before each scenario, so the bar shows 100 % while the last run is still computing
UX-53 Low PLAUSIBLE Help modal www/html/helper.html:3,7,17 Describes a "desktop application". No guide link, no contact email. MS Form iframe is 720 px fixed
UX-54 Low PLAUSIBLE Guide wording guide ~L531, 673, 807, 877 Labels differ: "farm code" (absent), "Herd composition" vs "Number", "Calving interval" vs "Parturition interval", "Feeding Periods" vs "Seasons", "JSON Input" (not on screen)
UX-55 Low PLAUSIBLE Shared pool search R/30_mod_scenario_server.R:519-522 Requires the exact file name; no browsing
UX-56 Low PLAUSIBLE Payload www/img/logo.png (72 KB at 1037 px, shown ~82 px); ui.R:20-27 CSS/JS are inlined, so not cacheable. The oversized logo doubles as the favicon
UX-57 Low PLAUSIBLE Copy R/10_mod_board_simulation_ui.R:29; ui.R:180 "andclick" typo; "Copyright ©" with no year or holder; "click here" link text
UX-76 Blocker CONFIRMED (live) + code Farm › Climate all 14 data/primary_database/*/lkp_climate.csv; pkg inst/extdata/ghg_parameters.json Table 10.17 The climate picker offers only 6 zones (Tropical ×4, Warm Temperate ×2) in every DB, including Mongolia, Uzbekistan and Nepal. The package supports Cool Temperate and Boreal (moist/dry). Users in those systems must pick a wrong zone, which gives wrong MCFs
UX-77 Blocker CONFIRMED (live) + code Feed Production › Add feed R/30_mod_scenario_server.R:1445,1460,1488 (lkp_landcover[1]) Every new feed/crop defaults to land cover "Dense forest" (C factor 0.001), e.g. Rhodes grass forage. Soil erosion is driven to ~0 unless the user finds column 7 of a 50-column table. Same "first row of lookup" anti-pattern as UX-03
UX-78 High CONFIRMED (live) Dashboard › results R/graphs.R:74-82; R/10_mod_board_simulation_server.R:488-590 All-zero results render as blank charts on a default 0–0.100 axis, with no message, empty state or diagnostic ("land requirement is 0 ha: check main product removal"). A user cannot tell "no emissions" from "model got nothing"
UX-79 High CONFIRMED (live) + code Feed Production › feed list (Southern Highland Tanzania Dairy) data/primary_database/Southern Highland Tanzania Dairy/lkp_feeditem.csv The default dairy DB offers 11 feeds and no Napier grass, the dominant planted forage in that system. Rhodes grass forage DM = 93.97 % (hay-like, not fresh), so as-fed/DM confusion is baked into the defaults. File as a data issue
UX-80 Medium CONFIRMED (live) Scenario › New Scenario R/30_mod_scenario_server.R (create_new_json) "New Scenario" instantly creates New_file (1).json, with no name or purpose prompt and no template choice. The DB is silently set to Southern Highland Tanzania Dairy. The toast says "The JSON has been created successfully!" (file-format jargon)
UX-81 Low CONFIRMED (live) Scenario picker / top bar ui.R:130-133; picker rendering Underscores display doubled (ZZ__UXREVIEW__DELETE__M…) and names are truncated. "Last scenario created: .json" did not change after a new scenario was created and renamed
UX-82 Medium CONFIRMED (live) Whole app (1036 px window) www/sass/app.scss (alerts, .help, carousel overflow) Unstable layout causes misclicks. Validation banners grow and shrink above tables, shifting every row. The "?" tooltip covers the scenario picker and Rename button. The Climate dropdown is clipped inside the carousel (3 of 6 options visible), doesn't close on Esc, and hides the Farm-name field. Header nav wraps into a vertical stack. Tabs wrap 2×2 at half width, and more than one tab can look active
UX-83 Medium CONFIRMED (live) Farm › Area (soil, land management) lkp_soil.csv, lkp_croplandsystem.csv, lkp_tillageregime.csv Only 9 soil types (no Ferralsol, Luvisol, Arenosol, Leptosol, Gleysol, which are common in SSA and SE Asia), plus the obsolete FAO-74 "Xerosol". "Estimated K Value" is unexplained (it is USLE erodibility). Cropland system and tillage options restate the climate ("tropical, moist/wet", "temperate/boreal, dry"), but nothing checks them against the Climate picker
UX-84 Medium CONFIRMED (live) Farm › Fertilizer; Feed Production › Crop inputs R/30_mod_scenario_init.R:334-341; R/30_mod_scenario_server.R:1878-1937 7 fertiliser types (no CAN, TSP/SSP, MOP, "other"). The Farm tab collects only type and % N; application rates (kg/ha) sit in a "Crop inputs" table on another tab, with no signposting. All 7 product columns appear there whatever was selected
UX-85 High CONFIRMED (live) Livestock / Feed tables validation R/30_mod_scenario_server.R:1177-1290 No cross-field logic. Accepted without comment: 100 % of time in stable with 0 % manure collected and a "Pasture" system for stable manure; milk for calves; 14 tree/DBH columns for a grass forage; "Main product" source with 0 removal
UX-86 Medium CONFIRMED (live) Feed Production R/30_mod_scenario_ui.R:1020-1080 The table headed "Crop areas and residue management" has no area field. Area is only derived from demand ÷ yield. Users who know their plot sizes can neither enter nor check them
UX-87 Low CONFIRMED (live) Copy / labels various "Selected a Feed" / "Selected a Crop"; "coefficiencies"; row label "…forage of Rhodes"; season re-cased ("Long rains" → "Long Rains"); "Soil" appears twice on the GHG x-axis; "Feed Item" axis title floats under the N chart; lower-case "water regime", "rice ecosystem type"; inconsistent "(kgs)"
UX-88 Medium CONFIRMED (live) Dashboard › DMI warning R/10_mod_board_simulation_server.R:251-269 "daily dry matter intake greater than 4% of their body weight: Cattle – Cows (local)" gives no cause, consequence or fix. It did not fire for calves producing 100 000 kg milk
UX-58 Medium CONFIRMED Login live: Auth0 login page Every participant needs their own account (email+password sign-up or Google). Production runs on an Auth0 tenant whose name starts with dev-; its rate/email limits need checking
UX-59 Low CONFIRMED Login live The login page carries the tagline only: no app name or purpose, no link to the guide, no language choice
UX-60 Low PLAUSIBLE Code hygiene R/20_mod_board_comparison_server.R:355-386; global.R:14-17,86-92; R/sass.R 44 copy-paste observers; startup install_github; SCSS recompiled at every start; dead globals; leftover dev comments (R/graphs.R:206)
UX-61 High CONFIRMED (user) Landing / first screen R/30_mod_scenario_ui.R:30-60; ui.R:130-148 The first screen after login is a file manager: "Choose a file option", "New Scenario", "Select Scenario", Clone/Rename/Share. Nothing says what iCLEANED is, which decisions it informs, who it is for, who built it, where it has been used, or what is new. Help is a CGSpace PDF that goes stale with every release
UX-62 High CONFIRMED (user) Farm › Climate R/30_mod_scenario_ui.R:311-320; data/primary_database/*/lkp_climate.csv (climate_code == climate_desc) IPCC climate zone is a bare picker with no definition, criteria (MAT/MAP/PET) or map. The zone keys the manure MCF lookup (pkg R/ghg_emission.R:185), so a guessed zone gives wrong CH₄ unknowingly
UX-63 High CONFIRMED (user) All pickers and fields whole UI; no dictionary in repo No data dictionary or controlled vocabulary behind labels, units, definitions, ranges and defaults. Selected values show no definition (tillage, cropland system, organic-matter input, grassland management, input level, manure systems, soil types). This single gap underlies UX-28, UX-33, UX-50 and UX-72
UX-64 Medium CONFIRMED (user) Farm › Seasons R/30_mod_scenario_server.R:636-657,734-741 The Add-season modal asks for a name only; days are auto-filled with the remainder. Editing days means double-clicking a table cell (not discoverable). No running total is shown, only "must be 365". No explanation of what seasons are for (feed-basket periods). Not linked to location
UX-65 Blocker CONFIRMED (user) + code Farm › Manure/Fertilizer R/30_mod_scenario_ui.R:384-450; pkg R/ghg_emission.R:343-359, R/nitrogen_balance.R:151-153 "Annual purchase of manure / compost / other organic N / bedding (kg N)" asks for nitrogen mass, which no farmer knows. Users will type kg of material, and the package uses it as kg N with no conversion, overstating N inputs ~50–200× (fresh manure ≈ 0.5–2 % N). Bedding "kg N" is especially opaque
UX-66 High CONFIRMED (user) Farm (whole tab) R/30_mod_scenario_ui.R:288-330 The unit of analysis is undefined: the whole farm, or only the land and inputs serving the livestock enterprise? The answer changes land, N and GHG results. No on-screen definition; the guide's "enterprise" framing is not repeated in the UI
UX-67 Medium CONFIRMED (user) Farm › Manure vs Feed Production R/30_mod_scenario_server.R:1878-1937; pkg R/ghg_emission.R:359 Purchased organic N is one farm-level lump. Per-field rates (pasture vs fodder) must be pre-aggregated by the user, while manure produced is split per crop via "fraction of manure used as fertilizer". Two different allocation models, and neither is explained
UX-68 High CONFIRMED (user) + code Farm › Waste of milk and meat R/30_mod_scenario_ui.R:453-600; pkg R/merge_outputs.R:1095-1104 Eight % fields with no explanation, source or "don't know" option. They only feed the output summary (food-loss adjusted products), not emissions or N, but nothing says they are optional. Participants stall here
UX-69 High CONFIRMED (user) Farm › Area R/30_mod_scenario_ui.R:607-910 Precipitation, rainy-season months, soil type and properties, and ET₀ are typed by hand, with no prefill from location. "Rainy season (months/year)" gives no instruction for bimodal rainfall; erosivity divides P by it (pkg R/soil_health.R:28), so the answer matters. No "I have my own soil test" override pattern; selected cropland, tillage, OM and grassland options show no description
UX-70 High CONFIRMED (user) Whole scenario flow R/30_mod_scenario_ui.R (all tabs rendered at once) No progressive disclosure. Every field for every system is shown to every user. A few system questions (species, grazing vs zero-grazing, grows own feed?, buys manure/fertiliser?, rice?) could hide irrelevant fields
UX-71 Medium CONFIRMED (user) Navigation R/30_mod_scenario_ui.R:289,913,982,1125; carousel :291-910 Four fixed-width tabs plus a hidden 5-slide carousel inside Farm. A hierarchical sidebar (Farm › General / Seasons / Manure / Waste / Area; Livestock › per category …) with completion and error status per node would show where the user is and what is left
UX-72 High CONFIRMED (user) All inputs no required markers anywhere; validation R/30_mod_scenario_validation.R:213 Required vs optional is never indicated, and there is no "I don't know / use default" option. Users either stall or type guesses; neither is recorded as such
UX-73 High CONFIRMED (user) Livestock R/30_mod_scenario_init.R:53-103; R/30_mod_scenario_server.R:958-1141 One flat picker of ~22 species×category labels, then a ~50-column wide table (min 250 px per column, www/sass/app.scss:651-692). Fields such as "Average annual live weight gain (kg/animal)" have no guidance, default source or "not applicable" for mature animals. Species → category two-level choice and one form (card) per category would fit the screen and the mental model
UX-74 High CONFIRMED (live) Livestock › milk, weights R/30_mod_scenario_server.R:992; pkg R/energy_requirement.R:109-111,144 Milk can be entered for any category (calves, bulls, steers) and any size (100 000 kg/animal/yr is accepted). The package adds lactation energy and CP for any category with annual_milk > 0, so impossible entries change results. No per-category applicability or plausible range
UX-75 High CONFIRMED (live) All numeric fields defaults in data/primary_json.json; R/30_mod_scenario_server.R:991-1015 Fields show 0 rather than blank, so "not entered", "unknown" and "true zero" are indistinguishable. A fix needs explicit NA semantics end to end: UI blank → stored null → model applies a documented default with a provenance flag, or refuses to run. See UX-05

Live walkthrough log (2026-09-29, Chrome, 1036 px window)

This is the step-by-step record of the approved throwaway scenario ZZ_UXREVIEW_DELETE_ME. The scenario and its results are still in your User Folder; delete them when you are done.

# Step What happened Findings
1 Load app The navigation-consent sweetalert appears before anything else (declined). The landing screen is a file manager: "Choose a file option", New Scenario, Select Scenario, Rename/Share/Clone/Delete. Header nav wraps vertically UX-23, UX-61, UX-82
2 Hover "?" by Select Scenario The tooltip covers the picker and the Rename button, and its own text warns that pooled files can be overwritten UX-19, UX-25, UX-82
3 New Scenario Creates New_file (1).json immediately. The DB silently becomes Southern Highland Tanzania Dairy. The toast says "The JSON has been created successfully!" UX-80, UX-22
4 Rename Works, but the picker shows ZZ__UXREVIEW__DELETE__M…, and "Last scenario created" still says .json UX-81
5 Farm › General Region lists AFRICA/ASIA/LATIN AMERICA. Climate lists 6 zones with no definitions or map. The dropdown is clipped, won't close on Esc, and hides Farm name UX-62, UX-76, UX-82
6 Farm › Seasons The modal asks for a name only. The first season gets 365 days, the second 0, silently. 200,5 → 2005. The warning doesn't show the total. Enter doesn't commit an edit. A blank cell shows empty (the code stores 0) UX-05, UX-30, UX-36, UX-64
7 Farm › Manure/Fertilizer Fields are kg N. -500 → banner only. 1,5 → 15. NPK added at 0 % N with no warning. 7 fertiliser types; the amounts live on another tab UX-05, UX-13, UX-65, UX-84
8 Farm › Waste 8 × "0" % fields with no explanation or "optional" marker UX-68
9 Farm › Area Precipitation 0 and 14 rainy months accepted. 9 soil types (Xerosol, no Ferralsol). K unexplained. 0 help icons UX-10, UX-69, UX-83
10 Livestock › Add 22 flat species–category labels, default "Buffalo - Calves". Each new row: every numeric field 0, all 4 manure systems "Pasture / range / paddock". ~50 columns; 3 visible at 1036 px; frozen column overlaps headers UX-03, UX-73, UX-75, UX-82
11 Livestock edits Calves 100 000 kg milk accepted. Time fractions validated (banner). Stable 100 % with 0 collection and a "Pasture" system accepted UX-74, UX-85
12 Feed Production › Add Tanzania DB: 11 feeds, no Napier. "Selected a Feed". Rhodes grass defaults: land cover Dense forest, removal 0, DM 93.97 %, 14 tree columns. No area field despite the heading UX-09, UX-77, UX-79, UX-86, UX-87
13 Livestock Feeding There is a Total row (good). The banner lists missing 100 % allocations; the banner resizing shifts the table. Left calves/Short rains at 0 % on purpose UX-15, UX-82
14 Dashboard › Run #1 Preselected New_file.json, not the edited scenario. Ran in <3 s despite invalid feeding. Result: all four charts empty (axis 0–0.100), plus a cryptic DMI > 4 % BW note for cows only UX-08, UX-16, UX-78, UX-88
15 Set removal 0.9 → Run #2 GHG ≈ 7.3 (enteric only; axis "ghg_emission"). Land ≈ 4 ha. N balance ≈ −840 kg N/yr. Manure bar 0 UX-09 → Blocker, UX-17
16 Stable collection 1 → Run #3 Manure bar still 0 UX-03
17 Stable system "Solid storage" → Run #4 Manure bar appears (41 px). Confirms the default label drops manure CH₄ UX-03
— Not tested live Comparison (needs a second scenario), Download (saves a file), Edit Parameters, Share/Clone, disconnect —

Redesign directions (from your notes, cross-checked against the code and the live walkthrough)

These are structural changes, not bug fixes. Together they answer notes 1–14. Build order is suggested at the end.

  1. A public front door. The landing page should say what iCLEANED is and which decisions it informs (e.g. feed interventions, manure management, herd changes). It should also cover who it is for, the team and partners, where it has been used (case studies or testimonials), news and roadmap, and a "Try an example" button that opens a read-only demo scenario, with no login needed to look around. Move the file manager behind "My scenarios". (UX-61)

  2. Help that lives in the app and is versioned with it. Replace the CGSpace PDF as the primary help with in-app docs, e.g. a Quarto or pkgdown site served at /docs and built in CI from the same repo. Then every release ships matching help. Add contextual "Learn more" links from each section to its doc anchor, and keep the PDF as an export of the docs, not the source. (UX-27 refuted, but the GUIDE button still points at a static PDF; UX-53)

  3. A data dictionary / controlled vocabulary as the single source of truth. One table (YAML/CSV) per input and per lookup value, with these fields:

    • id and package_field
    • label and label_fr/pt/sw/vi/id…
    • definition
    • unit and unit_display
    • type
    • min / max / typical_range
    • default and default_source (DB / region / IPCC / user)
    • required_if (a condition on system answers)
    • na_policy (block / use default with flag / allowed)
    • help_url
    • applies_to (species / category / feed type)

    From it, generate the labels, tooltips, validation, units, the translations, the "definition shown on selection" for every picker (climate zones with IPCC criteria, tillage, cropland system, OM input, grassland management, manure systems, soils), and the docs pages. This fixes UX-28, 33, 50, 63, 72 and 87 in one mechanism, and gives multilingual access almost for free.

  4. Ask first, then show only what matters (progressive disclosure). Start with 6–8 system questions:

    • species kept
    • production aim (milk / meat / both / draught)
    • grazing regime (zero-grazing / semi / free / off-farm)
    • grows own feed?
    • buys feed, manure or fertiliser?
    • rice?
    • trees in the feed system?
    • number of distinct seasons

    Use the answers to hide irrelevant sections: tree columns, rice columns, off-farm grazing, milk for non-lactating categories, waste. They also set sensible defaults (e.g. zero-grazing → stable time = 1, manure system ≠ pasture). (UX-70, UX-74, UX-85)

  5. Location-first prefill, with override and provenance. Ask for a point: a map click, GPS, or an admin-area picker. Then prefill:

    • IPCC climate zone from the JRC/ESDAC IPCC climate-zone raster;
    • annual precipitation and rainy months from a CHIRPS/TerraClimate climatology;
    • bimodal detection → propose seasons with day counts;
    • ET₀ from TerraClimate or FAO;
    • soil type and soil C, N, clay and bulk density from SoilGrids (ISRIC REST API);
    • K factor derived from soil.

    Each value shows its source and an "I have my own measurement" override that is stored with provenance. Cache the lookups server-side and keep an offline fallback, because of slow connections. (UX-10, 11, 62, 64, 69, 76, 83)

  6. Ask for what users know, convert server-side. For each purchased input, ask for the quantity and form: manure (kg fresh, or bags/cart-loads with a mass per unit), compost, bedding (kg straw, sawdust or other). Apply an editable default N content from the dictionary and show the resulting kg N. Allow inputs per field or feed (pasture vs fodder) and aggregate for the model, instead of asking the user to pre-sum. Apply the same idea to milk: litres per day × lactation days, converted to kg per year. (UX-65, UX-67)

  7. Define the system boundary on screen. Add one sentence and a diagram at the top of Farm, e.g. "Describe only the land, animals and inputs of this livestock enterprise; crops sold off-farm are excluded", with examples. (UX-66)

  8. Livestock as species → category → card. Use a two-level picker (species, then category, with definitions of "local / improved / high productive"). Give each category its own card or form with grouped sections (herd & production, housing & time budget, manure, advanced parameters collapsed). This replaces the 50-column table. Fields that don't apply are hidden (e.g. live-weight gain for mature animals, milk for calves). Each field shows its DB default with a "use default" chip, rather than 0. (UX-12, UX-29, UX-73, UX-74)

  9. Hierarchical sidebar navigation with status. Replace the 4 tabs and hidden carousel with a left sidebar: Farm › General / Seasons / Manure & fertiliser / Waste / Soil & climate; Livestock › [each category]; Feeds › [each feed]; Feeding › [each season]; Run & results. Each node shows ✓ complete, ! needs attention or ○ optional, plus a running summary: season days total, herd size, feed-basket totals. (UX-35, UX-64, UX-71, UX-82)

  10. Blank means unknown; never show 0 as a default. Empty fields stay empty. The dictionary's na_policy decides what happens: block the run, fill a documented default flagged "assumed" in the results, or treat as not applicable. The model receives null, not 0, and the package gains explicit NA handling instead of NA→0 coercion. The results page lists every assumed value, so a number can be traced back. (UX-05, UX-42, UX-72, UX-75, UX-78; package 04_package_code.md §F04)

  11. Pre-run check and meaningful empty states. Run opens a checklist of blocking errors and warnings, each with a "go to field" link. A result that is 0 or empty says why ("0 ha of land: main product removal is 0 for Rhodes grass"). (UX-08, UX-78, UX-88)

Suggested build order:

  • Before the workshop: dictionary skeleton for Farm and Livestock (labels, units, definitions, ranges); fix the three "first row" defaults and the climate list; blank-not-zero on new rows; pre-run check.
  • Next release: system questions and progressive disclosure; purchased-input conversions; livestock cards; sidebar.
  • Later: geospatial prefill; landing site; versioned in-app docs; translations.

Detailed findings

Blockers

UX-01: Errors kill the session; raw R errors can reach users

  • Evidence:
    • grep tryCatch|validate(|need(|safeError over R/, server.R and global.R returns nothing. The reviewer re-ran this personally.
    • The run observer calls fromJSON and the whole model chain directly (R/10_mod_board_simulation_server.R:160-300). An uncaught error in an observeEvent ends the Shiny session.
    • Inside renderGirafe the error text is printed in place of the plot, because shiny.sanitize.errors is not set in global.R:20-26.
  • Known triggers:
    • two rice feeds (package 04_package_code.md §F16);
    • duplicate feed×crop display names, which raise "duplicate 'row.names' are not allowed" at R/30_mod_scenario_server.R:2417-2421;
    • a missing results .rds (10_…:144,488).
  • Refutation:
    • Only up-front guards exist: at least one scenario (:163) and at least two for Compare (20_…:125).
    • Autosave limits data loss, but not the context loss.
    • Not refuted.
  • Workshop impact: a participant sees the screen grey out with no explanation and has to reload. They land back on the Scenario tab with nothing selected (UX-39) and assume their work is gone.
  • Fix:
    • Wrap each scenario run in tryCatch, and show a showModal naming the scenario and a plain-language cause.
    • Continue with the other scenarios when one fails.
    • Set options(shiny.sanitize.errors = TRUE).
    • Validate before running (see UX-08).

UX-02: The synchronous model run blocks everyone

  • Evidence:
    • observeEvent(input$run_scenario) → withProgress → lapply over the scenarios, running feed_quality … combineOutputs in-process (R/10_mod_board_simulation_server.R:174-300).
    • There is no future, promises or ExtendedTask in the app.
    • Comparison (20_…:133-203) and ZIP creation (10_…:364, 20_…:215) are also synchronous.
  • Refutation:
    • A spinner and a progress bar exist (10_…:171-178), so the running user gets feedback.
    • The hosting setup (Posit Connect or Shiny Server workers, processes per app) was not visible. If each user gets their own process, the impact drops to that user's session.
    • Kept at Blocker until the deployment is confirmed, because a room of 20 on one worker is the default risk.
  • Workshop impact: during the "run your scenario" task, runs queue behind each other. Participants on slow links see frozen UIs and click Run again.
  • Fix:
    • Short term: confirm the worker settings and raise max processes/min processes for the session. Stagger the runs by table.
    • Real fix: ExtendedTask + mirai/future with a disabled Run button while a run is in progress.

UX-03: The default manure-management system does not match the IPCC tables

  • Evidence:
    • manureman_selection <- lkp_manureman()$manureman_desc[1] is used for all four systems (R/30_mod_scenario_server.R:987,997-1005). In the primary DBs, row 1 is "Pasture / range / paddock" (e.g. data/primary_database/Central Uganda - Livestock General/lkp_manureman.csv).
    • The package joins on Table 10.17$Manure_management_systems and Table 10.21$system (cleaned_review_src/R/ghg_emission.R:188-194,239-245). Their value is "Pasture/Range/Paddock". The reviewer checked this in R: the spaced label exists only in the descriptive second column.
    • There is no trimws/tolower/normalisation in the app or the package. The unmatched join gives MCF NA, which becomes 0.
  • Refutation:
    • Users who change every manure system in the picker avoid the problem.
    • But the picker itself lists both spellings, and "Solid storage" twice.
    • Other labels also fail: "Anaer digester … HQ tec." has a trailing dot.
    • Related package notes: 01_climate_ghg.md §F07/F08.
  • Workshop impact: manure CH₄/N₂O is silently understated for every animal left on the default, so manure-management scenario comparisons show "no effect".
  • Fix:
    • Store manureman_code rather than the description.
    • Harmonise the lkp_manureman.csv labels with the IPCC keys.
    • Default to a blank value that validation must flag.
    • Add a post-run check that warns when any MCF/EF is NA.

UX-04: The mislabelled manure_onfarm_fraction zeroes collected manure

  • Evidence:
    • The label "Collection of manure during off-farm grazing (fraction)" is at R/30_mod_scenario_init.R:72, with default 0 (R/30_mod_scenario_server.R:1007).
    • The package multiplies all collected manure by it (02_livestock.md §H-05; 03_landuse.md N-05).
  • Refutation: there is no validation or tooltip. Not refuted.
  • Workshop impact: a user who "doesn't collect off-farm" enters 0 and gets no manure N for crops. This is invisible in the outputs.
  • Fix: relabel the field to match the package semantics (or fix the package), and warn when it is 0 while manure_in_* > 0.

UX-05: Blanks, commas and out-of-range numbers are accepted silently

  • Evidence:
    • DT cells: update_cell does ifelse(new_value == "", 0, new_value) (R/helpers_shiny.R:201-203).
    • numericInputs: blank → NA → as.numeric → toJSON writes "NA" (R/30_mod_scenario_server.R:2567), and validation treats NA as acceptable (R/30_mod_scenario_validation.R:213).
    • None of the 28 numericInputs has min, max or step (R/30_mod_scenario_ui.R).
    • In the local probe (Chromium, en locale), typing 1,5 produced 15 in both widget types.
  • Refutation:
    • Browsers in a French, Portuguese, Indonesian or Vietnamese locale may parse 1,5 as 1.5 in <input type=number>. The probe did not test this, so the behaviour is locale-dependent.
    • DT's numeric coercion itself is correct: the probe refuted an earlier crash hypothesis.
  • Workshop impact: many participants (francophone Africa, Indonesia, Vietnam) habitually type comma decimals. A 10× yield or weight error produces plausible-looking charts.
  • Fix:
    • Add min/max/step to the numericInputs.
    • Server-side, reject non-finite values and blanks with a field-level message.
    • Normalise , → . explicitly, or reject it with a message.
    • Never coerce blank to 0 silently.

UX-06: Duplicate names silently stop saving, and Run uses stale data

  • Evidence:
    • The autosave observer returns early with only cat(file = stderr(), "20 - Skipping JSON save…") (R/30_mod_scenario_server.R:2527-2533).
    • The banner says the simulation will fail (R/30_mod_scenario_validation.R:486-488). But Run reads the last saved JSON (10_…:181), so it succeeds with old inputs.
  • Refutation:
    • A banner exists, but its content is false.
    • Package 04_package_code.md §F18 notes the underlying fragility.
  • Workshop impact: a participant copies a feed row and edits it. Every edit after that point is lost, and their results do not change. They conclude the intervention has no effect.
  • Fix:
    • Block Run while has_errors.
    • Show "Not saved: fix duplicate names" beside the tab.
    • Or save anyway and block only the run.

UX-07: Comparison charts mislead about relative change

  • Evidence:
    • The static y-axis span holds graphs_desc$unit (e.g. "kg FPCM/year") even when the bars show scales::percent (R/20_mod_board_comparison_ui.R:201-206; R/graphs.R:373-375).
    • The package computes (scen − base)/base*100 and maps non-finite results to 0 (cleaned_review_src/R/compare_scenario.R:50-54 and following).
      • Baseline 0 therefore shows as "0 %".
      • A negative base (N balance, carbon stock change, carbon balance) flips the direction of the change.
    • The base scenario itself is excluded from the plot (:41-42).
  • Refutation:
    • The package's y_title ("% change in …") is used in the PNG export only.
    • The short description does switch to relative text (20_…:327-349), but the axis does not.
  • Workshop impact: "Carbon balance improved by −40 %" gets read as worse. A zero-emission baseline shows as "no change".
  • Fix:
    • Switch the axis label to "% change vs ".
    • Divide by abs(base).
    • Show "n/a" (not 0) when base = 0.
    • Display the absolute base value in the tooltip.

UX-08: Validation never blocks a run

  • Evidence:
    • Every check renders an inline alert-danger (R/30_mod_scenario_validation.R:200-257; server :734-741,1177-1290,1731-1875,2363-2370).
    • run_scenario checks only that a scenario is selected (10_…:163).
    • After the run, the only check is DMI > 4 % BW (10_…:251-269).
  • Refutation: none found.
  • Workshop impact: these all run silently:
    • seasons summing to 300 days (package 02_livestock.md L-08: shares are not normalised);
    • feeding columns at 80 %;
    • time fractions summing to 0.
  • Fix:
    • Add a pre-run validator over the saved JSON that lists all errors per tab, with "Go to field" links. Refuse to run while errors exist.
    • Show a red dot on the tab labels.

High (condensed)

  • UX-09 (zero land for new feeds).
    • Evidence: main_product_removal = 0 on add (30_…server:1449), and the validator accepts 0 (:1829-1832). "Purchased" feeds always get 0 ha, which is correct but not explained. See 03_landuse.md §LU-01.
    • Fix: default removal to NA and require it for "Main" rows; show the ha per feed after the run.
  • UX-10 / UX-11 (rain and soil defaults of 0).
    • Evidence: erosion and SOC are silently lost (03_landuse.md SE-01/02, S-03, N-07/08).
    • Refutation: the default templates may carry real values, but a new scenario from primary_json.json does not.
    • Fix: plausible ranges (P 100–4000 mm, rain months 1–12, BD 0.8–1.8, depth 0.1–2 m) with warnings; pre-fill from the region.
  • UX-12 (DB-synced columns revert silently).
    • Evidence: sync_columns_by_key (helpers_shiny.R:276-303) runs on load and on DB change (30_…server:940-952,1356-1381,2749-2754). The comment at helpers_shiny.R:259-260 confirms that only unlisted columns are preserved.
    • Workshop impact: sensitivity edits (e.g. a changed body weight) vanish the next day.
    • Fix: make the orange columns read-only in the scenario tables, with a link to "Edit Parameters".
  • UX-13 (fertilizer N can be lost).
    • Evidence: NPK %N is 0 by default; removing a product leaves stale kg/ha, which are then saved (30_…server:783-784,865-878,2588-2593). See 01_climate_ghg.md §F10, 03_landuse.md N-04.
    • Fix: require %N > 0 for NPK; clear the amounts on remove.
  • UX-14 (which package build is deployed?).
    • Evidence: the renv fork vs the install_github fallback; the field names the app writes (fat_milkcontent, n_manure_content, percentage_n) are v0.7.0 names.
    • Workshop impact: if production runs genuine v0.6.0, several inputs are ignored.
    • Action: ask the host for packageVersion("cleaned") and packageDescription("cleaned")$RemoteSha before the workshop.
  • UX-15 (feeding tables).
    • Evidence: the column-sum check uses na.rm = TRUE and allows negatives (validation.R:547-552). Allocations are keyed by display name (30_…server:2417-2453), so renames reset them.
    • Fix: key allocations by code; require 0–100 per cell.
  • UX-16 (wrong scenario preselected on the Dashboard).
    • Evidence: selected = list.files(...)[1] (10_…server:20-23).
    • Fix: preselect the scenario currently open on the Scenario tab.
  • UX-17 (GHG unit label).
    • Evidence: the xlsx unit cell for ghg_emission literally reads ghg_emission (checked with readxl). The chart plots t_CO2e_per_ha.
    • Fix: a one-cell xlsx edit to "t CO₂e/ha/year", and add "per ha" to the title.
  • UX-18 (pie legend order).
    • Evidence: unnamed palette (graphs.R:303-305) vs unique(feed) in the legend builder. Land-required uses a named palette and is correct.
    • Fix: setNames(color_palette[seq_along(feeds)], feeds).
  • UX-19 / UX-20 (shared pool and rename).
    • Workshop impact: if participants are told to "share your scenario", they will overwrite each other's same-named files ("Baseline.json").
    • Fix: sanitise names (fs::path_sanitize, reject / and ..); namespace pooled files by user; copy the DB on share.
  • UX-21 (extrapolation and benchmarks missing).
    • Evidence: guide §3.2 (~L1132-1211) prescribes multiplying the enterprise indicators by the number of enterprises and adoption rates; §3.4 (~L1416+) gives the Africa benchmarks. The app has 0 hits for either feature.
    • Workshop impact: the "extrapolate" task can only be done in a spreadsheet.
    • Fix: state this in the guide. Better, add a small "Scale up" panel (number of enterprises × adoption %) and a benchmark reference line on the per-kg charts.
  • UX-22 (New Parameters Database ignores the selection).
    • Evidence: it clones default_parameters_database (40_…server:189-199). A Uganda or Vietnam participant silently starts from Tanzania dairy parameters.
    • Fix: clone the selected DB (the Clone button already does this; rename or merge the buttons).
  • UX-23 (privacy).
    • Evidence: the ipify fetch runs on page load (www/js/bg-modal.js:164).
    • Workshop impact: a data-protection question you should not be answering on the day.
    • Fix: fetch only after consent, remember the consent answer, and disclose the email–IP linkage in the consent text.
  • UX-24 / UX-25 / UX-26 (keyboard, hover-only help, labels).
    • Fix: actionLink/<button>; :focus-within plus a click toggle for .help; pass label = and drop the h2s.
  • UX-27 (GUIDE button hidden).
    • Evidence: go_to() hides #maj_helper_btn except on the board (helpers_shiny.R:16-22).
    • Fix: always show it, and deep-link it to the relevant guide section per tab.
  • UX-28 (no column help).
    • Fix: DT headerCallback tooltips taken from a single dictionary (label, unit, range, meaning). The same dictionary can drive validation and translation.
  • UX-29 (zero weights for small ruminants).
    • Evidence: 30_…server:1010-1011; see 02_livestock.md §H-02.
    • Fix: sync the weights from the DB, or require them.

Medium and Low

The table above gives the evidence for these. The ones most relevant on the workshop day:

  • UX-35: the carousel hides the Farm sub-sections, so tell participants there are five slides.
  • UX-36: double-click to edit.
  • UX-37: autosave with no "saved" indicator, which unsettles users on slow links.
  • UX-39: Back leaves the app.
  • UX-43: the download ZIP contents.
  • UX-48: laptops only; no phones.
  • UX-58: accounts must exist before the session.

Done well (max 5)

  1. Autosave to per-user JSON. Most edits survive a disconnect. Deletes of scenarios, results, comparisons and DBs ask for confirmation (30_…server:201-216; 10_…:435-451; 20_…:267-285; 40_…:126-158).
  2. Indicator explanations come from one spreadsheet (data/iCLEANED - Graphs Information.xlsx). There are short and long, absolute and relative texts for all 48 charts, which is a good seed for help and translation.
  3. Honest axes. There is no truncation (ylim/coord_cartesian); zero baselines use expansion(mult = c(0, .1)), and a dashed 0-line appears when values go negative (graphs.R:74-82).
  4. Many data-model mismatches are prevented. Livestock types are click-locked in the params DB; every feed × livestock basket row is always written, which avoids package F18; the climate and fertilizer names all match the package (refuted suspicions in the input sweep).
  5. Light asset payload. www/ is ~370 KB, the fonts are local, there is no CDN dependency for core rendering, and the tables are small.

iCLEANED user-testing workshop: facilitator run sheet

Format: 3 h 15 min in total (a 2 h 30 min testing block plus a 45 min setup/debrief), for 8–25 participants in pairs, with one observer per 2–3 pairs.

Audience: researchers, NGO and extension staff, and students. They are not R users, their English varies, and they work on laptops over mixed-quality connections.

Companion: see 01_uiux_review.md (UX-nn IDs), and use 03_feedback_form.md to log issues.

1. Goals

  1. Find out whether first-time users can build and run a baseline scenario and an intervention scenario, and compare them, using the app plus a 1-page task card. They should not need the PDF guide.
  2. Identify where users enter wrong values without noticing: units, blanks, comma decimals, and defaults.
  3. Test whether users read the Dashboard and Comparison charts correctly (absolute vs %, direction of change, units).
  4. Collect usability (SUS) and usefulness ratings, plus concrete issues ready to file on GitHub.
  5. Non-goal: validating the model's science. Treat all numbers as illustrative, and say so.

2. Before the day (checklist)

  • Accounts: make sure every participant has an account before the session (UX-58). Test sign-up, Google sign-in and password reset on the Auth0 tenant (dev-…) with 3 test users. Check the tenant's email and login rate limits.
  • Server capacity: runs are synchronous (UX-02). Get the host to confirm the number of R processes per app and raise it for the day. Do a load test: 10 people click Run at the same time and you time it.
  • Package build: confirm the deployed cleaned build with packageVersion("cleaned") and the RemoteSha (UX-14).
  • Template scenario: prepare WS_Baseline in the Shared Examples folder with valid seasons (sum 365), feeding at 100 %, a manure system chosen explicitly (not the default, UX-03), manure_onfarm_fraction > 0 (UX-04), non-zero rain and soil values (UX-10/11), and non-zero main-product removal (UX-09; with the default of 0, every chart is empty), land cover set to a real cover rather than the default "Dense forest" (UX-77), and a climate zone valid for the site (the list lacks Cool Temperate and Boreal, UX-76).
  • Fixes: if time allows, apply the quick fixes: the xlsx unit cell for GHG (UX-17), the manure label (UX-04), and the manure default (UX-03).
  • Materials: print the task cards (section 4), a 1-page glossary of units, and the known-issues slide (section 3).
  • Fallback: keep an offline plan: screenshots or a screen recording of a full run, in case the server or the Wi-Fi fails.
  • Observers: each observer gets the observation checklist (section 5) and the feedback forms. Assign pair IDs P01, P02 and so on.
  • Browsers: ask participants to bring a laptop with Chrome or Edge. Phones are not supported (UX-48).

3. Known issues to announce up front (5 min, slide)

Keep this to one slide, in plain language:

  1. Numbers are for learning today, not for reports. Several default values can make some results zero.
  2. Decimals: type a point, not a comma, e.g. 1.5, not 1,5. The app may otherwise read 15 (UX-05).
  3. Don't leave number cells empty. An empty cell becomes 0 (UX-05).
  4. Editing tables: double-click a cell to edit it, then press Enter (UX-36). The Farm tab has 5 pages; use the arrows at the sides (UX-35).
  5. Saving is automatic. There is no Save button. Red warning boxes do not stop the run, so fix them before running (UX-08).
  6. Before you click Run, check that the Dashboard shows your scenario ticked (UX-16). Click Run once and wait, because the server is shared (UX-02).
  7. If the screen goes grey, reload the page and select your scenario again. Your inputs are usually saved (UX-01, UX-39).
  8. Don't use the browser Back button. It leaves the app (UX-39).
  9. On comparison charts in "%" mode, the axis label still shows the original unit. Read the bars as % change (UX-07).
  10. Watch three defaults. When you add a feed, set "Main product removal" (for example 0.9) and change "Land cover" from "Dense forest". When you add animals, choose each manure system rather than leaving "Pasture / range / paddock" (UX-09, UX-77, UX-03). If all your charts are empty, it is almost always main product removal = 0.
  11. Purchased manure, compost and bedding are asked in kg of nitrogen, not kg of material. Leave them at 0 today unless you know the N amount (UX-65).
  12. Don't use "Share to pool" today. Names collide between users (UX-19).

4. Tasks (participants, in pairs)

Give each pair a printed card with one task at a time. The facilitator only gives a hint after 2 minutes stuck, and the observer notes it as an "assist".

# Time Task (as written on the card) Success criteria Watch for (UX-ID)
T1 10 min Log in, answer the welcome questions, and find the GUIDE and Help. Reaches the Scenario screen. Finds Help "?" and the GUIDE within 2 min. Consent pop-ups; GUIDE opens a static PDF only (UX-27 refuted live: button is visible); hover-only help (UX-25)
T2 10 min Make your own copy of the example scenario "WS_Baseline" and rename it "Baseline_". Copy exists in User Folder with the new name; no assist needed. Shared Examples vs User Folder vs Pool radios; Clone vs Rename; name collisions (UX-20)
T3 15 min In Farm, check the seasons add up to one year, and set annual rainfall to 1250.5 mm. Seasons sum to 365; rainfall saved as 1250.5 (facilitator checks the JSON or the field value after reload). Carousel pages missed (UX-35); comma decimal (UX-05); units
T4 15 min In Livestock, change the number of lactating cows to 12. Each cow gives 8.5 litres of milk a day for a 300-day lactation; enter that. Tell us which manure system the cows use. Herd = 12. Milk entered as annual kg per animal (≈ 2,600 kg/animal/year). Note whether they convert litres/day to kg/year unaided. Participant can find and name the manure system. Label "Average annual milk production (kg/animal)" (R/30_mod_scenario_init.R:57); double-click editing (UX-36); 50-column scroll; "Number" label (UX-33); manure picker duplicates (UX-03)
T5 15 min In Feed Production, add one new feed grown on the farm (e.g. Rhodes grass; note that Napier is not in the Tanzania DB, UX-79). Feed added with a yield and a non-zero "main product removal". Zero-removal default (UX-09); "Dense forest" land cover (UX-77); duplicate name trap (UX-06); orange columns reverting (UX-12)
T6 15 min In Livestock Feeding, give the new feed 20 % of the diet for cows in each season, and keep the totals at 100 %. Every season column sums to 100 %; red box gone. Banner discoverability; negative %s (UX-15); many seasons
T7 10 min Go to the Dashboard, run your two scenarios (Baseline and the one with the new feed), and tell us the GHG emissions. Include the unit. Runs the correct scenarios; states a value with a correct unit (t CO₂e per ha; the axis wrongly says "ghg_emission"). Wrong scenario preselected (UX-16); "ghg_emission" axis (UX-17); freeze during the run (UX-02)
T8 15 min Compare the two scenarios with Baseline as the base. Did milk production and GHG go up or down, and by how much? Correct direction and approximate % for both indicators. Absolute unit on % axis (UX-07); category picker; base excluded from the chart
T9 10 min Download your results and open them. Find the milk production number. Opens the ZIP and finds the value in the xlsx. ZIP contents, .rds, no CSV (UX-43); file names
T10 10 min (optional) Using the guide, estimate the GHG effect if 30 % of 10,000 similar farms adopted the new feed. Any reasonable method; note how they do it. No extrapolation in the app (UX-21)

Timing: tasks T1–T9 take ~115 min. With T10, a break, the intro and the debrief, the whole session is ~3 h 15 min. If time is short, drop T10 and merge T3 into T4.

4a. Numbers make feedback precise

Every step and heading in the mockup is numbered (7. Animals → 7.1 Cows, local breed → 7.1.2 Numbers). Ask participants to quote the number when they say anything: "7.1.2 is confusing" beats "the animals page". The numbers are also stored with every comment, so the export maps straight onto issues. Tell them the numbers renumber if we add a step, so quote them in the session, not weeks later.

4b. What to test in the redesign mockup

The mockup is where the redesign is tested; the task cards above are for the live app. Two flows are new in v0.7 and worth a task each:

  • Herd then animals. Ask a pair to add a second herd and put one animal group in it, starting from the herd card rather than the Animals step. Watch whether they expect manure handling to be asked on the herd (it is) and whether they notice a group can override it.
  • Feeding plan per herd. With a two-herd scenario (Rungwe commercial or Tanzania national), ask for the dry-season diet of the second herd. Watch whether the Herd tabs are read as tabs, and whether anyone tries to plan both herds in one grid.
  • Defaults they disagree with. Ask a pair to change the dry matter of one feed. Watch which of the two routes they take — "this scenario only" or "in the parameter set" — and whether they can say afterwards who else their change affects.

5. Observation checklist (one per pair per task)

Use a tick or a short note, and record the time in minutes.

  • Completed: unassisted, assisted, or failed
  • Time on task
  • First click correct? If not, what did they click?
  • Hesitation over 20 s. Where?
  • Label or unit misread. Quote the label.
  • Wrong value entered without noticing (comma, blank, wrong unit). Record the value typed.
  • Did not see a red warning box, or ignored it
  • Lost work or grey screen. Note the time and the action just before.
  • Used the guide PDF or Help? Which section, and did it help?
  • Misread a chart: direction, unit, or % vs absolute
  • Connection issue (slow, spinner over 30 s, disconnect)
  • Language difficulty. Which word or phrase?
  • Emotional signal: frustration, confusion, delight, "aha"
  • Quote worth keeping (verbatim)

Log every issue on 03_feedback_form.md right away, with pair ID, task and screen.

6. Think-aloud prompts (neutral; don't lead)

  • "What are you looking for right now?"
  • "What do you expect will happen when you click that?"
  • "What does this number or label mean to you?"
  • "How sure are you that this value was saved? What makes you think so?"
  • "You paused there. What were you thinking?"
  • "If you were alone, what would you do next?"
  • "In your own words, what does this chart tell you?"
  • "Is this the result you expected? Why or why not?"
  • Avoid: "Is that confusing?", "Did you see the button?", and explaining the UI before they have tried it.

7. Post-session questionnaire (10 min, paper or form)

7a. System Usability Scale (SUS)

Rate each statement from 1 (strongly disagree) to 5 (strongly agree).

  1. I think that I would like to use iCLEANED frequently.
  2. I found iCLEANED unnecessarily complex.
  3. I thought iCLEANED was easy to use.
  4. I think that I would need the support of a technical person to be able to use iCLEANED.
  5. I found the various functions in iCLEANED were well integrated.
  6. I thought there was too much inconsistency in iCLEANED.
  7. I would imagine that most people would learn to use iCLEANED very quickly.
  8. I found iCLEANED very cumbersome to use.
  9. I felt very confident using iCLEANED.
  10. I needed to learn a lot of things before I could get going with iCLEANED.

Scoring:

  • Odd items score (response − 1).
  • Even items score (5 − response).
  • Sum the ten item scores and multiply by 2.5, giving a score from 0 to 100. A score around 68 is average.

7b. App-specific questions

  1. Units: how confident are you that the values you entered were in the units the app expected? (1 = not at all, 5 = fully confident)
  2. Results: in your own words, what did the comparison chart tell you about your intervention? Was the direction (increase or decrease) clear? (free text + 1–5)
  3. Help: when you were stuck, where did you look first: the "?" tooltips, the GUIDE PDF, your partner, or the facilitator? What was missing? (multiple choice + free text)
  4. Connection: did the app feel too slow or freeze at any point? (yes/no; which step)
  5. Use: would you use iCLEANED for your own work? What single change would make that more likely? And which language would you prefer the app in? (1–5 + free text)

8. Debrief (15 min, whole group)

  • Ask each pair for the top 3 pain points, and put them on sticky notes on the wall under Farm / Livestock / Feed / Feeding / Dashboard / Compare / Other.
  • Dot-vote on severity.
  • The facilitator maps each note to an existing UX-ID or a new issue in 03_feedback_form.md.
  • Thank participants. Tell them what will happen to their feedback: it will be filed on GitHub, with no personal data.

9. After the session

  • Compute the SUS mean and SD, and the task success rates (unassisted, assisted, failed) and median times.
  • Deduplicate the feedback forms, and map them to UX-IDs and to the GitHub forms (see 03_feedback_form.md).
  • Delete the test scenarios and the throwaway accounts. Participants' onboarding answers are stored in DATA_DIR/Onboarding/onboarding.csv; handle that file according to your consent statement.

iCLEANED workshop: participant issue capture form

Observers fill in one block per issue during the session. The facilitator triages the blocks afterwards and files them on GitHub.

Where issues go (routing)

Update, 2026-10-01. Feedback on the redesign mockup no longer needs this routing: it files itself to peetmate/cleaned-review, either one click at a time from the Feedback screen or in bulk with ui_ux/mockup/tools/file_feedback_issues.mjs. That repository now has its own issue templates. The routing below still applies to findings about the live app and the package, which belong upstream.

The GitHub issue forms (bug_report.yml, data_issue.yml, feature_request.yml, question.yml, with blank_issues_enabled: false) currently exist in the CIAT/cleaned package repository. The web app lives in a separate repository, CIAT/icleaned, which has no issue templates. The package forms are written for R users. The bug form requires a component from the package-function list, an R reproducible example, and packageVersion("cleaned"), and workshop participants cannot supply any of those.

Route each issue as follows:

Issue is about… File in Form Who completes the required fields
App screens, buttons, saving, crashes, charts and labels, downloads, login, speed, accessibility CIAT/icleaned No form there. Use the "App bug / UX" block below as the issue body. Suggestion: add issue forms to icleaned too Facilitator
Wrong or implausible result that persists when the same JSON is run through the package CIAT/cleaned Bug report ([Bug]: ). Component = the model function; attach the scenario JSON exported from the user folder as the reprex Facilitator/dev (needs R)
A parameter, emission factor, feed value or lookup entry that looks wrong or is missing (e.g. manure-system labels, a missing fertilizer such as CAN) CIAT/cleaned (package data) or CIAT/icleaned (data/primary_database/*.csv) Data or parameter issue ([Data]: ) Facilitator + participant (source/reference)
Missing capability (extrapolation, benchmarks, translation, CSV export) icleaned for UI; cleaned for new calculations Feature request ([Feature]: ) Facilitator
"How do I…" / "what does this mean" cleaned (method) or icleaned (UI) Question ([Question]: ) Facilitator

Before filing, check 01_uiux_review.md. If the issue matches a UX-nn finding, add the participant evidence to that finding rather than opening a duplicate.

Issue capture block (copy one per issue)

Issue #: WS-___            Pair ID: P__        Observer: ____
Task (T1–T10): __          Time into task (min): __
Screen / tab:  [ ] Login/onboarding  [ ] Scenario select  [ ] Farm (page __/5)
               [ ] Livestock  [ ] Feed Production  [ ] Livestock Feeding
               [ ] Edit Parameters  [ ] Dashboard – run  [ ] Dashboard – compare
               [ ] Download  [ ] Help / Guide  [ ] Other: ____
Field / label / chart (exact text on screen): "________________"

TYPE (pick one → GitHub form):
  [ ] A. Bug – app (crash, grey screen, didn't save, wrong button behaviour) → icleaned
  [ ] B. Bug – result (number wrong/implausible)                          → cleaned Bug report
  [ ] C. Data / parameter (value, label, missing item in a list)          → Data or parameter issue
  [ ] D. Usability / wording / unit confusion                             → icleaned (UX)
  [ ] E. Feature request                                                  → Feature request
  [ ] F. Question / didn't understand                                     → Question

WHAT HAPPENED (participant's words, verbatim quote if possible):
______________________________________________________________

WHAT THEY EXPECTED:
______________________________________________________________

STEPS JUST BEFORE (clicks / values typed, incl. exact value e.g. "1,5", blank):
1. ____  2. ____  3. ____

EVIDENCE: [ ] screenshot/photo #___  [ ] error text copied: "________"
          [ ] scenario name: ________  (JSON retrievable from user folder: Y/N)

OUTCOME: [ ] recovered alone  [ ] recovered with help  [ ] blocked task  [ ] didn't notice (observer spotted)
IMPACT:  [ ] Blocker (couldn't finish / wrong result unknowingly)  [ ] High  [ ] Medium  [ ] Low
Matches known issue UX-__ ? [ ] yes  [ ] new

ENVIRONMENT: Browser ____  OS ____  Screen: laptop / tablet / phone
             Connection: good / slow / dropped   Browser language/locale: ____

Field mapping to GitHub forms (for the facilitator when filing)

→ bug_report.yml (CIAT/cleaned) — type B only

Form field (id) Required Fill from capture block
Title — [Bug]: <indicator> <wrong how> when <input condition>
component (dropdown) yes Map the chart or indicator to its function: GHG → ghg_emission; N balance → n_balance; land required → land_requirement; water → water_requirement; soil/erosion → soil_health; comparison % → compare_scenario / calculate_differences; otherwise Not sure / other
description yes "What happened", plus the error text
expected yes "What they expected"
reprex yes R code: para <- jsonlite::fromJSON("<scenario>.json") followed by the call chain. Attach the scenario JSON from Users/<user>/study_objects/
pkg-version yes Deployed build, i.e. packageVersion("cleaned") + RemoteSha on the server (UX-14)
session no Server sessionInfo()
context no Pair ID (no names), task, screenshot, UX-ID link, "found via iCLEANED web app"

→ data_issue.yml (CIAT/cleaned) — type C

Form field (id) Required Fill from
Title — [Data]: <table/entry> <problem>
source yes ghg_parameters.json / energy_parameters.json / stock_change_parameters.json / Feed library / Input mappings / Other. For app DB CSVs (lkp_*.csv) choose "Other package data" and state that it is an app DB, or file in icleaned
current yes Exact entry and value as seen on screen, e.g. lkp_manureman: "Pasture / range / paddock"
proposed no Participant's value, if known
reference yes Participant's source (national statistics, paper). If none, write "participant field knowledge – needs source"
impact no Which charts looked wrong
pkg-version no Deployed build

→ feature_request.yml — type E

Form field (id) Required Fill from
problem yes "What happened" / "What they expected", written as a user need ("As an extension officer I need to…")
solution yes Participant's suggestion, or the fix from the UX-ID
alternatives no Current workaround (e.g. "extrapolated in Excel")
methods no Guide section, e.g. §3.2 extrapolation, §3.4 benchmarks
contribute no "I can help test or provide data" if the participant offered

→ question.yml — type F

Form field (id) Required Fill from
question yes Participant's question, verbatim
tried no What they clicked or read (guide section, "?" tooltip). This field renders as R code, so wrap plain text in comments # ...

Note: a question that several participants ask about the same screen is a usability issue (type D), not just a question. File one UX issue in icleaned, and add the count.

→ CIAT/icleaned (no forms) — types A and D, suggested body

**Screen / step:** <tab › section › field>   **Workshop task:** T_   **Pairs affected:** n / N
**What happened:** …
**Expected:** …
**Steps:** 1. … 2. … 3. …
**Input used:** (exact values, e.g. "1,5", blank)
**Environment:** browser / OS / screen / connection / locale
**Severity:** Blocker | High | Medium | Low
**Related review finding:** UX-__ (cleaned_review/ui_ux/01_uiux_review.md)
**Evidence:** screenshots, error text (no participant names/emails)

Suggested labels: ux, bug, validation, dashboard, accessibility, i18n, performance, workshop-2026.

Privacy reminder

  • Do not put participant names, emails or IPs in issues. Use pair IDs only.
  • Scenario JSONs attached to issues must not contain identifying farm names. Rename farm_name before attaching.

04 · Design benchmarks: how proven farm / livestock tools structure data entry

Purpose: adapt, not invent. This note surveys well-regarded tools that describe farms or livestock systems and extracts the UX patterns worth borrowing for the iCLEANED data-entry redesign (Home/scenarios → Location & climate → Plots → Seasons → Herds & animal groups → Feeds → Manure & inputs → Feeding plan → Check & run → Results).

Evidence basis: official user guides / manuals were read in full for Cool Farm Tool (livestock), Agrecalc Cloud, GLEAM-i 2.0, CAP'2ER (methodological guide + brochure), COMET-Farm, Farm Carbon Calculator, Bord Bia/Teagasc Carbon Navigator, FEAST data application and EX-ACT quick guidance. Farm-management and farmer-facing apps (AgriWebb, Herdwatch, Breedr, Farmbrite, farmOS, Ruminati, MLA calculator, iCow, Nuru, Plantix, Farmer.Chat, Hello Tractor) were assessed from official product pages, help centres and published evaluations. Anything not directly observed is marked (inferred).


1. App-by-app table

# App (owner) Scope described Primary users Platform UX patterns worth borrowing Anti-patterns / cautions
1 Cool Farm Tool – Beef & Dairy (Cool Farm Alliance) Whole herd GHG for one reference period; 7 sections: General, Production/Milk, Herd, Grazing, Feed, Manure, Energy, Transport Producers, agronomists, technicians "who work with farmers" Web (desktop) (a) Herd = one table: 6 fixed age/sex categories × {on-farm avg number, live weight} × {sold, purchased}; dairy live weights auto-filled from breed and editable. (b) Two feed approaches: precise DMI per feed per category, or simple % feed mix for whole herd (must sum to 100). (c) Manure = per category, pick 1..n management types with % shares; if none selected, grazing assumed; grazing-time manure auto-derived from Grazing section. (d) Number + unit dropdown on every quantity; hover "i" contextual help on every question; free-text notes box per section. (e) "Save as" to duplicate an assessment and build what-if scenarios; compare view. (f) Reference period start chosen, end auto-set to +12 months (editable for cycle-based systems). Feed crops must be assessed separately in the crop module and saved as feed items – a two-pathway detour that confuses first-time users. No rotational grazing / soil C for grassland. No offline.
2 Agrecalc Cloud (SAC Consulting / SRUC) Whole-farm + enterprise + product footprint; 3 sections: Land & Crops, Livestock, Energy & Waste Farmers, consultants (validators), supply-chain users Web, "best on desktop/laptop" (a) Report set-up wizard: pick sectors present → add a group per herd/flock → tick livestock classes present → benchmark "enterprise system type" from list. Only the ticked classes appear on later pages. (b) Livestock class list with plain definitions (Cow, Bull, Heifer 0-12/12-24/24-36 mth, Steer…). (c) Livestock pages in order: Numbers & Weights → Purchases, Sales & Deaths → Performance → Manure Management → Purchased Feed → Purchased Bedding → Feed Ration. (d) Manure = % of time per class grazing in-bye / hill / housed in each of 6 systems, red cells until rows total 100 %. (e) Prepopulated typical values (ADG, ration digestibility, CP) usable "in the absence of more specific data"; manure N/P prepopulates from type and is editable. (f) Auto-save on "Next"; no forced order ("we recommend starting with Land & Crops"). (g) "Allocate fuel use" button distributes whole-farm energy to enterprises. (h) Consultant validation as a QC step; multi-herd support. ~2.5 h first time (Farmers Weekly). Pesticides require the user to pre-adjust area by number of sprays – arithmetic pushed onto the user. Hectare-only areas.
3 CAP'2ER (Idele, France) Multicriteria environmental diagnostic for ruminant workshops: GHG, energy, water, air, C storage, biodiversity, nourishing performance, economics Trained advisers (1-day L1 / 2-day L2 course), with farmer Web (a) Two explicit depth levels: Level 1 = 27 data points, 30 min, awareness/observatory; Level 2 = 150 data points, 3 h, action plan & simulation. (b) Level-1 "forfaits" (fixed defaults) are published per indicator – e.g. one housing type (full straw yard), 180 housing days for dairy heifers, one slurry store type, N excretion per animal category from Nitrates Directive references, 15 % legumes in grassland. (c) 3-step framing: Diagnostic → Action plan → Monitoring (import from other databases, export results, simulate levers, track over time). (d) Positioning against a reference group. (e) Ecocert certification covers data collection by advisers as well as method – consistency of entry is treated as part of the tool. Licence + training required; no self-service farmer path. Documentation is French-only.
4 GLEAM-i 2.0 (FAO) National/sub-national livestock GHG (6 species) via Herd, Feed, Manure modules; Baseline vs Scenario Policy planners, project designers, NGOs Excel workbook (macros); Shiny web version exists (a) Country selection loads Default values for every variable; three colour-coded columns: Default (light blue, locked), Baseline (dark blue, editable), Scenario (green, editable). Before edits all three are identical. (b) Herd module: enter reproductive adult numbers by system × herd type; parameters (fertility, mortality, replacement, weights) drive derived cohorts; total animal numbers update live in "special cells". (c) Feed: Summary ration by category + Detailed ration by feedstuff; "Ration percentages overview" turns green at 100 %, red otherwise, yellow if no animals. (d) Manure: % share per IPCC MMS per species/system/herd, same 100 % check. (e) Navigation bar with Previous/Next plus direct module jumps, selected country always shown top-right; short instruction block under the nav on every page. (f) Results: Baseline and Scenario side by side with % change labels; regional averages shown for reference; Tier 1 vs Tier 2 comparison. Excel with macros – not portable to low-end laptops/browsers. Country averages are the only defaults (no sub-national).
5 COMET-Farm (USDA / Colorado State) Cropland, animal agriculture, agroforestry, forestry GHG; baseline vs scenario US producers, conservation planners Web (a) Project Roadmap pinned to top of every page: icons per accounting activity with 4 states (unavailable/greyed with hover reason, incomplete, selected, complete); report only unlocks when all complete. (b) Fields drawn or uploaded on a map; soils (SSURGO) and weather (PRISM) auto-derived from location; model runs per soil map unit. (c) Per-animal-type accordions (Animal Details → Feed Characteristics → Housing → Manure), each with grey→green completeness tick. (d) Monthly tables for population and live weight with blue arrows to copy a value across months. (e) Feed Calculator: tick the months a ration applies, pick feedstuffs, enter as-fed kg/head/day, review summary, "Populate Feeds" – converts to DMI/CP/fat/ash. (f) Housing = % time per housing type per month (must = 100 %), and the housing choice triggers only the relevant manure follow-ups; solids/liquids tables warn in red if ≠ 100 %. (g) "Groups" = same category, different diet/housing/manure; capped at 3-5 per category. (h) Copy management to other years/fields; crop-rotation templates; duplicate project. US-only location (state/county), imperial units. Deep nesting (housing → separator → solids/liquids) is heavy for smallholder contexts.
6 Farm Carbon Calculator (Farm Carbon Toolkit, UK) Whole-farm/enterprise footprint; ~9 categories incl. livestock, inputs, sequestration Farmers self-entry; advisers Web (a) Explicit "Collate your data" step with a downloadable spreadsheet whose order mirrors the online form. (b) Category list on the left; free navigation but sequential recommended; toggle to mark a section done. (c) Every item added via "+ Add New" → cascading dropdowns; "if not applicable, leave it out". (d) Live results panel on the right updates after every entry (tCO2e, % by item, click segment for detail). (e) Report at 3 detail levels (Summary / Detailed / Full); compare reports; share; PDF/CSV. (f) Livestock: pick animal type, average number over period, what happened to manure; diet optional. ~2 h plus 2-3 h collation (Farmers Weekly). Nine sections feel long; benchmarking still being improved.
7 Carbon Navigator (Bord Bia / Teagasc, Ireland) Beef & dairy: 6 efficiency measures, current vs 3-year target, GHG % change and € benefit Trained advisers with farmers Web (adviser dashboard) (a) Prefill from national databases (AIM animal movements, ICBF, milk processors) – farmer not asked for stock numbers/ages/breeds. (b) Farmer consents via SMS authorisation code that the adviser enters. (c) Only six inputs per enterprise, each as Current / Target pair; date-picker or typed dd/mmm. (d) Results as bar charts of performance vs peers (orange current, green target), % GHG change and € benefit per measure; totals on top. (e) Advice sheet auto-seeded with pre-scripted options the adviser edits. (f) Excel download and print. Depends entirely on national data infrastructure that does not exist in most iCLEANED countries. Adviser-only.
8 FEAST data application (ILRI) Site-level feed diagnosis: focus-group PRA + individual farmer interviews; livestock holdings, feed sources by month, crops, income, prices Researchers, extension/NGO facilitators in SSA and S/SE Asia Windows desktop, no install (unzip, run .bat), fully offline; export .zlib to global repository (a) Strict hierarchy: Project → Site → Focus group → Interview respondent; site fixes country, currency and pasture nutrition defaults that prefill children. (b) Tabbed forms mirroring the paper guide (General, Farm/Household size, Seasons/Rainfall, Feed/Context issues, Agro-economic, Labour, Interview data). (c) Seasons defined by assigning months; rainfall 0-5 per month; feed availability per month as 0-10 scores across sources that must total 10 – ordinal scoring instead of quantities. (d) Prev/Next buttons cycle through months or categories inside a pop-up so the user never leaves the form. (e) Livestock holding = category → type → headcount + average weight (+ optional breed). (f) Custom crops/feeds/livestock/units allowed but discouraged ("only when necessary"), with required reference and nutritional stats; local units defined by kg equivalence. (g) Reports viewed in-app or via Excel package; site-level or group-level. Windows-only; deletion cascades permanently with only a warning; data keyed after the field session, not during.
9 EX-ACT (FAO) Ex-ante project carbon balance, 8 modules incl. Grassland & Livestock; with/without project Project designers (IFIs, ministries) Excel (no macros) (a) Description module first: continent, climate, moisture regime, dominant soil, project duration – these set every Tier 1 default. (b) Repeating colour code across modules: light-blue = enter, brown = variable/unit label, violet = optional Tier 2 override. (c) "Only fill modules your project touches; it is normal for many cells to remain empty." (d) Results by module, gross and per-hectare, with/without project. Spreadsheet navigation; no validation of totals; heavy for non-experts.
10 Ruminati (Australia) Farm-level scope 1-3 emissions for beef, sheep, dairy, grain, feedlot; reduction plans; supply-chain sharing Producers (free), advisers, corporates (VISION) Web app (a) Marketing promise "Your emissions report, done in under 30 minutes", positioned against months-long Excel GAF frameworks; designed by an external UX studio "simple and easy for farmers to fill out". (b) Uses "data you already have"; reduction-plan modelling of what-if changes. (c) Explicit data ownership: data stays private; sharing is opt-in per partner and revocable. (d) In-person onboarding events ("Burgers and Baselining"). Public docs do not show screens; inputs list (inferred) from press: property size, fertiliser, crop & livestock numbers.
11 MLA Carbon Calculator – Quick Start (Meat & Livestock Australia) Beef/sheep/grain/feedlot/goat GHG baseline and intensity Producers Web (a) Quick Start mode asks only for average livestock numbers per species; the tool applies the regional average distribution of stock classes (steers, cows, wethers, ewes). (b) Regional dropdown ("region where most cattle purchased") plus % purchased. (c) Per-step eLearning lessons keyed to each input page. Full mode still requires class-level detail.
12 AgriWebb (AU/UK/US) Livestock record-keeping: mobs, paddocks, movements, treatments, weights Commercial ranchers, farm teams Mobile (offline-first) + web (a) Offline-first: capture in the paddock, auto-sync when back in range. (b) Farm map as the home surface; mobs dragged between paddocks; landmarks. (c) Mob-level (group) records as default, individual animal optional. (d) 40+ typed record forms; team management. Subscription; heavy for one-off assessments.
13 Herdwatch (Ireland/UK) Compliance-ready cattle/sheep/dairy records Farmers Mobile + web, offline (a) Offline in the yard with automatic sync. (b) Herd managed "individually or in groups". (c) Prefill/submission to government registries (BCMS, ScotEID) removes double entry. (d) Bluetooth EID readers and scale heads; one account, many devices. Registry integration is jurisdiction-specific.
14 Breedr (UK/US) Individual cattle performance, weights, movements Cattle producers Mobile, offline capture (a) Chute-side flow: weight + EID captured in seconds, app shows ADG since last weigh and up/down trend immediately. (b) Records posted straight to BCMS/ScotEID. (c) Paid tier includes personal onboarding and phone support. Individual-animal centric; not suited to head-count descriptions.
15 Farmbrite (US) Whole-farm records incl. livestock, fields, accounting Small/medium farms Web + mobile (a) "Create Animal" needs only name + DOB; everything else optional, "you don't have to add all of it right now". (b) Bulk animal import from spreadsheet. (c) Three grouping models: smart groups (filter-based), manual groups, and "set groups" for animals never tracked individually (flocks). (d) Import boundaries from KML/CSV. Custom fields locked to paid tiers.
16 farmOS (open source) Assets (land, animals, groups, equipment) + logs Farmers, researchers Web (Drupal) (a) Draw land assets with polygon/line/point/circle; modify/move/delete vertices; snapping grid. (b) Location hierarchy (parent/child) by drag-and-drop. (c) Group assets contain animal assets → herds as groups. (d) Form fields "grouped into sensible tabs, with a sidebar of meta information". Developer-oriented; generic asset/log model needs configuration.
17 Climate FieldView (Bayer) Field boundaries and agronomic data layers Row-crop growers (US) Web + iPad + cab (a) Add a field by searching address / lat-lon / picking a CLU polygon, then adjust boundary; required minimal metadata (field, client, farm name, approximate area). (b) Bulk import of shapefiles; auto boundary detection in cab. Boundaries from cadastral CLUs only exist in the US.
18 iCow (Kenya) Dairy/poultry advisory, gestation calendar, expert search, marketplace Smallholders on feature phones SMS keywords, USSD (*285#), Android, KaiOS, web (a) Register a cow (insemination date) → automatic date-triggered SMS reminders. (b) Keyword SMS ("BLOAT") returns 10 tips; USSD numbered menus. (c) Same content across SMS/USSD/app/voice so "no farmer… is left behind". (d) Structured data collection via SMS (body condition, milk yield, calving dates). Per-message cost; free text limits; no visual data.
19 PlantVillage Nuru (Penn State / CGIAR) Cassava, maize, potato disease diagnosis in the field Smallholders and extension officers (phones distributed to lead farmers) Android/iOS, fully offline on-device model (a) Camera-first, real-time detection ("wave your phone in front of a leaf"), no typing. (b) Advice delivered offline in Swahili, French, Twi, Hindi, English; voice command being added for non-literate users. (c) Deployed through extension workers who own the device. Reliant on trained ML; not a data-entry tool.
20 Plantix (PEAT) Photo diagnosis + community + input shop Smallholders, mainly India Android (a) 18 languages; photo → diagnosis → treatment in one flow. (b) Community Q&A and video tutorials for low literacy. Requires data connection (2G+).
21 Farmer.Chat (Digital Green) AI advisory for extension agents Extension agents, lead farmers (India, Kenya, Ethiopia) WhatsApp / Telegram / web (a) Deploy on channels people already have – no app install. (b) Voice notes in + text-to-speech out in six languages; images accepted. (c) Deployed to agents, not farmers, as a human-review layer. (d) 20-30 s video tutorials and starter prompts because older users/women struggled to phrase queries. Data cost is the binding constraint, not literacy.
22 Hello Tractor (Nigeria/Kenya) Tractor booking Smallholders via booking agents Android app + agents (a) Booking-agent model: a trusted local intermediary enters farmer details (location, farm size, date) on the farmer's behalf. (b) Land-measurement tool is the agents' most-used feature. Farmer never touches the app – ok for services, but farmers lose visibility.

Other checked but not tabulated: Alltech E-CO2 (assessor-led 2-3 h farm visit, report within 5 days, benchmark + recommendations – a consultancy model, no self-entry); TechFit (ILRI, workshop scoring matrix companion to FEAST, not software); Sandy/Trinity AgTech (5-step set-up, API links to farm software, field-level – paid, arable-first).


2. Farm-description sequences observed

  • Cool Farm Tool livestock: General (system type, organic, reference period, name) → Production/Milk (breed → default weights, yield, fat/protein defaults) → Herd table → Grazing (days, h/day, type, quality per category; grassland area + fertiliser) → Feed (DMI per feed per category or % mix) → Manure (% per MMS per category; bedding) → Energy → Transport → results/duplicate.
  • Agrecalc: Farm report (name, year-end, avg temperature default 10 °C, sectors, groups, classes, enterprise type) → Land & Crops (areas, fertiliser, imported manures, transferred manure, pesticides, production & use, crop→livestock allocation, biochar, soil C) → Livestock (numbers & weights → purchases/sales/deaths → performance → manure → purchased feed → bedding → ration) → Energy & Waste → Results (whole-farm, enterprise KPIs, charts, benchmarks).
  • GLEAM-i: Start (name, region, country → defaults loaded) → Herd → Feed → Manure → Results (Baseline vs Scenario).
  • COMET-Farm animal ag: Project → Animal location (state/county) → select animal categories/types (+ groups) → per type: Animal details (monthly head, weight) → Feed (monthly, or Feed Calculator) → Housing (% time/month) → Manure follow-ups → Scenario (optional) → Report.
  • FEAST: Project → Site (country, currency, pasture nutrition defaults, custom feed sources) → Focus group tabs → Individual interview tabs (General, Property/Income, Livestock holding, Crops, Fodder & diet, Livestock sales, Milk, Feed availability by month).
  • Carbon Navigator: select farmer (prefilled) → 5 base facts → six measures Current/Target → Update → chart vs peers → advice sheet → print.
  • CAP'2ER: Diagnostic (L1 27 items / L2 150 items) → positioning vs reference group → action plan → monitoring/simulation.

3. Synthesis 1 — the 10 most transferable patterns for iCLEANED

  1. Two explicit depth levels with published defaults (CAP'2ER L1/L2; MLA Quick Start; GLEAM-i defaults; EX-ACT Tier 1/2). Offer "Quick (≈30 min, ~25 questions)" and "Detailed" modes. Quick mode fills everything else from region/system defaults and shows the list of assumptions it made. Apply on Home (mode choice per scenario) and surface the assumption list on Check & run.
  2. Set-up wizard that prunes later screens (Agrecalc report set-up; COMET category selection). Ask up front which species, systems and plot types exist; only those rows/cards appear in Herds, Feeds, Manure. Apply on Home → Location: "What's on this farm?" checklist.
  3. Herd as one table of fixed categories with counts and weights, breed-driven defaults (CFT Herd; Agrecalc classes; FEAST livestock holding). Fixed rows (e.g. lactating cows, dry cows, heifers, calves, bulls, draught) × {number, avg live weight}; pick breed → weights, milk yield, fat % prefill and stay editable. Running total of head and TLU (GLEAM-i "total animal numbers" live cell). Apply on Herds & animal groups.
  4. Percent-share rows with a live 100 % check, coloured green/red (GLEAM-i ration & MMS; Agrecalc red cells; COMET housing/solids; CFT manure). Use for manure system shares, diet shares per season, time-on-plot shares. Apply on Manure & inputs and Feeding plan.
  5. Season × month mapping and per-season ordinal scores (FEAST rainfall 0-5, feed availability 0-10; COMET monthly tables with copy-across arrows). Seasons defined by clicking months; a diet or grazing pattern entered once and copied to other seasons with one action. Apply on Seasons and Feeding plan.
  6. Persistent progress roadmap with four states and a locked Run button (COMET roadmap; Agrecalc Next/auto-save; FCC section toggles). Header shows each iCLEANED section as complete / incomplete / unavailable-with-reason / current; Check & run lists the blockers. Auto-save on Next.
  7. Location → auto-derived climate/soil, with edit affordance (COMET SSURGO/PRISM; Agrecalc soil C by postcode; EX-ACT climate/moisture/soil choice). Pin on map or pick admin unit → temperature, rainfall, AEZ, soil class prefilled and marked "from map – edit". Apply on Location & climate; draw or GPS-walk plots as simple polygons with area auto-computed (farmOS, FieldView, Hello Tractor agents' land tool).
  8. Live or near-live results panel and side-by-side scenario comparison (FCC live CO2e; GLEAM-i Baseline vs Scenario with % change; CFT "save as" duplicate; Carbon Navigator current vs target). Duplicate a scenario, change one thing, compare. Apply on Home/scenarios (duplicate) and Results (two-column, % deltas, regional reference band).
  9. Contextual help on every field plus a printable data-collection form that mirrors the screens (CFT hover "i" and notes box; FCC collation spreadsheet; FEAST printable forms; COMET data-entry spreadsheets). Ship a one-page paper/PDF checklist in the same order as the UI so an enumerator can interview first and key later, offline. Apply globally; the checklist becomes the FEAST-style field form.
  10. Advisor-on-behalf-of-farmer with consent and a validation step (Carbon Navigator SMS code; Agrecalc consultant validators; CAP'2ER certified data collection; Hello Tractor booking agents). Accounts have farms; a "collected by / farmer consent recorded" stamp; an optional reviewer flag before results are shared.

3b. Screen-by-screen application map

iCLEANED screen Patterns to apply (source) Concrete proposal
Home / scenarios Farm → scenario hierarchy with duplicate (CFT "save as", COMET duplicate); Quick vs Detailed (CAP'2ER); roadmap (COMET) Card per farm; each scenario card shows mode, completeness %, last saved; "Duplicate as what-if" action; "Collect on paper first" link to the printable form
Location & climate Location → auto climate/soil (COMET, Agrecalc postcode, EX-ACT); admin-unit fallback (Farmer.Chat data-cost lesson) Country → district pickers first; optional map pin; derived temperature, rainfall, AEZ, soil shown as "from map — edit" chips
Plots Draw/GPS polygon with area auto-computed (farmOS, FieldView, Hello Tractor); minimal required fields (Farmbrite) Add plot needs only name + type + area (typed or drawn); slope/soil default from location; bulk "same as previous plot"
Seasons Months assigned to seasons; ordinal rainfall (FEAST); copy across months (COMET) 12-month strip; click-drag to assign season; 0-5 rain score per month optional; defaults from AEZ
Herds & animal groups Fixed category table with counts/weights, breed defaults (CFT, Agrecalc, FEAST); live totals (GLEAM-i); groups only when management differs (COMET) One table per species; breed → weight/yield defaults; running head + TLU total; "Split group" only if diet/manure differ
Feeds Feed library by category with local names; custom feed discouraged but possible with reference (FEAST, Agrecalc list) Search-first picker grouped as forage / crop residue / concentrate / by-product; "add local feed" requires DM % and CP with source; link to plot if home-grown
Manure & inputs % time per MMS per category with 100 % check (CFT, Agrecalc, GLEAM-i); grazing deposition derived (CFT) Short MMS list (≤6) as slider rows summing to 100 % per group; fertiliser as product or "N only" pseudo-product (Agrecalc)
Feeding plan Season × group ration as % shares or ordinal availability (GLEAM-i, FEAST); copy to other seasons (COMET arrows) Matrix: rows = groups, columns = seasons; cell opens ration editor with green/red total; "copy to all seasons"
Check & run Locked report until complete (COMET); list of defaults in use (CAP'2ER forfaits, GLEAM-i default column) Blockers list with deep links; "Assumptions you are carrying" list of fields still on default; run is the only network step
Results Baseline vs scenario with % deltas and regional reference (GLEAM-i); 3 detail levels, PDF/CSV, share (FCC, CFT share code); peers/targets (Carbon Navigator) Two-column compare; Summary / Detailed / Full tabs; export PDF for farmer; anonymised share code

4. Synthesis 2 — low-bandwidth / low-literacy patterns (FEAST, Nuru, iCow, Farmer.Chat, Hello Tractor)

  • Offline-first with deferred sync (FEAST desktop app with export file; AgriWebb/Herdwatch/Breedr mobile). For a web tool: local storage of the draft, explicit "Saved on this device / synced" indicator, and the model run as the only network step (inferred design consequence).
  • Ordinal scores instead of quantities where farmers cannot give numbers (FEAST 0-5 rainfall, 0-10 feed availability summing to 10, 0-4 issue severity). Use for seasonal feed availability and pasture quality; convert to model quantities behind the scenes with visible defaults.
  • Intermediary-centred design: Nuru phones given to extension officers; Farmer.Chat deployed to agents as a human-review layer; Hello Tractor booking agents. Design iCLEANED for the enumerator/adviser as the hands-on user, with a farmer-facing printable summary.
  • Local units and names: FEAST defines custom units by kg equivalence and marks them "local"; use debe/bundle/bag with a conversion the enumerator sets once per site.
  • Language and voice: Nuru advice offline in Swahili/French/Twi; Farmer.Chat voice-in/TTS-out; iCow parity across SMS/USSD/app. Minimum for iCLEANED: short plain-English labels, icon + label for categories, glossary tooltips; Swahili/French/Amharic/Vietnamese label files as a later step (inferred).
  • Keep page weight tiny: iCow and USSD show the floor; Farmer.Chat found data cost, not literacy, was the binding constraint. Avoid map tiles by default (offer "pick district" first, map second), no heavy chart libraries on entry screens (inferred).
  • Short video/prompt onboarding (Farmer.Chat 20-30 s clips, Plantix tutorials, Ruminati in-person onboarding events).

5. Synthesis 3 — how the best tools handle "unknown" and defaults

  • Default is always visible and editable, never blank: CFT fills breed weights, fat/protein, feed source region; Agrecalc prepopulates ADG, digestibility, CP, manure N/P; GLEAM-i loads a full country default set and colour-codes locked default vs editable baseline; EX-ACT drop-downs of IPCC defaults.
  • Defaults are explained and scoped: CAP'2ER publishes each Level-1 forfait; Agrecalc says "use farmer estimates or industry default values … consultants will assess feasibility"; CFT's guide gives DMI ranges by yield class as sanity anchors.
  • Leave-blank rule is explicit: FCC "if not applicable, leave it out"; EX-ACT "normal for many cells to remain empty"; Agrecalc soil C "leave blank and we use postcode default". Blank = default, not zero.
  • Alternative entry routes for the same fact: Agrecalc offers "Urea / Other N / Other P" pseudo-products when fertiliser brands are unknown; CFT offers precise DMI or % mix; MLA Quick Start derives class distribution from a single total.
  • Derived quantities shown back: GLEAM-i total animals; CFT grazing-derived grass DMI and pasture manure "already calculated – do not add again".
  • For iCLEANED: every field gets a three-state control – entered, default (source shown), don't know → use default; the Check & run page lists all fields still on default so the user sees the uncertainty they are carrying (inferred synthesis).

6. Synthesis 4 — multi-farm, multi-user and advisor workflows

  • Hierarchy: FEAST Project → Site → Group → Respondent, with site-level facts (country, currency, pasture nutrition) inherited downward; COMET project → fields/animal types with copy-between-fields; CFT one farm per account by default, multi-farm switch for members; Agrecalc farm → reports (years) → enterprises/groups.
  • Roles: Agrecalc farmer / consultant / supply-chain users with consultant validation; Carbon Navigator adviser dashboard listing authorised vs unauthorised clients; Ruminati producer accounts plus corporate VISION dashboards fed only by opt-in sharing.
  • Consent and privacy: Carbon Navigator SMS authorisation code; CFT footprints private by default, shared via share code with farm name optionally removed; Ruminati data revocable at any time.
  • Reuse across years/scenarios: CFT "save as"; COMET duplicate project and copy management; FCC compare reports; Agrecalc "future entries ~1 h" because structure persists.
  • For iCLEANED: Organisation → Site (defaults) → Farm → Scenario (year or what-if); collector and farmer fields on each farm; share-code export of anonymised results (inferred application).

7. Synthesis 5 — how livestock GHG calculators structure herd and manure inputs

Aspect CFT Agrecalc CAP'2ER GLEAM-i COMET-Farm Ruminati/MLA
Herd unit 6 fixed age/sex categories per module Class list per group (10 cattle classes) with definitions Animal categories with N-excretion forfaits (L1) Reproductive adults + parameters → derived cohorts Animal types (+ up to 3-5 groups) with monthly head/weight Species totals; regional class split (Quick Start)
Numbers Average on-farm + sold + purchased (deaths excluded) Average over 12 months; opening/closing weights; purchases, sales, deaths, KO % Effectifs par catégorie Adult count; fertility/mortality/replacement Monthly population Annual average
Weights Live weight per category; dairy prefilled from breed Opening/closing per class; weaning weight for lambs Forfait by category Parameter defaults by country Monthly average, lbs Regional default
Performance Optional KPIs (age at 1st calving, calving interval, replacement) shown in Performance tab Required: age 1st calving, calving %, birth weight, ADG (prepopulated) Level 2 only Fertility, mortality, weight at slaughter Milk fat % monthly –
Feed DMI per feed per category or % mix; grazing-derived grass auto-added Purchased feed by category/type; ration digestibility & CP prefilled Average concentrate per system type (L1) Detailed ration % by feedstuff with summary by category Feed Calculator: as-fed kg/head/day by month → DMI/CP/fat/ash –
Grazing Days + h/day + pasture type + quality per category % time grazing in-bye / hill Housing days forfait Implicit in system type % time on pasture/range per month Region
Manure % per MMS per category; grazing time already accounted % time per MMS per class = 100 %; % exported; transfer to crops by % One housing type + one slurry store fixed at L1; spreading method & delay % per IPCC MMS per species/system/herd Housing → separator → solids/liquids % tables, storage length, temperature Simplified
Check – Red cells until 100 % – Green/red/yellow totals Red warning at ≠100 %; grey→green accordions –

Common denominators worth adopting: fixed category rows; average-count semantics stated in the UI (snapshot vs opening/closing average, with both illustrated in CFT's guide); manure as time-share per category across a short IPCC-based list; grazing deposition derived from grazing time rather than asked; performance KPIs optional but displayed.

8. Synthesis 6 — five things NOT to copy

  1. Spreadsheet-first delivery (GLEAM-i, EX-ACT): macros, colour codes as the only affordance, no validation beyond cell colour. Keep colour codes but as a secondary cue with text state.
  2. Making the user do the model's arithmetic (Agrecalc pesticide area ÷ sprays; Agrecalc lime annualisation; CFT pre-averaging herd numbers by hand). Ask for the raw fact and compute.
  3. Mandatory two-pathway detours (CFT: assess feed crops in the crop module, save as feed item, come back). Keep feeds and plots in one flow; link plot → feed inside the Feeds screen.
  4. Deep conditional nesting for manure (COMET housing → separator → solids/liquids → storage length → temperature). iCLEANED's context needs ~6 MMS options and a % time share; put the rest behind "Detailed".
  5. Dependence on infrastructure the user does not have (Carbon Navigator national databases; FieldView CLU polygons; Herdwatch registry sync; Plantix always-on data). Design for paper-first, offline draft, admin-unit fallback before map.

Also avoid: 9+ equal-weight sections presented as a wall (FCC/Agrecalc first-time 2-2.5 h); permanent cascade delete with a single warning (FEAST); adviser-only access with no farmer-facing summary (Carbon Navigator, Alltech).


Sources

Scenario Builder mockup: adversarial review and fixes

  • Date: 2026-09-29
  • Subject: ui_ux/mockup/ (build dev-20260929), reviewed by three independent passes: compile/validation correctness against the cleaned v0.7.0 input contract (node harness over ~45 mutated states), live UX walkthrough at 1280 px and phone width, and feedback-layer/runtime-contract/security hygiene against the artifact runtime .d.ts files.
  • Outcome: 93 findings (4 Blocker, 21 High, 47 Medium, 21 Low). All Blockers and Highs and most Mediums are fixed in the same build; the rest are listed under "Open".

Verdict

The redesign's core promise held under attack: compiled JSON has key/shape parity with the app's study-object format (0 missing, 0 extra keys apart from the intentional livestock_mapping and param_set_copy sidecars), unit conversions are exact, herd merges are head-weighted and flagged, every emitted manure/climate/slope/stock-change label is in the package's vocabulary, and the parameter-set copy overlay reaches the compiler. What failed was around the edges: a freshly added animal group broke (default objects stored as values), the dry-matter entry mode did not round-trip, several "silent zero" paths the redesign exists to close still slipped through (residue yield 0, blank rice fields, duplicate feed rows, 0 % N urea), screengrabs always failed (color-mix() in CSS), and keyboard entry lost focus after every value. The render scheduler could also stall for good in a hidden tab. All of these are fixed and re-verified live.

Blockers (all fixed)

ID Where Issue Fix
C01 30_dict.js decorate A new animal group with no hours stored the {value, source} default object as its hours; the card showed [object Object], validation emitted nonsense, compile sent time_in_stable = 1 and dropped manure questions. Store d.value; head-weighting falls back to equal weights when no group has a head count yet. Verified: fresh improved cows compile to 0.5/0/0.5/0.
F-01 styles.css html2canvas 1.4.1 cannot parse color-mix(); every screengrab failed and the failure toast was overwritten by the save toast. All seven color-mix() uses replaced with rgba tokens; "Capturing the screen…" toast while capturing.
F-02 30_location.js Location variant B (dropdowns) reset the region to "Region…" on change, so it could never set a location. Region handler captures the chosen value before rebuilding options. Verified: Kenya › Kakamega fills Lurambi, Tropical Moist, 1900 mm.
F-33 / render stall 10_store.js notify rAF never fires while the pane is hidden or unpainted; the scheduled flag stayed set and every later update was dropped for the life of the page. rAF raced with a 40 ms timeout; first to fire renders.

High (fixed unless marked)

ID Issue Fix
C02 / F-12 DM-mode diet entry did not round-trip; DM totals always read 100 %. The whole row is rebuilt from DM shares, other cells rescaled to fill the remainder, then converted to as-fed; the footer shows the as-fed total.
C03 The same feed item could appear twice (main + residue, or two plots), producing duplicate feed_items rows the package mis-pairs. Validation error naming the feed.
C04 Fields the compiler could not fill (rice_fields, soil_k_value, region) were "blocked" but not errors; Run stayed enabled. Every compile-blocked path becomes a Check & run error; rice fields are required for grown rice.
C05 Residue feeds with residue yield 0 passed (0 ha silently). Blocked with a message naming the feed.
F-03 / F-04 Season templates wiped the whole feeding plan silently; Copy/Same-for-all/Clear had no confirmation or toast. Two-step confirm when a plan exists; toasts name what happened.
F-05 / F-06 / F1 / F2 Focus lost after every committed value (inputs had no ids); dashboard search lost focus per keystroke. Focus restored by a stable key (nearest data-fb + control identity + index); search debounced with an id. Verified: focus stays on "In a roofed shed" after change.
F-07 / F3 "Reset example data" wiped feedback state; votes/ratings looked unsaved. reset() preserves fb.
F-08 Blank scenario had no way to name itself; Check linked to Home. Editable scenario name on About; error links there.
F-09 Manure follow-ups rendered above the choices they belong to. Radios appended before follow-ups.
F-10 "napier" found nothing. feedSynonyms table (Napier/elephant grass, Rhodes, dairy meal…) searched and shown first.
F-11 Three A/B vote widgets pushed content 250–540 px down on every card. Collapsed into one "Design alternatives (n)" box on list views only.
F4 Rating/vote ids built from a null viewer id collided. Falls back to the device's anonymous id; ids sanitised to the path grammar.
F5 A refused shared write went to a throwaway adapter nothing read. One local adapter kept from init; on refusal the page switches to composite (read live / write local) with the banner.

Medium (fixed)

C06 fertiliser %N range and 0 % blocked · C07 dangling plot/herd references cascade to null and enum values are checked against options · C08 unknown land-use choices fall back and block instead of throwing; compile/validate wrapped in try/catch in render · C09 head/area maxima removed for macro scale · C10 country → IPCC region by whole-name table (9 regions), unknown blocks · C11 intercrop fraction inverted to "share taken by the other crop" · C12 manure_sales_fraction = 0 (package double-discounts; matches the app) · C13 bought/collected feeds compile to Main so off-farm land counts; only the DB placeholder crop is Purchased · C14 database_code = the copy's base set, copy recorded in param_set_copy · C15 adult weight taken from the (overlaid) row before falling back · C16 validation labels translated for scale · C17 feeding errors route to the season tab · C19 parity fields (*_ipcc, grassman*, "NULL" strings) match the app · C20 compile/validate read the parameter copy from the passed state · C22 negative rates, out-of-range months, fertiliser rows gated · C23 "left where it drops" forces collected = 0 · C24 unknown livestock/feed codes blocked and skipped in the basket · C25 soil depth default 0.3 m · F-13/F-14 remaining "farm" and raw {token} leaks (herd patterns, validation labels, callouts, boundary text) · F-15 herd pattern change re-derives hours for groups without hand-entered hours (toast) · F-16/F-17 chip menu focuses the right control; "Clear the value" for user values without defaults · F-18 seeded feed origin/plot marked "assumed" · F-19 "Looks right — keep it" on assumed values; month arrays print as names · F-20 world region shown as calculated · F-21 feeding totals row carries data-fb so jump links flash it · F-22 new entities show no red until touched or until Check has been visited · F-23 participant-view banner clears on route change · F-24 phone top bar collapsed to one chip · F-25 placeholder contrast; theme button labelled · F-26/F15 focus rings on chips, rating scale, radios, months · F-27 skip link · F-29 "farms represented" at group scale · F-30 About says "answer the remaining questions" instead of skipping steps · F-31 "from maps" only claimed when a location exists · F-32 fertiliser status from data · F-34 plain labels for land-cover classes · F-35 "animal group" wording · F-36 rounding, version in proposal · F-37 season hint always shown · F-38 close button on the model-input panel · F-39 toast after removal · F6 one in-flight write per document · F7 subscription errors surface a banner · F8 import validates ids and fields, whitelists screenshots, throttles writes · F9 screenshot src must be a base64 image data URL · F10 CSV formula-injection guard · F11 session/name/group persisted separately · F12 localStorage carries the schema version; persisted system/meta merged with fresh defaults · F13 <!doctype html> on the offline file · F14 aria-live removed from the re-rendered screen · F16 highlight overlay touch-action: none and pointercancel · F17 preview not rendered while hidden · F18/F19 build validates types, option predicates, _meta.* names, required field ids; < escaped in inlined JSON · F20 byte-accurate size ladder, 4000-char text cap · F22 saves guarded until the store connects · F25 store banner has its own class.

Open (not fixed; decisions or follow-ups)

  • C18 / manure labels: "Burned for fuel" is the Table 10.17 label; 10.21/10.22 use "…or as waste" and hold the string "NA". Kept 10.17; file a package data issue.
  • C11 intercrop direction and C12 manure sales double-discount need confirmation from the package maintainers; both are documented in the compiler.
  • F-28 offline dependencies: Google Fonts and html2canvas still load from CDNs; the offline file falls back to system fonts and text-only capture without network. Vendoring html2canvas into the offline build is the next step.
  • F-36 Goats → IPCC "Sheep" class is DB data, not a mockup bug.
  • F21 participant-view variant restore, F24 focus return after screengrab, F27 object URL revoke, F28 composite merge preferring remote, F29 README note on network dependencies: cosmetic, deferred.
  • Keyboard: Tab through the full sidebar before the form is mitigated by the skip link only.

Done well (from the reviewers)

Provenance chips with actionable menus and one legend everywhere; parameter-set copy workflow with per-cell "was … · revert"; plain-language manure choices with the exact IPCC mapping visible; keyboard behaviour of the comment panel (Enter opens, Tab wraps, Escape returns focus); purchased inputs in the user's units with a live kg N readout; comma-decimal normalisation with a toast.

Re-verification (live, after fixes)

Fresh improved-cow group → time_in_stable 0.5, time_in_onfarm_grazing 0.5, manure system "Solid storage" · hours input keeps focus after change · DM entry of 50 shows 50 with the as-fed footer total · "napier" finds "Napier grass · Pennisetum purpureum" · Location variant B sets Kakamega/Lurambi with map defaults · national scale reads "About this national herd", "Grazing on private land / on communal or open land" · reset keeps the feedback store connected · demo scenario: 0 errors, 0 warnings, 10 assumptions, 45 parameter-set values · Germany → WESTERN EUROPE · no console errors · no color-mix in the built file.

v0.7 — answers to your questions (2026-09-29)

Seven questions, and what changed in the mockup for each.

1. Can the animals be entered with the herd? Yes, and now that is the main route. The herd card carries an "Animal groups in this herd" table (group, head, the four day hours, Edit) and an + Add an animal group button that opens the species → group picker inside the herd, so you never have to pick a herd from a dropdown. Step 7 (Animals) stays as the overview and the per-group editor.

2. Should manure management be on the herd, not the animal? Agreed, and it moved. Manure handling and the collected share are answered once per herd (hmanure_stable, hmanure_pen, hmanure_onfarm, hmanure_offfarm, hmanure_kept_share), because handling follows the place the animals are kept, not the animal category. Each animal group inherits them, shows what it inherited with a "⇣ from the herd" chip and a link to change it for the whole herd, and can still override in a folder called "Manure for this group only" — a calf pen handled differently is real, but it is the exception. The compiler is unchanged in shape: it still writes manureman_* and manure_in_* per livetype_desc, resolving herd → group override, and still merges by head count when one animal type appears in two herds.

3. Feeding plan linked to the herd. The feeding plan is now herd-first: a Herd tab row above the Season tabs, with a group count and an error badge per herd, plus "All herds together". "Copy from " and "Clear" act on the herd you are in, not the whole scenario.

4. Multiple herds — one plan each? Yes. The screen states it: "2 herds × 2 seasons = 4 grids", and every group's column must reach 100% in each. Two of the example scenarios (Rungwe commercial, Tanzania national) have two herds so this is testable in the workshop.

5. If fertiliser is entered on the feeds, what is the Fertiliser section for? Only for what is in the bag. Rates and placement stay on the feed cards; this screen asks the nitrogen percentage once per product, because the model works in kilograms of N and two crops using the same product share one number. The screen now says so, lists which feeds use each product with their rates and a "change" link, and shows the resulting kg N per hectare. It appears only when a product is actually used.

6. Show the selected parameter above the tables, unlockable and editable. Clicking any row in a parameter table opens it above the table as its own transposed table: plain label, unit, value in this set, shipped value, and per-cell Revert. It is locked by default (🔒) with Unlock to edit; on a shipped set the button is "Make my own copy to edit" instead. Column names are now plain language with units (cp_lactmilk → "Crude protein per kg of milk"), and values that belong to other species are parked in a folder instead of padding the table with zeros.

7. Feed nutritional parameters: a folder you can unlock. Each feed card has a "Nutritional parameters" folder showing dry matter, energy, protein and the two nitrogen contents read-only with their source chips. It offers the choice explicitly, because this is the part that needed deciding: Unlock for this scenario only (an override on this feed in this scenario, parameter set untouched) or Edit in the parameter set / Edit the crop row (jumps to the parameters screen with that row selected and unlocked, changing every scenario that uses your copy of the set). The folder spells out the difference, so nobody edits a shared default when they meant to fix one scenario.

Where are the results shown? On the Results screen (#results), which in the real app receives the model output. The mockup does not run the model, and now says so at the top of that screen, with a link to Check & run for exactly what would be sent. It also shows the size of what you described — animals, hectares, annual milk, herds, feeds, seasons — labelled as inputs added up rather than results, so a wrong scale is caught before running. What belongs on the screen is deliberately left open for the workshop.

Project vs scenario. A scenario is one description of one enterprise for one year. A project is the folder that groups related scenarios and the people who may see them; scenarios in a project can be compared with each other. Both are now defined in the welcome glossary and on the Home screen, which previously showed a Projects card with no explanation.

Verified live: all six example scenarios compile with 0 errors (Rungwe keeps 2 legitimate warnings) and the herd manure answers reach the package labels — biogas → Anaer digester, Low leak, HQ stor, LQ tec, wet pit → Liquid/Slurry Pit below animals 3 Month, covered heap → Solid storage - Covered/compacted. Editing "Live weight" in an unlocked parameter row records {from: 350, to: 365} in the copy; "Edit in the parameter set" from a feed card lands on feed row 11 with the panel open.

v0.7 follow-ups

Build stamp in the header. The header now reads Scenario Builder · mockup v0.7.0 · 29 Sep 16:46 — semantic version from package.json plus the build time to the minute, so you can tell at a glance whether what you are looking at includes the last change. The full stamp (with target) is in the title tooltip and on the welcome screen; on a phone the prefix drops and the version stays.

Sidebar no longer lists every record. Listing one child per feed does not survive a real feed list. Entity steps now show a count (Feeds ×12), and list children only while there are six or fewer — small scenarios keep the map, large ones get 12 feeds · 2 to fix · filter linking to the step.

Filter chips on the list screens. Feeds, Animals and Plots gained a chip row built from the data itself, so a chip appears only when it matches something: for feeds, origin (grown / bought / collected), main product vs residues, and one chip per plot; for animals, one per herd plus milking and young stock; for plots, the land use. Each chip carries its count, there is a search box matching feed names, crop names and local synonyms, a "Needs attention" chip when something is blocking, and a card/list toggle that switches to a compact table automatically above eight records. Search keeps focus while filtering.

Topbar crowding fixed. Between 900 and 1200 px the scenario chips were being squeezed to 77 px because the action buttons never shrink. The scenario row now always sits on its own line.

Batch upload (v0.7.1)

A separate Batch tab in the sidebar, for descriptions that already exist — a household survey, a project's monitoring sheet, a district inventory — rather than typing each enterprise into the wizard.

The file contract. One table per thing you are describing, joined on enterprise_id: enterprises, seasons, plots, herds, animals, feeds, diet. Either a .xlsx workbook with one sheet per table, one .csv per table (uploaded one at a time and merged), or a single .json. Columns hold names, not codes — "Napier grass", "Cows, local breed" — because that is what a field officer's spreadsheet holds; an unknown name is reported, never guessed. Templates download from the screen (JSON, and one CSV per table). The xlsx reader (SheetJS) loads from the CDN only when a workbook is actually dropped, and says to use CSV if it cannot.

QAQC. Every enterprise is built into a scenario and run through the same validator and compiler as a typed one, so a batch that passes would pass field by field. Problems come in two layers: file-level ones naming the sheet and column (animals.hours_*: "Cows, local breed": the day adds up to 27 hours, not 24; feeds.feed: "Napier (local variety)" is not a feed in this parameter set) and the model-level ones the wizard already knows. The table shows one row per enterprise with animals, land, status and an expandable problem list, filter chips (All / Blocked / Check / Ready), and "Open in the builder", which loads that one enterprise into the wizard while leaving the batch intact.

Visualise. Batch totals plus ranked distributions of animals per enterprise, land per enterprise, milk per animal and animals per hectare, each with median, mean and extremes. Labelled as inputs added up, not model results — their job is to expose the unit mistakes a batch hides: litres a day typed as litres a year, hectares as acres, a herd entered once per animal. After a run this is where the same enterprises would be ranked by emissions per kilogram of milk.

Download. QAQC report as CSV with one row per problem (sheet, column, message) so it can go straight back to whoever filled the sheet; compiled model input as JSON, either ready-only or everything, one study object per enterprise with its assumed values; and the file as read.

Sample batch. A button builds 12 enterprises from the example scenarios with ±15 % jitter and three deliberately broken rows (a 27-hour day, a diet column at 85 %, a feed name not in the parameter set), so the QAQC table has something real to show in the workshop. Verified: 7 ready, 2 with the legitimate Rungwe warnings, 3 blocked, each naming the right sheet and column.

Information architecture, second pass (v0.7.2)

Four changes from your reading of the structure. The wizard is now 10 steps, not 13.

"What we count" left the wizard. It was a page of explanation sitting in a sequence of data-entry steps, and it made the wizard look longer than it is. It is now background material, reachable from the sidebar's reference group, from the welcome screen and from the FAQ, with no step number and no place in Next/Back.

"About this farm" is renamed. On the web, About means who we are and how to contact us, so using it for the system description was a false signal. The step is now "Your livestock system" (sidebar: "Your system") — "a few questions about what you keep and how, so we only ask what applies to you".

Help, FAQ and contact now exist. A new reference page with twelve questions answered (what it calculates; scenario vs project; what belongs inside the boundary; whether every field is needed; why results come out zero; where defaults come from and what you may change; a feed not in the list; units; the comma decimal; batch; who can see your data; how to report a problem), a "stuck right now" box, links to the background pages, and a reporting table that routes an app problem to CIAT/icleaned, a wrong result or a wrong default to CIAT/cleaned, and anything workshop-related to the in-page Comment button. It carries no email address — the team's channel is filled in by whoever runs the session. Reachable from "Help & FAQ" in the top bar and the sidebar, and it shows the build stamp to quote in a report.

Location, seasons and land are one step. Step 2 is now "Place, climate, seasons and land": where the animals are, the climate and soil from maps, then the seasons, then the land. Seasons and plots keep their own detail screens for month strips and per-plot answers, and hang under the step in the sidebar instead of taking step numbers of their own.

Seasons are proposed from the rainy months, which answers the naming problem. You already tick the months with rain in a normal year. The app reads the contiguous runs of those months and offers named seasons — "Rainy season (Nov–Apr) · Dry season (May–Oct)", or "Long rains / Short rains / Dry season" where there are two wet runs — with one button to accept and inline renaming afterwards. The suggestion disappears once the seasons match it, however they are named and in whatever order they are listed, and taking it warns first when it would clear an existing feeding plan. Verified on the Kenyan template: nine rainy months became "Rainy season (Mar–Nov)" and "Dry season (Dec–Feb)", 365 days placed.

Also fixed in passing: the feeding plan showed as "not started" in the sidebar even when fully entered, because the diet lives in allocation, which is not provenance-tracked.

v0.7.3 — sidebar as a map, enterprise wording, labelled chips

Records left the sidebar. Feeds, animals, herds, plots and seasons no longer list one line each in the contents. A step shows a count (Feeds ×12) and nothing more, so the sidebar stays a map of the ten steps at any enterprise size.

Records became tabs on their own screen. Each entity screen carries a tab strip — 5.1 Brachiaria hybrid · 5.2 Maize stover · 5.3 Rhodes grass · + Add · All feeds — with the numbered heading on each tab, an error badge where something is blocking, and previous/next under the card. The numbers are the same ones used for feedback, so "5.2 is confusing" still resolves to one record. The filter chips and the compact list remain for long lists.

"Farm" became "enterprise" throughout. The scale words now read enterprise / on your own land / off your land (communal or rented) / enterprise gate, and the questions follow: "Which animals does this enterprise keep?", "Grown on land you manage", "Collected off your land". The scale question's own first option keeps the word where it is the literal unit — "One enterprise (a household or a farm)" — and the two deliberate contrasts stay ("a herd model, not a farm model"). A sweep of every screen and every entity card found no other use.

Top-bar chips say what they are. Each is now labelled and carries a tooltip explaining the concept and how things nest: Project (groups scenarios and the people who may see them; project → scenarios → the herds, land and feeds in each, with a parameter set beside them), Scenario (one enterprise for one year; baseline vs intervention), Describes (the scale and size), Defaults from (the parameter set, what may be edited and what wins).

How many herds can an enterprise have? (investigated in the package, 2026-09-30)

In the mockup: as many as you keep. In the model: none — it has no herd concept at all. A study object holds one flat livestock array, one row per animal category, and livetype_code is its de-facto primary key.

What the package does with a repeated category, verified in the source:

  • Same livetype_code twice → the run stops. feed_quality() selects the category by code (R/feed_quality.R:40), gets two rows, and rep()s a length-2 vector against an n-row frame at :137 — "arguments imply differing number of rows". A loud failure, but from a message no user could act on.
  • Same description, different code → silently wrong. energy_requirement.R:192-194 sums intake grouped by code, :199 re-joins the livestock table, and :211-212 joins twice on a non-unique key, so each duplicate's time_in_* and manureman_* are applied to the combined intake and a 2×2 cartesian comes out. land_productivity.R:73 and ghg_emission_v2.R:122 join on the description string, so duplicate names fan out there too. Nothing in the package validates for duplicates (grep -rn "duplicated" R/ is empty); water_requirement.R:75-80 is the only place that is duplicate-safe, because it sums row-wise off the raw input without joining.
  • herd_composition is not a grouping key despite the name — it is the head count of that category, a scalar multiplier (land_productivity.R:56-65, energy_requirement.R:134). The app's own column header for it is "Number" (30_mod_scenario_init.R:53-56).

So the mockup's head-weighted merge is not a convenience, it is the only thing that produces a runnable file, and it is now stated as such on the Herds screen with the affected animal types named. The escape routes are real but limited: give the two groups genuinely different animal types, or describe them as two scenarios and compare.

New finding for the current app (UX-89, High). 30_mod_scenario_server.R:985 guards adding a livestock category with if (!(input$livestock %in% livestock_data()[, "livetype_code"])) — adding a category that is already in the table is a silent no-op: the modal closes, no row appears, no message. A user trying to describe a second group of the same cows gets nothing and no explanation. The guard is protecting the pipeline from the crash above, so the fix is a message, not removal.

For the maintainers. A herd dimension would need a key change in at least feed_quality.R:27/40/148, energy_requirement.R:152/163/192/199/211/212, land_requirement.R:31, land_productivity.R:38/44/54/73, ghg_emission_v2.R:89/100/122/193/199, plus the app's basket columns at 30_mod_scenario_server.R:2417/2628. Short of that, the cheap win is a validation that rejects duplicate livetype_code with a sentence a user can act on, and a lookup that allows user-defined categories (which the CLEANED-flexible mapping the mockup already emits would then feed).

v0.7.4 — batch made visible, a draft that survives an interruption, and a disclaimer

Batch processing is now a first-class route, not a footnote. It existed from v0.7.1 but sat below the reference pages where nobody would find it. It is now named "Batch processing" in a sidebar group called Tools and reference, has a ⇈ Batch entry in the top bar, and has its own card on Home — "Many enterprises at once", with one paragraph on when to use it and a button straight into it. The route (#batch) and everything on it are unchanged.

The form now keeps a draft, and says so. A first pass takes 15–30 minutes, so an interruption before any save point must not cost the work. Every change is written to the browser's local draft within 300 ms, and the wizard bar carries a live ✓ Draft saved · just now that ages by itself (just now → 40s ago → 3 min ago). If the browser refuses to store (private window, storage full) it says Draft not saved instead, with a tooltip pointing at the export on Check & run, because a silent failure here is worse than none. On reopening, the draft is restored and a toast names the time of the last change: "Unfinished draft restored — last change 30/09/2026, 09:28:56." Verified by typing a value and reloading with no save: both the scenario name and the changed rainfall came back.

Requirement for the real app (bigger than the mockup can show): the draft must be local-first and sync to the account, so that closing a laptop, losing the connection or a server restart cannot lose a part-finished description. Concretely: write locally on every change; sync in the background with a visible state (saved / saving / offline, last-synced time); on reopening, offer the newer of local and server rather than silently picking one; keep a short version history per scenario so a bad edit can be undone; and never block a user behind an explicit Save button, since the current app has none and people still lose work on a grey screen (UX-01, UX-39). The same applies to a batch: an upload and its QAQC result should be recoverable after a browser crash.

Disclaimer, on every screen where a number might be believed. Welcome, Home, Check & run, Results, Batch and Help now carry it, and it is written into both batch exports (a comment line at the top of the QAQC CSV, a disclaimer field in the model-input JSON):

Garbage in, garbage out. iCLEANED calculates from the description you enter and the defaults you accept. It cannot tell whether a number is right: a wrong herd size, unit or yield produces a wrong result that looks just as confident as a right one. Check the inputs, and the assumed values listed on Check & run, before quoting any output. Results are indicative, for comparing scenarios rather than for reporting absolute figures, and the developers and their institutions accept no responsibility for the accuracy of the outputs or for decisions taken on them.

On Check & run it also states how many assumed values the scenario carries — "every one of them is a number nobody typed" — and on Batch that one mistaken column applies to every row in the file. The wording is a starting point: the institutional half of it should be checked by whoever owns the tool before release.

v0.8 — a horizontal contents bar, a real Results section, and a feature queue

Two levels of navigation, so neither has to do both jobs. The major sections now sit in a horizontal bar under the header — Scenarios · Describe · Results · Batch processing · Parameters · Feature requests · Help, with Feedback on the right — and the sidebar holds only what is inside the section you are in. In Describe that is the ten steps (with Seasons and Land nested under step 2); in Results it is Summary / Greenhouse gases / Land & feed / Water / Nitrogen / Compare; in Parameters the nine parameter tables; in Scenarios mine / shared / templates; in Feature requests open / shipped / everything. The header and the bar scroll as one sticky block, and the bar scrolls sideways on a phone. Batch processing is a top-level section in its own right, so it is no longer reachable only through a shortcut.

Results exists properly now. It was a placeholder, which is why it kept not being findable: it was excluded from the navigation entirely. It is now a full section with six tabs: headline indicators (greenhouse gases, emission intensity per kg of milk, feed eaten, land, water, nitrogen balance), production (milk, meat, milk per cow), emissions split into enteric methane and manure nitrous oxide with a ranked bar per animal group, land and feed with a per-plot supply-against-demand check, water, a nitrogen in/out balance with a plain-language explanation of why a negative balance matters, and a comparison tab.

The numbers are a sketch, not the model — the mockup cannot run cleaned, and the screen says so in a banner that cannot be missed. Each figure comes from a deliberately crude calculation from the actual inputs (enteric methane from head counts and IPCC-ish per-head factors × GWP100; nitrogen excretion from live weight; intake at 2.5 % of live weight per day; 1,200 litres of green water per kg of feed dry matter), and every formula is printed on the screen under "How this sketch got its numbers", with the factors in the download too. This is deliberate: a results screen with no numbers cannot be tested for layout, units or wording, and invented numbers with hidden arithmetic would be worse than either. A 20-animal Njombe baseline sketches at 54.8 t CO₂e a year and 4.62 kg CO₂e per kg of milk — the right order of magnitude, which is all it claims.

Feature requests are a visible, ranked queue. A section listing what is queued, what is being built and what is already in the mockup, each with its rank, a rough size (days / weeks / months), where the request came from, what it would let you do, and — the part that usually gets lost — why it matters. Anyone can add a request from the page and vote on an existing one. Eleven are seeded from this work, with #1 Scenario planning for ex-ante assessment: define an intervention once, apply it to one or many baselines, compare side by side with the input uncertainty visible, and include adoption assumptions so a district or national effect can be estimated from an enterprise-level description. The rank is shown precisely so it can be argued with in the workshop; comments are about what exists, this queue is about what does not.

v0.8.1 — "Why iCLEANED": the case, the offer, use cases, and an honest comparison

A new top-level section, first in the bar, with five sub-sections.

The question it answers. The argument stated in one line — most tools tell you the carbon; livestock decisions are rarely only about carbon — then the five indicators with a sentence each, and the three things that follow: one description at any scale, built for systems where feed is residues and communal grazing rather than a formulated ration, and comparison before absolutes.

What we offer. Six concrete things rather than a product pitch: the open R package (no lock-in), this app as a hosted entry point, regional parameter sets built with a partner from their own data and published as named, versioned, citable sets, batch assessment of data that already exists, training and facilitation with the workshop materials, and co-developed analysis on a specific question. With an explicit what we do not offer: no certification, no audited footprint, no farm-management system — and a pointer to Cool Farm or Agrecalc where an audited number is what's needed.

Working with partners. Five partner types (ministry or statistics office, research programme, NGO or project, dairy hub or processor, extension service), what each needs, and what we would actually set up for them; plus the six steps an engagement goes through, with the honest note that steps 3 and 5 — building the parameter set and running the real data — are where the time goes, and both are data work rather than modelling.

Use cases — explicitly placeholders. Five sketched (national inventory, project baseline and ex-ante appraisal, dairy hub sourcing, intensification pathways, teaching), each carrying a "before this can be written" line naming what is missing: a partner, real data, or a queued feature. A banner says plainly that none of them has been done. They are there so the shape can be argued about, not to imply a track record.

How it compares. Seven tools side by side — iCLEANED, Cool Farm Tool (Beef & Dairy), GLEAM-i 2.0, Agrecalc Cloud, CAP'2ER, COMET-Farm, FEAST — on who it is for, what it reports, where, at what scale and how it is accessed, drawn from the manuals read for 04_design_benchmarks.md. Every entry has a "better than us at" line, and iCLEANED's own says "young, thin documentation, and the interface is what this mockup exists to fix". The closing paragraph sends the reader elsewhere where that is the right answer: Cool Farm or Agrecalc for an audited supply-chain figure, GLEAM-i for a national estimate from country defaults, CAP'2ER and COMET-Farm inside France and the United States. What is left is the case iCLEANED is actually for: a mixed crop–livestock system in the tropics, where the question is land, water and nitrogen as well as carbon, and where one description has to work from a household to a national herd.

A comparison that only flattered the tool would be worth nothing in front of the partners who already use those tools, so the dates and the basis are stated and a comment can correct anything out of date.

v0.8.2 — use cases by segment, and capacity sharing in the queue

Work drafted in a parallel session, reviewed and kept here with two corrections.

Use cases are now organised by the four segments the team named, replacing five flat placeholder cards. Research networks, implementing partners, advisory services and government institutions, because each makes a different decision, uses a different interface and needs different training. Each segment carries: who it actually is (named institutions), how CLEANED fits as a first pass written to be argued with, the honest part — what is wrong or missing in that segment — a table of candidate use cases with a "Blocked on" column cross-referencing the feature queue, and a list of what somebody still has to go and find out, most of which is asking a partner a question rather than research.

Two of the four carry a "Never tried" chip: advisory services and government institutions. The other two say "Done before". The old placeholder cases were redistributed into the segments they belong to rather than thrown away.

Three things in it are worth keeping visible because they are the kind of thing a pitch usually hides:

  • Research networks: reproducibility is listed as the weak point, not a strength — the batch workflow sources its functions over HTTP from a moving development branch, so the same script run a month apart can give different answers.
  • Advisory services: "CLEANED is a rapid ex-ante estimate built for comparison, not a farm-specific prediction. Ranking a farmer's options is defensible. Telling one farmer what their footprint is, is not."
  • Government institutions: the GLEAM question is named as a decision for the team — complementary or competing — and stated as something to settle before any government conversation.

A fifth possible segment, value chains and processors, is raised as an explicit in-or-out decision rather than quietly included or quietly dropped, with the recommendation to leave it out until verification exists, since corporate reporting needs an audited figure against a recognised standard and CLEANED is not close to that bar.

Capacity sharing entered the feature queue at #7, which pushed the printable form to #8, languages to #9, maps to #10 and the two shipped items to #11 and #12. Three parts: a structured request training route that records who is asking, which segment, how many people, which interface and what data they hold — so demand is visible instead of arriving as email to individuals; a modular short course whose worked examples run on the requester's own country and data (a context module they supply, so a new country is a config file and a dataset rather than a fork of the materials), with a credit-bearing version a university could adopt; and short task-shaped videos indexed by task and segment. The reason it is a feature and not a workplan line: training is already listed under what we offer, there is no way to ask for it, nothing to hand over, every workshop is rebuilt from scratch, and a recorded queue of requests is the evidence a capacity-sharing budget line needs in a proposal.

Corrections made when merging: the cross-references to the queue were written against the old numbering and pointed the printable form and languages at the wrong ranks; both were fixed, and the feature body now renders its markup like the other rich entries.

v0.8.3 — no funding or fundraising content in the tool

The merged draft carried two traces of it — a training-request field recording “whether funding is attached” framed as making demand visible, and a line arguing that a request queue is the evidence a capacity-sharing budget line needs in a proposal. Both are removed: the tool describes what it does for the people using it, and anything about instruments, budget lines or demand evidence lives in internal notes outside this repository. Also reworded: a “the money” phrase in the value-chain question, a compile item asking what a partner’s donor wanted in the donor’s own words, and a use case titled “Reporting a change to the donor”, now “Reporting the change at endline”. A sweep of every screen and every tab for funding vocabulary comes back empty.

v0.9 — "scenario" becomes "assessment", and an enterprise now holds a timeline

The word changed because the thing was two things. "Scenario" was carrying both this is the herd as it was in 2024 and this is what would happen if we did X, which is why it read as jargon and why tracking over time was impossible. Split:

  • An enterprise is what persists — a herd and the land that feeds it. It has a name, a place and an owner.
  • An assessment is one description of that enterprise: dated when it records what was there that year, marked what-if when it describes a change being considered.

The section, the tab and the counts are all Assessments — that is the thing you name, open, run, compare and share. “Enterprise” is only the grouping the assessments hang under, and the word was already in use in the tool for the physical unit (a livestock enterprise), so it adds no new vocabulary. Tracking over time is then ordinary rather than a feature: another dated assessment of the same enterprise. Home is now a list of enterprises, each showing its assessments as a timeline with year, label, kind and size, and three actions — Assess again this year, Test a change, See the change over time. A follow-up copies the most recent observed assessment, so a re-assessment is a review of what changed rather than a re-entry from nothing. The examples were regrouped into five enterprises, and the Njombe enterprise gained a real 2026 follow-up (herd 20 → 26, fodder plot 0.8 → 1.2 ha) so the timeline has something behind it.

Results gained three comparison modes, replacing a single mocked one:

  • Over time — the same enterprise's dated assessments in a table, each figure carrying the percentage change on the previous one. On the Njombe enterprise: animals +30 %, total sketch emissions +32 %, emissions per kg of milk −28 %, which is exactly the trade-off shape a monitoring conversation needs. It also states what it cannot do: it shows that something changed, not why, and attribution is queued.
  • Against another assessment — the baseline-versus-what-if comparison, now able to reach any assessment of any enterprise.
  • Against a benchmark — deliberately unbuilt, with what it would compare against listed in order of honesty (others in the same project or batch first, since method and parameter set match) and the trap named: a single national average presented as a target invites a like-for-unlike judgement.

Three feature requests added, at ranks 3, 4 and 5:

  • Track an enterprise over time, and attribute what changed. The structure now exists; trends, a stated comparison basis and attribution do not. Without a stated basis, a trend line is worse than no trend line — it is how a rainfall year gets attributed to a project.
  • Benchmark context for every result. A figure with no context is the most common way a result is misread.
  • Test a range of values, not one value at a time. Enter several candidate values for one or more inputs and run every combination; ranked results and the inputs that move the answer most. Covers sensitivity and option screening, and is the cheapest honest answer to the uncertainty problem: if an input can plausibly take three values and the ranking does not change, the ranking is robust.

Also: queue cross-references are now by name rather than by number, since they broke twice when the queue was reordered; the collection key in the data model was renamed with the vocabulary, not just the labels; and the mangled auto-rename of the first queue item ("Assessment planning for ex-ante assessment") was rewritten as "Ex-ante appraisal".

v0.9.1 — projects become a control, and three things the rename broke

Project was named everywhere and controlled nowhere. It appeared as a labelled chip in the top bar, in the tooltip explaining how things nest, in the welcome glossary and on a card at the bottom of the list — and no screen let anyone create one, put anything in one, or filter by one. Now:

  • Filter chips above the list: All projects · one per project with its count · No project, shown only when there is something to filter, and reset when you switch between mine / shared / templates so a filter can never leave an unexplained empty list.
  • A project select on every enterprise card, which moves the enterprise, all of its assessments, and the open assessment's top-bar chip in one action.
  • A project field on the new-assessment card, with + New project… revealing a name box and creating the project on save.
  • The Projects card is now a control: project, enterprises, assessments, people, a Show button that applies the filter, a mocked Invite, and a create box. Its empty state says plainly that work can sit outside a project and that one is worth making when more than one person needs the same set.

Three defects, all introduced by the v0.9 split rather than pre-existing:

  • Opening an assessment did not change its project. load() copied the enterprise, date, kind and label from the library row onto meta but not the project, so the top-bar chip kept whatever the previously open assessment had — the kind of quiet wrongness that only shows up when someone works across two projects in one sitting.
  • Batch rows became orphans. "Open in the builder" created an assessment with no enterprise, so it vanished from the Assessments screen the moment you navigated away, and it invented a "Batch upload" project that existed in no project list. It now creates a real enterprise (named and placed from the batch row), attaches the assessment to it with the right year, and registers the project once so the whole batch shows up as a group.
  • Seven "a assessment"s left by the blind rename, and a glossary entry that was wrong on substance rather than grammar: it said a project holds assessments, when after v0.9 a project holds enterprises and an enterprise holds its assessments.

The lesson for the second one is worth keeping: a structural change is not finished when the screens it targeted work. Everything that creates one of the moved objects has to be found — there were three such places, and the batch was the one nobody would have opened during a demo.

Two more things the audit turned up while checking the projects work, both also rename fallout: styles.css still targeted .topbar-scenario after the markup and the JavaScript had moved to .topbar-assessment, so the top-bar chips had silently lost their layout rules; and the two "new project name" inputs shared one accessible name, which a screen-reader user could not tell apart. A class audit — every class referenced in the code against every class defined in the stylesheet — now comes back clean apart from two deliberate cases.

Project documentation (2026-09-30)

The repository had no agent-facing or contributor-facing documentation, so three were written:

  • CLAUDE.md at the repository root: what each document is, the four non-negotiable rules (no funding content in the tool or site, no issues filed on the upstream repositories, never commit from the stale site/ clone, read before overwriting), how to ship a mockup change end to end, the version-bump discipline, how to verify in the browser, the house style, and the four traps this repository has already fallen into.
  • ui_ux/DECISIONS.md: thirteen decisions with their reasoning — herd not farm, assessment not scenario, the three levels, why the results are deliberately a visible sketch, the four provenance states, blank as unknown, manure at herd level and why the head-weighted merge is compulsory rather than convenient, asking what people know, computed numbering, comparison before absolutes, two-level navigation, no funding content, and stating every limitation where it bites. Several of these reverse a choice that looks obvious, which is exactly why they are written down.
  • ui_ux/mockup/README.md brought up to date: it still described a flat list of six scenarios and the old screen set.

v0.9.2 — the feedback store now states where it is saving, and proves it

Why: reading the published artifact's database found it completely empty — no comments, no ratings — after two saves had been made in it. The page had been telling the truth all along, in a toast that disappears after a few seconds and a banner with a Dismiss button. For the one mechanism the whole mockup exists to serve, that is not good enough: a facilitator can run a session believing it is being recorded and find out afterwards that it was not.

What changed:

  • A permanent storage card at the top of the Feedback dashboard. A chip reading shared store or this device only, a sentence saying what that means for the session ("Comments are staying on this device only. Export before you close the tab."), and the facts underneath: which adapter, whether the runtime offered a shared database, whether this viewer may write to it, why it fell back if it did, the last error verbatim, and the viewer id.
  • A "Test the store now" button that writes a throwaway document, reads it back, deletes it, and reports what happened — including the distinction that matters: wrote and read back, but that is local storage, not the shared store. It is meant to be run before a session starts.
  • Export buttons moved onto that card, next to the verdict, because that is where someone reads that they will need them.
  • Every write now records its outcome (ok, the error code, the time) instead of only logging to a console nobody has open.

A bug the self-test found immediately: both local adapters pre-created an event emitter for each known collection and threw Cannot read properties of undefined (reading 'emit') on any other — so the self-test could not run against them, and any future collection would have failed the same way. Collections are now created on demand. An adapter that cannot be tested is an adapter that should not be trusted.

Still unproven: whether the shared store works inside the published artifact. The local build always reports "outside the artifact runtime", correctly, because it is. That verdict can only be read in the artifact itself.

Where the two deployments store feedback (and why the artifact's database was empty)

The empty database was not a bug. There are two deployments and they cannot share a store:

Shared store What a facilitator must do
GitHub Pages (peetmate.github.io/cleaned-review/ui_ux/mockup/) None — a static page has no backend Export at the end of every session, on every device
claude.ai artifact (private link) Yes, if the runtime grants the page a database and the viewer may write Nothing, if the storage card says "shared store"

The two comments that prompted this were made on the Pages copy, which reports "outside the artifact runtime" exactly as it should. Whether the artifact's shared store actually works still has to be read off the storage card in the artifact.

v0.9.3 — filing feedback to GitHub without a token

The limit first. A static page cannot submit to GitHub on its own. Anything shipped to the browser is public, so a token with permission to open issues would also let any visitor close them and read private repositories. There is therefore no silent submit, and there should not be one. Two routes that need no secret:

  • Per item, one click. Every comment on the dashboard has File as issue ↗, which opens GitHub's new-issue form with the title, the body and the labels already filled in — screen, numbered element, field id, kind, severity, group, reporter, session, build and viewport as a table, plus a hidden fb-id marker. The person presses Create. Typical link is ~500–700 characters; if a comment is long enough to threaten the URL limit the link degrades to the text plus a pointer to the export rather than truncating silently.
  • In bulk, as the facilitator. tools/file_feedback_issues.mjs takes the JSON export and files every comment through the gh CLI, so it authenticates as whoever runs it and no credential is ever published. It is idempotent — each body carries <!-- fb-id: … --> and the script searches for that before creating, so re-running after a partial failure files only what is missing. --dry-run prints what it would do, --min-severity 2 files only the serious ones, --repo points it elsewhere. It creates the workshop-feedback label if absent. Screengrabs cannot be attached by the CLI, so they are written out as image files next to the export and each issue names the file to drag in.
  • Export as issues (Markdown) produces one file with every comment already written as an issue, for reading, pasting by hand, or attaching to a report.

Issues are filed to peetmate/cleaned-review, the development repository, not to the upstream CIAT repositories — per the rule in CLAUDE.md. Changing that is a one-flag change when the team decides.

What was rejected: a serverless proxy holding the token (real zero-click submission, but it needs infrastructure to run and maintain, and it lets anyone with the link file issues under the project's name); and a third-party form service (the data leaves, which is exactly what the workshop's consent statement says will not happen).