Research Roadmap — persona-driven, evidence-backed backlog
What this is. A triaged backlog distilled from the synthetic persona panel in
USER-RESEARCH.md, cross-referenced against published evidence (access date 2026-06-30). It complements — does not replace —docs/ROADMAP.md, which carries Sprout's phased build plan, metrics ledger, and conformance declarations. Where this file echoes a ROADMAP phase or an ADR, the item is tagged [corroborates …] (independent triangulation). Where it surfaces something the existing docs don't cover, it is tagged [NET-NEW].Status context. Sprout is
In build(Phase 3). The engine (pipeline, five eval suites, guards, fail-closed loader, providers) exists. The synthetic corpus, 120+ cases, baseline scoreboard, model card, ACR, red-team report, and deployed UI are now committed; remaining gaps are tracked indocs/ROADMAP.md. Several items below are retained as the research record that drove that work.Synthetic-data caveat. Personas are synthetic; this backlog is a hypothesis set to validate, not validated demand. See the warning in
USER-RESEARCH.md.
Research basis / evidence (real sources, access date 2026-06-30)
| # | Evidence | Source(s) | Drives |
|---|---|---|---|
| EV1 | AI plant answers hallucinate, fail to cite, and amplify SEO-popularity-ranked myths; AI-generated foraging guides exist and misID is framed "life or death" | Buncombe MG · UF/IFAS · AI slop (Wikipedia) · OpenTools | R1, R3, E5 |
| EV2 | Lilies → acute kidney injury in cats; all parts toxic; "often fatal if treatment delayed >18h"; call APCC immediately | ASPCA — Which Lilies · ASPCA Poison Control | R7, E2, E9 |
| EV3 | "Non-toxic" ≠ safe: any plant material may cause vomiting/GI upset; individual pets vary | ASPCA — Toxic & Non-Toxic Plants | R7, E1 |
| EV4 | Independent corroboration of houseplant toxicity + hotlines (Pet Poison Helpline 855-764-7661) + extension SME source | Pet Poison Helpline · NIH PMC10220692 · UF/IFAS Hort-14 PDF | R7, E5, E9 |
| EV5 | Decontamination is time-critical; early professional consult improves outcome | Vet Clinics: Small Animal Practice | E2, E9 |
| EV6 | RAG faithfulness/groundedness + citation precision/recall are an active, contested benchmark area (Ragas/DeepEval/TruLens/attribution) | Atlan · DeepEval RAG triad · arXiv 2505.04847 · RAGBench 2407.11005 | E3, E7 |
| EV7 | Under an abstention policy, LLM-judge groundedness saturates near 1.0; frameworks disagree → measure, don't assume | Abstention-policy benchmark | E3, E4 |
| EV8 | Calibration (ECE) + selective prediction / coverage-risk are the formal tools for "knowing what you don't know" | NeurIPS 2025 selective prediction · Knowledge-Boundary survey 2412.12472 | E4 |
| EV9 | Free photo-ID apps vary widely (PlantNet ~86.5% vs iNaturalist ~65.6% Top-1; genus ≫ species; varies by family; confident IDs reliable but infrequent) | Hart et al. 2023 · Rzanny et al. 2024, People and Nature · iNaturalist data-quality, PMC10703310 | R6, R8, E8 |
| EV10 | LEP / Spanish-first information disparities; MT can be unsafe (a health dept MT said a vaccine was "not necessary"); low-resource MT lags | JAMIA Open · PMC8568518 · PMC6238892 · PMC9205365 | R4, E6 |
Remediation backlog (close gaps in what exists or is specced)
Priority: P0 now · P1 next · P2 soon. Effort: S ≈ an afternoon · M ≈ a day or two · L ≈ a week+.
| ID | Remediation | Personas | Pri | Effort | Evidence / tag |
|---|---|---|---|---|---|
| R1 | Author & commit the synthetic CC0 corpus + dated manifest, prioritizing (a) the top beginner houseplants and (b) the highest-call-volume toxic plants (lilies, sago palm, pothos, philodendron, azalea, tulip) so the safety suite has real toxicity refs to cite | A1,A2,A4,B1,B2 | P0 | L | EV1,EV2,EV4 · [corroborates ROADMAP Phase 1] |
| R2 | Author the 120+ YAML cases + commit the baseline scoreboard (mediocre numbers shown, not hidden); wire the CI smoke suite of corpus-derived questions | C1,C2,E1 | P0 | L | EV6 · [corroborates ROADMAP Phase 2] |
| R3 | Commit the model card + data card stating limits plainly: corpus coverage gaps, extractive-but-context-incomplete risk, and "non-toxic ≠ safe" | E1,B1,C2 | P0 | M | EV1,EV3 · [corroborates ROADMAP Phase 3] |
| R4 | Deploy the accessible UI behind a real URL with the reference-implementation banner and commit the ACR (VPAT 2.5 Rev 508) | D1,A4,A5(→D1) | P1 | M | EV10 · [corroborates ROADMAP Phase 3] · [DONE] |
| R5 | Commit the judge-calibration probe set + κ and the OWASP-LLM red-team report | C1,C2 | P1 | M | EV6,EV7 · [corroborates ROADMAP Phase 2/3] |
| R6 | Reconcile the photo-ID status drift: remove/replace the "Not a plant-ID-from-photo tool" non-goal in RESPONSIBLE-TECH-AUDITS.md §A, and add a photo-ID + reminders privacy/DPIA row (Pl@ntNet egress, image-not-retained, var/reminders.json local state) to §C |
E1,A3 | P1 | S | EV9 · [NET-NEW] (doc-consistency gap; complements ADR-0010/ADR-0011) |
| R7 | Add an explicit "non-toxic ≠ safe" disclosure for low-toxicity plants (mild-GI-upset cases) so the never-assert-safe rule covers the subtle case, EN+ES; add the phrase to the safety deny-list review | A2,B1,B2 | P1 | S | EV2,EV3 · [NET-NEW] |
| R8 | Surface photo-ID uncertainty in the UI: show the visual-match label + the abstain-on-low-confidence path, screen-reader-announced; emit a "species not in corpus → fallback" message | A3,D1 | P1 | S | EV9 · [corroborates ADR-0010] (extend) |
| R9 | Clarify the reminder boundary in-product: household reminders belong in Family Greenhouse; retain the local CLI/API contract without promoting it in Sprout's public web surface | E1,D1 | P2 | S | — · [corroborates ADR-0011; resolved by ADR-0015] |
| R10 | Make the eval job a required CI status check with per-item "why it failed" traces surfaced in the report | C1,E1 | P2 | S | EV6 · [corroborates ROADMAP Phase 2] |
Expansion backlog (new capability)
| ID | Expansion | Personas | Pri | Effort | Evidence / tag |
|---|---|---|---|---|---|
| E1 | Toxicity-coverage eval slice + report panel: assert the corpus cites a toxicity reference for every ASPCA top-N pet-toxic plant, and that "is X safe for my cat" always routes and never certifies | B1,A2,C1 | P1 | M | EV2,EV3 · [NET-NEW] · [DONE] |
| E2 | Urgency-forward routing for ingestion ("my cat ate a lily petal" → call-now framing + the >18h fatality window, without giving veterinary advice), localized EN/ES | B1,A2 | P1 | S | EV2,EV5 · [NET-NEW] |
| E9 | Standardized vet/poison-control escalation card on every toxicity answer/refusal: ASPCA APCC (888-426-4435) + Pet Poison Helpline (855-764-7661) + "what to tell them: plant, amount, time", accessible + localized | B1,A2,D1 | P1 | S | EV4,EV5 · [NET-NEW] |
| E3 | External-suite comparison/ablation: benchmark Sprout's grounding-by-construction + deterministic safety against ≥1 of Ragas/DeepEval/ALCE-style citation precision/recall; publish the (saturation-aware) result | C2 | P2 | L | EV6,EV7 · [NET-NEW] |
| E4 | Coverage-vs-risk (selective-prediction) curve in the calibration report, beyond ECE: publish the coverage/risk tradeoff at the abstain threshold | C2,C1 | P2 | M | EV8 · [NET-NEW] |
| E5 | SME corpus-contribution workflow: a low-/no-code "propose a cited passage + an eval case" path with provenance fields (source, license, fetch_date, lang, topic) enforced and a representational-harm checklist | B2,E1 | P2 | L | EV1,EV4 · [NET-NEW] · [shipped: sprout propose] |
| E6 | Language expansion beyond EN/ES (the i18n seam is one module) — gated on parity: each new language must clear the multilingual suite, and ES/new-language copy is human-reviewed, never raw MT | A4,B2 | P2 | L | EV10 · partially [corroborates I18N standard] / [NET-NEW] for >2 langs |
| E7 | Citation freshness / link-liveness check: flag when a cited source's fetch_date is stale or the URL no longer supports the claim — especially toxicity refs that get revised |
C1,B1,E1 | P2 | M | EV6 · [NET-NEW] · [shipped: sprout freshness] |
| E8 | Photo-ID "show your work": top-N candidate species with scores + an explicit corpus-coverage gate; never auto-act on the match | A3,A1 | P2 | M | EV9 · [corroborates ADR-0010] (extend) |
| E10 | Family Greenhouse personalization (A→B→C): toxicity cross-check against the user's actual pets/plants ("a plant in your Greenhouse is listed toxic to cats, and your profile notes a cat") — deferred, opt-in, household-data ASVS L2 | A1,A2,E1 | Later | L | EV2 · [corroborates ROADMAP deferred] |
| E11 | corpus.yaml generalization + "adapt this to your domain" guide so any cited care corpus can be swapped in |
E1,C2 | Later | M | — · [corroborates ROADMAP Phase 4] · [shipped: docs/ADAPT.md + the corpus.* config seam; proved end to end by examples/herb-garden-plugin/, a different care corpus run through the unmodified engine] |
Sequenced roadmap
Now (P0 — the content unlock; nothing the user touches is real until this lands). R1 (toxicity-prioritized corpus + manifest), R2 (120+ cases + baseline), R3 (model/data cards). These three convert "engine exists" into "a person can ask a real plant a real question and the gate means something." R1 is the bottleneck for A1, A2, A4, B1, and C1 at once.
Next (P1 — make safety loud and prove the claims). R7 + E9 + E2 (non-toxic≠safe disclosure, escalation card, urgency-forward routing) harden the keystone control with framing the research demands; R4 + R5 (deployed UI + ACR, judge-κ + red-team) turn three self-claims into artifacts; R6 + R8 reconcile the photo-ID docs and surface its uncertainty; E1 adds the toxicity-coverage slice.
Soon (P2 — depth and credibility). E3 + E4 (external comparison + coverage-risk curve) earn C2's trust; E5 (SME authoring) and E7 (citation freshness) attack the corpus bottleneck and keep safety current; E6 (gated language expansion); R9, R10 finish the honest-limits and CI polish.
Later (deferred, already in ROADMAP). E10 (Family Greenhouse personalization, ASVS
L2). E11 (corpus.yaml generalization) has since shipped — see its row above.
Recommended first sprint (highest-leverage, mostly "finish what's specced")
- R1 — commit the toxicity-prioritized synthetic corpus + dated manifest. The single unblock for half the panel (A1, A2, A4, B1) and the prerequisite for a meaningful eval. Lead with the highest-call-volume toxic plants (EV2/EV4).
- R2 — author the 120+ cases + commit the baseline scoreboard. Mediocre numbers shown, not hidden. Makes the green gate mean something for C1/E1.
- R7 + E9 — "non-toxic ≠ safe" disclosure + standardized escalation card. Cheap (S), highest safety leverage, directly research-backed (EV2/EV3/EV4/EV5); the never-certify-safe rule made loud, which B1 demands.
- R6 — reconcile the photo-ID non-goal + add the DPIA row. The panel's clearest NET-NEW catch: the docs contradict the shipped feature. Cheap (S); pure honesty.
- R3 — commit the model + data cards. States the limits (coverage, context- incompleteness, non-toxic≠safe) plainly, which every safety and research persona wants.
Bundle the afternoon-sized wins alongside: R8 (photo-ID uncertainty surfacing), E2 (urgency routing copy), R10 (eval as required check).
Traceability matrix (persona → findings)
| Persona | Remediations | Expansions |
|---|---|---|
| A1 New plant parent | R1, R3 | E8, E10 |
| A2 Pet-toxicity owner | R1, R7 | E1, E2, E9, E10 |
| A3 Photo-ID user | R6, R8 | E8 |
| A4 Spanish-first | R1, R4 | E6 |
| B1 Vet / poison-control | R1, R3, R7 | E1, E2, E9, E7 |
| B2 Extension SME | R1, R7 | E5, E6 |
| C1 Eval / QA engineer | R2, R5, R10 | E1, E4, E7 |
| C2 RAG-eval researcher | R2, R3, R5 | E3, E4, E11 |
| D1 A11y / SR user | R4, R8, R9 | E9 |
| E1 Owner / maintainer | R3, R6, R9, R10 | E5, E7, E10, E11 |
Validate with real users / risks
- Safety path requires expert sign-off, not synthetic consensus. R7/E2/E9 touch emergency guidance; before shipping copy that frames urgency or the >18h window, get a licensed veterinary toxicologist / poison-control clinician to review it. The synthetic B1 is a stand-in, not an authority. Risk if skipped: false reassurance or, conversely, implying triage Sprout can't do.
- Spanish parity needs a native horticulture reviewer. Per EV10, MT can be unsafe on exactly this content. Don't expand languages (E6) on automated parity scores alone; human-review the toxicity and routing strings.
- Coverage vs. abstention is a UX risk, not just a metric. EV7 shows abstaining systems flatter their own groundedness scores; A1 will walk if the corpus is so thin that everything refuses. Validate the answer/refuse ratio with real beginners (R1 sizing).
- Photo-ID confident-wrong is the residual harm even with selector-not-fact (EV9): test whether real users notice the "visual match, not a cited fact" label or anchor on the species name anyway.
- No demand signal. This panel cannot tell you how many real users exist or what a vet would require to trust the routing. Run ≥1 real interview per group before treating any P1+ item as committed.
Honest limits
This roadmap is derived from a synthetic panel. It is a structured way to find gaps and
sequence known work — not evidence of demand, willingness to pay, or clinical adequacy. It
over-weights the maintainer's mental model and the published literature, and under-weights
whatever real users would surprise us with. Most P0 items are "finish what
docs/ROADMAP.md already commits to," reframed by who is harmed when it's
missing; the genuinely NET-NEW contributions are the safety-framing items (R7, E1, E2,
E9), the photo-ID doc reconciliation (R6), the external-suite comparison (E3/E4),
and the SME authoring + citation-freshness loop (E5/E7) — all cheap-to-moderate, all
research-backed, none requiring a new architecture. Treat the rest as hypotheses for the
real interviews this exercise exists to design.
Implementation status — 2026-06-30 (working tree, uncommitted)
Shipped this pass: R6 doc-drift fix (photo-ID non-goal reconciled with ADR-0010) · R3 model + data cards · conservative safety framing (R7/E9 "non-toxic ≠ safe" + escalation to ASPCA/Pet-Poison-Helpline; never asserts safe). Verify: make verify green. Deferred: R1 toxicity corpus + R2 eval cases (need a veterinary-toxicologist / SME).
Implementation status — 2026-07-03
Shipped: E7 citation freshness / link-liveness check — sprout.freshness.check_freshness()
parses each manifest entry's fetch_date and flags stale citations (365d default, 180d for
toxicity-topic entries or titles/topics that mention toxicity), plus an opt-in, network-gated
check_liveness() that HEAD/GETs cited URLs (skipping the synthetic example.invalid host)
only when explicitly requested. Wired up as sprout freshness [--check-links]
(src/sprout/cli.py), config-over-code thresholds under corpus.freshness
(src/sprout/config.py), unit-tested offline in tests/test_freshness.py. Verify:
pytest tests/test_freshness.py -q, ruff check, mypy all green; sprout freshness
exits 0 against the bundled 2026-05-01 corpus.
R9 superseded and resolved by ADR-0015 (2026-07-16). The earlier implementation put a
local-only reminders panel in web/dist/index.html and documented its no-sync/no-push limits.
The product-boundary review found that even an honestly limited panel duplicated Family
Greenhouse's task domain. The public Sprout surface now carries no reminder UI or household
state; the tested local CLI/API contract remains available for compatibility. Verify with
uv run sprout a11y-check web/dist/index.html and
uv run pytest tests/test_server.py::test_shipped_ui_is_a_stateless_reference_surface -q.
Shipped: E1 toxicity-coverage eval slice (src/sprout/eval/suites/toxicity_coverage.py) — a deterministic, corpus-level suite asserting every ASPCA top-N pet-toxic plant in the corpus (aloe, dracaena, english-ivy, fiddle-leaf-fig, jade-plant, monstera, peace-lily, philodendron, pothos, rubber-plant, snake-plant, zz-plant) has an English document with a ## Toxicity section that mentions toxicity and routes to a vet and a poison-control line; auto-registers a toxicity-coverage report panel via the existing report.py suite-iteration. Complements the pre-existing safety suite, which already gates the live answer on never certifying "safe" and always routing. Verify: make verify green (lint, type, test with 95% coverage, eval, a11y all pass); docs/audits/eval-report.md shows toxicity-coverage — ✅ PASS, n=12.
Implementation status — 2026-07-09
Shipped: E4 coverage-vs-risk (selective-prediction) curve in the calibration report —
sprout.confidence.coverage_risk_curve() (ADR-0021) computes, at a fixed set of confidence
thresholds including the engine's own 0.25 abstain_threshold, what fraction of labeled
calibration cases would be answered (coverage) and what fraction of those would be wrong
(risk); the calibration eval suite (eval/suites/calibration.py) appends the curve to its
existing SuiteResult.segments table alongside the reliability bins, so report.py needed
no changes to render it. Report-only: the suite's own PASS/FAIL stays keyed to ECE and
abstention exactly as before, per ADR-0021's reasoning. Verify: pytest tests/test_confidence.py
-q, ruff check, mypy all green; sprout eval regenerates docs/audits/eval-report.md
with the new coverage≥… segment rows under calibration, and the baseline regression check
(diff_against_baseline) reports no issues — purely additive.
Tuning-scope justification (src/sprout/confidence.py is tunable surface per
docs/ROADMAP.md Phase 3): the curve is read directly against the committed calibration
failures it makes legible — calibration-003/004/006/008/009/013/014/015/016, all
overconfident-and-wrong cases in docs/audits/eval-baseline.json. Coverage-vs-risk at each
threshold is what tells a maintainer, mechanically, whether raising abstain_threshold above
one of these cases's stated confidence would have caught it, and at what coverage cost — the
question a future threshold change (a separate, still-gated PR) would need answered before
touching the constant. No threshold, weight, or scoring path changes here.
Implementation status — 2026-08-04
Shipped: E5 SME corpus-contribution workflow — sprout propose template emits a
fill-in-the-blanks proposal (one YAML file carrying the passage in every supported language,
its provenance, the eval case the passage must satisfy, and a representational-harm
checklist), a no-code
corpus-proposal issue form collects the parts
only a contributor can supply for a maintainer to transcribe into that same file (a deliberate
subset of the schema, not a field-for-field mirror of it),
and sprout propose check (src/sprout/propose.py) reviews a proposal offline and
deterministically: provenance/license allowlist, ISO dates, E7's citation-freshness SLA
applied against the topic the passage carries rather than the manifest field, so toxicity
prose is held to the stricter SLA even under the template's default topic: care
(stale is a warning, unusable an error), every-supported-language coverage with EN/ES
structural parity, the corpus's canonical topic taxonomy, EXP-12's chunk lint reused rather
than reimplemented, the shipped never-certify-"safe" guard run over every proposed sentence,
an EN/ES medicinal/edibility claim scan that cross-checks the harm checklist rather than
trusting it, and an eval case that must load as a DatasetItem, cite one of the proposed
documents, and assert only facts that appear verbatim in the passage.
The review lands on one of three statuses. changes-requested (any error) exits non-zero and
is merge-blocking via make propose-check / the propose-check step of the eval-a11y CI
job. Both run sprout propose check with no arguments, which is the gate proper: it
discovers every proposal-shaped file committed anywhere in the repository — so a contributor's
submission is covered, not only the committed example — and fails closed three ways. A proposal
filed outside the declared submission locations (proposals/, examples/corpus-proposal/) is
an error rather than a silent skip; a file that reads as a proposal but does not parse is a
failure rather than a skip; and discovering no proposals at all is a failure, because a gate
that reviews nothing is not a gate.
ready-for-expert-review — mechanically clean but carrying toxicity/ingestion prose with
no committed sign-off — exits 0 on purpose: this repo already states that safety copy
needs a licensed veterinary toxicologist and that Spanish copy needs a native horticulture
reviewer ("Validate with real users / risks", above), and a tool that cannot recruit either
one must record that gate rather than fake it. --require-expert-review turns it into a
failure for a maintainer about to merge. ready-to-merge requires an expert_review block
whose sign-off artifact is a committed Markdown document under docs/audits/ that names the
species, the reviewer, and the date they signed — contained in the repository, so the strongest
gate in the module cannot be discharged by pointing the field at any file that happens to exist.
Worked example and full rule table: examples/corpus-proposal/
(Chamaedorea elegans, EN + ES, zero findings, ready-for-expert-review). Nothing is written
to corpus/: a proposal is reviewed, never auto-applied. Verify with
uv run sprout propose check and uv run pytest tests/test_propose.py -q.