Inspiration

Maya's therapist knows the evidence-based next step for her depression is to schedule things she enjoys. But six months flat, Maya can't name one thing she'd enjoy this week. So the session goes to a worksheet that says "go for a walk."

That step is activity scheduling, the core of behavioral activation (BA), a first-line, low-cost treatment for depression. In the COBRA trial (Lancet 2016), BA delivered by junior mental-health workers did as well as CBT — so the people who would use a tool like this are often not specialists. The step breaks in three places: anhedonic patients can't generate options, generic options aren't personally rewarding, and nobody checks that a suggested place is open at the planned hour, close enough for a low-energy day, within budget, quiet enough, or alcohol-free for a patient in recovery.

We wanted the planning step to start from what the patient actually loved, and to be true about the real world.

What it does

Try it (no login): https://rekindle.edycu.dev — click Plan Maya's week, live (about 20 s; the agent trace shows each Qloo call as it happens). Then click In recovery: in about 2 s the places that serve alcohol are struck through with their rule (R-ALC) and replaced by verified alcohol-free ones. Approve the week, rate three activities (a food outing 8, the at-home song 7, one other 2 shows the ember thread most often), and click Re-plan week 2; the Patient sheet prints only what you approved. Or start a fictional case with your own loves (New fictional case). Every case is fictional. Receipts: https://rekindle.edycu.dev/judge · every live Qloo call: https://rekindle.edycu.dev/integrations/qloo · the story page: https://rekindle.edycu.dev/about · the deck: https://rekindle.edycu.dev/pitch · 2:29 video: https://youtu.be/a2axsKACYsk

  1. Intake. The clinician enters 3–6 things the patient loved before the illness (an artist, a show, a food). Each is resolved live to a Qloo entity or tag. The clinician sets a neighbourhood point, an energy level and constraints: in recovery, quiet, wheelchair entrance, distance, budget.
  2. A graded week, built by an agent. An LLM tool loop works up an energy ladder: 10-minute at-home doses (an artist, podcast, book or show), then solo outings, then one social outing placed in the top-affinity taste cell from a Qloo heatmap. The model chooses where to look. Qloo and deterministic code decide what is true — the model never names a venue.
  3. Every candidate is verified. Deterministic rules check what Qloo returned: open at the slot, near, within budget, an activity category, alcohol-free in recovery, quiet or accessible when required, not rated low before. Refused venues stay on the sheet, struck through, with their rule; ambiguous ones go to the clinician in a REVIEW box.
  4. The clinician gate. Approve, swap (re-runs that one slot live) or remove each card, then approve the week. Nothing reaches the patient without approval.
  5. Ratings → re-plan. Pleasure and mastery are rated 0–10. A rating ≥7 becomes a Qloo signal for week 2; ≤3 becomes an exclusion. An ember thread links a rated activity to a week-2 card where Qloo's explainability names it as the leading input — Qloo's attribution, not causation.

What makes it Qloo-powered: without Qloo, the plan is an LLM naming venues it cannot verify. Our benchmark measures exactly that:

  • ⟳ The number: asked for named places, the same LLM got 7 of 17 audited venues wrong (41%: 4 not found, 2 closed at the planned hour, 1 serving alcohol to a patient in recovery); Rekindle got 0 of 14 (a random 20 per arm, checked one by one against public listings by our build agent (Claude, an AI; not blind); bench/audit.json). We lead with this manual audit because our automated checker leans on Qloo, which flatters Rekindle, and over-flags the LLM: across all 20 profiles it flags 58 of 79 vs 6 of 66, but 4 of the 10 venues it couldn't match in the audit sample were real and open.
  • ⟳ Ablation (10 in-recovery profiles): with the screen on, 0 of 30 outing cards carry an alcohol tag or keyword. Screen off, Qloo's 14-tag exclusion kept: 3 of 28. Screen off, only the 4 obvious bar tags excluded: 21 of 28 (bench/results.json, ablation_in_recovery).

Rekindle is clinician-support only. It does not diagnose, makes no efficacy claims, and sends Qloo only taste entities, tags and a map point — never a name, diagnosis, note or rating. Three care needs travel only as place filters: a case in recovery adds 14 alcohol-tag exclusions, a quiet need adds a quiet tag, and a wheelchair need adds a wheelchair-entrance tag.

