Downstream — decide with the version of you who has to live with it
Status: v1 in build (2026-07-15). The user's north-star "holy grail" app. Internal model name: stress curves. Product name: Downstream.
Every task app treats a task as a point — a due date. Downstream treats it as a function: how the cost and the stress of a deferred thing change over time. The reminder stops being "this is due" and becomes "you're about to hit the knee of the curve — act now, before it gets expensive."
Downstream is the maintenance half of the self. DreamPro / Workout Window is the aspiration engine (become who you want to be); Downstream is the upkeep engine (keep your life from quietly falling apart). Together they're the whole picture — growth and maintenance — and upkeep is the half almost every self-improvement product ignores.
The thesis (why it's named Downstream)
The core discipline the product teaches: don't let the version of you that exists right now — dreading something, or comfortably ignoring it — make the call. Look at what flows downstream, to the version of you who has to live with it. The name is the thesis. It also holds a second, on-brand reading: once you act, it's all downstream from here — the ease you earn. The name carries both the discipline and the payoff.
Present emotion is a terrible predictor of downstream cost, and that error runs in both directions — which is the product's central intelligence (below).
It's two curves, not one — and the gap between them is the whole product
Every deferred thing has two separate curves:
- Cost — the objective consequence over time (money, damage, a lapsed relationship, a legal problem).
- Stress — your subjective dread over time.
They rarely match, and the mismatch is the diagnosis:
| Low cost | High cost | |
|---|---|---|
| High stress | The dreaded 4-minute phone call → reframe / just do it (this is our existing reframe engine, pointed at a task) | Do it now |
| Low stress | Genuinely fine → permission to ignore | The slow roof leak, the check-engine light → warn (the one place a scary forecast is earned) |
Nobody has built the "you're more stressed about this than it deserves / you're not stressed enough about that" mirror. That mirror is "look downstream."
The soul is relief, not dread — a triage engine, not a nag
The failure mode is obvious: a wall of curves all climbing toward pain = an anxiety machine. We invert it. The hero output is a don't-do list with a clear conscience: "you can safely let 11 of your 14 open things drift today — here's the 1 that's about to spike." Productivity culture screams do everything now; Downstream can, with evidence, grant permission to defer the flat-curve stuff.
The emotional promise is a worry-offloader: dump the nagging background maintenance into it and it holds the anxiety for you, surfacing things only when they matter. "Put it down; Downstream will tell you when." This plugs into the JQ-as-second-brain thread.
Brand bright line (non-negotiable): Downstream is never a guilt machine. The scary forecast is used sparingly and only for the low-stress/high-cost trap. Everything else leads with relief and agency. (Consistent with the "no consumption/nagging metaphors — transformation, not pressure" brand voice.)
The curve model (the engine)
Deferred things fall into a few archetypes; naming them is most of the model.
Each archetype is a function pressure(elapsed | timeToDeadline, params) → 0..1,
plus a phase label and a plain-language downstream forecast.
flat— pressure ~constant and low. The permission-to-defer items. (Most of a typical list.)ramp— deadline-anchored. Cost is roughly fixed, but stress rises as the date nears (taxes, an RSVP, a birthday gift). Params:deadline,leadDays(how far out it starts climbing).decay— no hard deadline, but the real cost grows, usually accelerating, the longer you wait (oil change → engine wear; a cavity → root canal). Modeled as a logistic with the knee atkneeDays. This is the "maintenance debt" case and the heart of the product.step— cost jumps at discrete thresholds (late fee at day 30, a lapsed license). Params:steps: [{ atDays, level }].compounding— interest-like growth (debt, clutter, the inbox). Params:ratePerDay.
phase ∈ calm | warming | knee | urgent | overdue, derived from pressure (and
past-deadline for ramp). The knee — where marginal cost starts
accelerating — is the actionable moment, and it is not the due date. Finding
the knee is the product's whole value.
Curves are inferred, never entered
No one will hand-author a logistic. The design rule: the user just names the thing ("change the furnace filter", "renew the passport") and the curve is proposed from three sources —
- an LLM prior by task category (v1),
- the calendar (seasonal shapes: taxes, tires, gutters),
- eventually your own history.
A starter library of ~30 common maintenance tasks with default curves does the cold-start. The user only ever nudges a curve; they never draw one.
The calibration loop is the open-science dataset
When you finally resolve a thing, one tap: "how bad was it actually — easier / as expected / worse?" That single signal does triple duty:
- refines your personal curve steepness,
- feeds the normative crowd curve (the citizen-science data point),
- builds the public artifact — "the shape of human procrastination cost."
The two-layer "open science" (species-level normative curve + personal overlay) rides the existing Citizen Science infra (crowd data, world map, contribute-and-refine). The most valuable thing the crowd data can reveal: which deferrals are actually harmless — so the platform can hand out guilt-free permission with evidence behind it.
Snooze-with-consequences: deferring doesn't just re-alarm tomorrow — it shows the curve cost of the snooze ("+$40 and +2 dread if you push this a week"). Honest, not manipulative.
The metaphor system (the product's language)
Downstream lives in fluid dynamics — warm, on-brand for HiveJournal's science DNA (Graphene, Emberkiln, allotrope) — with a few borrowed mechanical terms for the starting problem:
- Downstream — the future consequence you inspect before deciding.
- Current — the natural momentum of your week.
- Eddies — things stuck circling, going nowhere (a stalled to-do).
- Downstream cost — what flows to you later if you don't clear it now.
- Activation energy (borrowed) — the push needed to start; JQ's nudge supplies it at the right moment.
- Static friction (borrowed) — why starting is the hard part; once moving, it's easier.
Talk to humans in this language, not "the knee of the logistic."
v1 scope (what's being built now)
The core loop, delivering the soul:
- Capture — type a thing you're putting off → JQ infers its archetype + params + the cost/stress split + a downstream forecast (the starter library seeds common ones).
- Triage — your things sorted by where they are on their curve, led by the relief headline ("let these drift; do this one") and the cost-vs-stress mirror.
- Detail — a per-item pressure gauge + "what's downstream if you wait" + resolve / snooze-with-consequences.
- Calibrate — on resolve, the one-tap "how bad was it actually" that refines the curve.
Phasing beyond v1
- Curve-timed nudges — ✅ BUILT 2026-07-15. A 6-hourly cron
(
downstream-nudge.ts) finds each user's most-pressing item that has crossed the knee (phase knee/urgent/overdue), composes a short calm line (templated — no per-tick LLM), renders it in the user's own cloned voice when they have one (sayInUserVoice, cache-first) else text-only, and records it. Calm by design: ≤1 nudge/user/tick, ≥3 days between nudges for the same item. OFF by default (kill switchdownstream_nudge, heartbeatdownstream_nudge_tick). Surfaced as a banner on/dashboard/downstream(with the voiced audio) and viaGET /api/downstream/nudges. Thedownstream_nudgestable (migration 482) doubles as the traceability log — a reviewable record of everything Downstream ever said to you (the seed of the glasses "what did it tell me" feature), surfaced as a "what Downstream has told you" history on the dashboard.- Quiet hours — ✅ BUILT. The browser timezone is captured into
downstream_prefs(migration 483) on dashboard load, and the tick never records a nudge during the user's night (default 21:00–08:00 local, per-user configurable).GET/POST /api/downstream/prefs. - Web push — ✅ BUILT. A service worker (
public/sw.js)- a "Turn on nudges" button on the dashboard subscribe the browser; the nudge
tick sends a web push (
downstream-push.ts) so a nudge lands even when the app is closed. Migration 484 (downstream_push_subs). Requires VAPID env keys (VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY/VAPID_SUBJECTon the backend,NEXT_PUBLIC_VAPID_PUBLIC_KEYon the frontend) — degrades to no-op (in-app banner still works) until set.
- a "Turn on nudges" button on the dashboard subscribe the browser; the nudge
tick sends a web push (
- Still to do: glasses delivery (the glasses app polls the
/nudgesendpoint — a glasses-app change, backend already ready).
- Quiet hours — ✅ BUILT. The browser timezone is captured into
- Built-in Pomodoro (focus sessions) — ✅ v0 BUILT 2026-07-15. Turns triage's
"do this one" into a bounded work/break session (classic 25/5, long break every
4 rounds). At each boundary it speaks a calm cue in your own voice (reusing the
nudge/voice/push plumbing) and logs it to the traceability log; finishing a
work phase on a pinned item eases that item's curve (
applyFocusProgress→focusEasedAnchor, elapsed-based archetypes only), closing the triage → act → recalibrate loop.downstream-focus.ts(pure core tested 16/16), migration 485 (downstream_focus_sessions), a dashboard focus widget + per-item Focus ▶ button. The client timer drives boundaries while the app is open; app-closed delivery is thedownstream_focuscron (1-min, OFF by default) which advances due sessions so the in-voice + push cue lands with the tab closed — kept safe against the client timer by a compare-and-set (no double cue), and self-terminating after the long break. Still to do: wiring the rhythm engine (below) into the cadence + in-ear soundtracks — see DOWNSTREAM_AUDIO_IDEAS.md. - Ultradian-rhythm engine (v0) — ✅ BUILT 2026-07-15. The timing layer: estimates
where you are on your ~90-min BRAC energy wave from time-since-wake (no
hardware) and surfaces a phase-aware suggestion (peak → take the hard item;
trough → take the break).
downstream-rhythm.ts(pure, tested 9/9),GET /api/downstream/rhythm, migration 486 (wake time + cycle ondownstream_prefs), a dashboard rhythm strip labeled a rough estimate. It now drives the Pomodoro cadence —rhythmTunedDurationstunes a session's block/break lengths to the current phase (peak → longer block; trough → shorter block + longer break), via a "⚡ Match focus blocks to my energy" toggle (on by default). v1 calibration is built —calibratePhaseblends recent signal into the estimate (recency×weight-weighted): self-report Low/Steady/High taps (downstream_energy_taps, migration 487) + behavioral signal from completed focus sessions (focusEnergyTaps), flipping the read tobehavioralwhen either's in play. Open-science overlay is built —aggregateEnergyCurvepools the taps into a personal + anonymized-normative average-energy-by-hour curve (GET /api/downstream/rhythm/curve, ≥5/hour anonymity floor), shown as a "shape of your day" overlay (you vs everyone). Interaction-cadence signal is built —interactionEnergyfolds live app-usage cadence (?epm=) in as a weak, ephemeral (never-stored) nudge. The normative curve now feeds back into the estimate — ✅ BUILT:applyNormativePriorfolds the crowd's average energy at the current local hour (cached ~30 min; ≥5-sample anonymity floor) into the base as a WEAK prior (0.2), applied beforecalibratePhaseso any personal signal (up to 0.7) still dominates; when no personal signal follows, the read issource:'normative'("· crowd-informed" on the dashboard). Still to do: the (unbuilt) adaptive soundtracks; v2 opt-in watch HR/HRV. - Normative / open-science curves — aggregate the calibration data into
per-category crowd curves; personal-vs-normative overlay.
- "Shape of your procrastination" — ✅ BUILT. The dread-vs-reality mirror,
broken out per category with a you-vs-everyone overlay. Pure core
(
downstream-calibration-core.ts:shapeCategoryCalibration+aggregateNormativeCategories(privacy floor — a category's crowd rate shows only pastNORMATIVE_MIN_ROWS=8 resolutions ANDNORMATIVE_MIN_USERS=3 distinct users) +compareCalibration, tested 11/11).GET /api/downstream/calibration/categories→ per-category{personal, normative, delta}; the dashboard renders a bar per category (your easier-rate) with the crowd average as a ▏marker, and calls out the category you over-dread most vs the crowd. No new migration — reads the existingresolution_feedbackrows. - Public "shape of human procrastination" — ✅ BUILT. A public, no-auth page
at
/downstream/shape: the anonymized normative dread-vs-reality across everyone — an overall "N% turned out easier than feared" + per-category bars, framed as open science.GET /api/downstream/public/shape(getPublicProcrastinationShape, reuses the floor-gatedaggregateNormativeCategories, cached ~1h + CDN cache headers). Linked from the/downstreamlanding.
- "Shape of your procrastination" — ✅ BUILT. The dread-vs-reality mirror,
broken out per category with a you-vs-everyone overlay. Pure core
(
- Household / shared curves — maintenance is often shared (who changes the furnace filter); ties to JQ Bridge / family.
- Downstream Glasses — the ambient layer: your curves surface in-ear at the knee, hands-free (see the glasses buildables). (Brand rename JQ Glasses → Downstream Glasses tracked as its own task.)
Related
- DreamPro Coaching System — the aspiration half; Downstream is the maintenance half.
- The Citizen Science platform — the open-science substrate for normative curves.
- The JQ reframe engine — pointed at high-stress/low-cost tasks.
- MOBILE_JQ_PRESENCE.md / the glasses docs — curve-timed nudges + the ambient "Downstream Glasses" layer.