How we built it

Next.js 15 (App Router, TypeScript, React 19) on Vercel; week plans stream to the browser as Server-Sent Events. The agent is a Vercel AI SDK generateText loop with typed zod tools (findAtHome, findOutings, placeSocial); the router is Qwen qwen3.8-flash, with Groq openai/gpt-oss-120b taking over on an error or timeout, and both racing for the one-line coaching notes. Constraints never come from the model: radius, price, recovery, quiet and wheelchair are applied from the stored case on every request. A pure-TypeScript verifier (rules R-ID … R-HIST, no I/O) decides every card. Storage is Upstash Redis on its free tier (zod-validated JSON per case and plan, atomic counters), with a safe mode that keeps planning live if storage refuses.

The Qloo surface the plan loop depends on: /v2/insights place queries with signal.interests.entities / .tags, filter.location (WKT POINT) + filter.location.radius, filter.hours, filter.price_level.max, filter.tags, filter.exclude.tags, filter.exclude.entities and feature.explainability; urn:heatmap with output.heatmap.boundary=urn:geohash → filter.location.geohash for the social outing; four at-home entity types; /entities to re-check, live, the activity a re-plan keeps; /search and /v2/tags at intake.

⟳ Rigor: 230 Vitest tests (19 files) and 60,000 generated verifier cases, 94.6% line coverage, keyless Playwright E2E in CI, and an 18-gate smoke script against the live deploy. Every number in the README and on /judge is generated from a committed file, and a docs check fails the build if they drift.

Challenges we ran into

  • Qloo's tag exclusion alone isn't enough for recovery. With the four obvious bar tags excluded, 17 of Maya's 25 nearby candidates still carried alcohol tags; even with 14 alcohol tags excluded, her query returned a coffee shop tagged for specialty alcoholic drinks and a butcher with a craft-beer selection (live probe, 2026-10-07; DX-REPORT §2). So Rekindle runs a deterministic screen on top — and the ablation above measures what each layer does.
  • A substring screen would have been wrong: it refuses espresso_bar and independent_publishing ("pub"). R-ALC matches exact tag IDs and whole tokens, with an allow list.
  • Opening hours: filter.hours is day-level (Maya's filter.hours=thursday query returned 25 places, 12 of them closed at 18:00 by their own hours), and some places have no hours at all (3 of 20 in a /search probe; /entities filled 0 of 4). We check the slot in code and refuse unknown hours instead of assuming open.
  • Explainability covers entity signals only. Maya's third love, ramen, shapes the ranking but gets no weight, so the because-line names only her entity loves (DX-REPORT §7).
  • Free tiers. We moved storage off Vercel Blob to Upstash Redis mid-build when the Blob operation budget became the app's availability budget, and added a safe mode and per-IP limits so a quota can't take the judged app down.

Accomplishments that we're proud of

  • The In recovery moment: one click, for a clinical reason, and real venues are struck in place with their rule and replaced live in about 2 seconds.
  • A truth boundary that holds: no card exists without a Qloo entity ID that passed every rule, and the model's coaching lines are screened (no venue names, distances, prices or drinking language).
  • An honest benchmark that reports its own grader's bias, a manual audit labelled as not blind, an ablation, and a "What we got wrong" section in the README.
  • A Qloo DX report with 8 reproducible findings, each with a repro command (DX-REPORT.md in the repo).

What we learned

Taste graphs are good at what someone might enjoy; the hard part of a clinical-adjacent tool is everything that must be true around it — hours, distance, alcohol, accessibility. Letting the model choose where to look, and letting Qloo plus deterministic code decide what is true, made the system both creative and checkable. And we learned to lead with the measurement that is least flattering to us.

What's next for Rekindle

A practising clinician's review of the flow; free-text address geocoding at intake (seeded neighbourhood points only today); mastery-driven changes to the energy ladder (mastery is recorded but does not change the plan yet); multi-clinician use; and an external blind audit from the standalone handout (scripts/manual_audit.py handout).

Code: MISSING: public repo URL — fill at the D19 public flip (https://github.com/edycutjong/rekindle, MIT)

Thank you for your time reviewing this project. I built Rekindle because the hardest part of getting better is often the first small thing you can actually look forward to, and I wanted that thing to be real, open and close by.

Built With

Share this project:

Updates

Submission history