Impulse AI Docs
Intern dokumentasjon
Hopp til innhold

Momentum-beregning

Momentum er appens antifragile alternativ til streaks. Scoren (0–100) degraderer gradvis ved inaktivitet istedenfor å resette til null. Systemet belønner at du er her nå, ikke at du aldri tok en pause.

Beregningen lever i én delt modul:

apps/api/src/lib/momentum.ts ← kilde
├── routes/dashboard/home.ts (konsument — hjemskjerm)
└── queues/insights/insights-helpers.ts (konsument — regen-worker)

Hjemskjerm-endepunktet cacher resultatet via getOrSetCache(CacheKey.home(...)).

Aktivitetskilder

heading.anchorLabel

Momentum teller aktivitet fra alle 4 brukerhandlinger:

KildeTabellDato-feltHva den fanger
Impulserimpulsescreated_atImpulsfangst
Sjekk-inncheck_inscreated_at”Hva gjør du nå?”-refleksjoner
Sesjonersessionslast_activity_atSteg-aktivitet i frigjøring/manifestasjon
Notaternotescreated_atRefleksjoner og journalføring

Dette sikrer at all brukerengasjement gir momentum, ikke bare impulsfangst.

Beregningsformel

heading.anchorLabel
Score = Recent Activity (0-50) + Cycle Progress (0-30) + Consistency (0-20)

Komponent 1: Recent Activity (50%)

heading.anchorLabel

Aktivitet siste 7 dager, vektet mot nyere dager. Maks én telling per dag — volum gir ikke ekstra score.

For hver unike aktive dag siste 7 dager:
vekt = 1.0 - (dagerSiden × 0.1)
Dag 0 (i dag) = 1.0
Dag 1 (i går) = 0.9
Dag 2 = 0.8
...
Dag 6 = 0.4
sum = Σ(vekter for unike aktive dager)
maxScore = 4.9 (1.0 + 0.9 + ... + 0.4)
recentComponent = min(50, (sum / 4.9) × 50)

Egenskaper:

  • Aktiv hver dag i 7 dager = 50 poeng (maks)
  • Kun aktiv i dag = ~10 poeng
  • Ingen aktivitet siste 7 dager = 0 poeng
  • Flere aktiviteter samme dag teller ikke ekstra — forhindrer volum-gaming

Decay-rate: Uten ny aktivitet mister du ~0.1 vektpoeng per dag. Etter 7 dager inaktivitet = 0.

Komponent 2: Cycle Progress (30%)

heading.anchorLabel

Andel impulser som har blitt prosessert gjennom sesjoner, siste 30 dager.

completionRate = recentCompletedSessions / recentImpulses (siste 30 dager)
cycleComponent = min(30, completionRate × 30)
Sesjoner (30d)Impulser (30d)RatePoeng
1010100%30
51050%15
55010%3
0200%0

30-dagers vinduet sikrer at scoren reflekterer nåværende praksis, ikke historisk gjeld.

Komponent 3: Consistency (20%)

heading.anchorLabel

Antall unike dager med aktivitet siste 7 dager (fra alle 4 kilder).

consistencyComponent = (uniqueActiveDays / 7) × 20
Aktive dagerPoeng
7/720
4/7~11
1/7~3
0/70
ScoreNivåIkonBeskrivelse
80–100Ignitingflame.fillYour practice carries its own force
60–79FlowingwindYou’re finding your rhythm
40–59Steadycircle.fillA quiet strength is forming
0–39Stirringarrow.up.circleSomething is starting to move

API returnerer nivået som streng i transformation_momentum.momentum_level.

Scenarioanalyse

heading.anchorLabel
BrukertypeRecentCycleConsistTotalNivå
Daglig, prosesserer alt503020100Igniting
Daglig, halvparten prosessert50152085Igniting
4 dager/uke, prosesserer alt~3330~11~74Flowing
Kun sjekk-inn daglig (ingen impulser)5002070Flowing
Én impuls i dag, ny bruker~100~3~13Stirring
Var aktiv, 3 dager pause~19varierer~6~25–45Stirring/Steady
Var aktiv, 7 dager pause0varierer00–30Stirring
ScenarioOppførselStatus
Ny bruker, 0 aktivitetscore=0, level=“stirring”OK
Tom activityDates arrayReturnerer 0 umiddelbartOK
DB-feil på activity-queriesLogget som warning, tom array, score=0Graceful
DB-feil på stats/impulsesKaster feil, 500-responsKorrekt
Fremtidig dato (klokkedrift)daysAgo < 0, filtrert utOK
completedSessions > impulses (30d)Rate > 1.0, capped av Math.min(30)OK
50 aktiviteter på én dagTeller som 1 dag (volum-cap)OK
latestInsight.content er nullReturnerer tom strengOK
Concurrent requestsCache forhindrer dobbeltberegningOK

Kjente begrensninger

heading.anchorLabel
  1. Server-tidssone. Dager beregnes fra serverens midnatt (setHours(0,0,0,0)), ikke brukerens lokale tid. Akseptabelt for nåværende nøyaktighetsnivå.
GET /api/v1/dashboard/home
→ getOrSetCache(CacheKey.home(userId))
→ fetchHomeData(userId)
→ Promise.all([
impulseActivity, checkInActivity, sessionActivity, noteActivity, (14 dager)
recentCompleted, recentImpulses, (30 dager)
impulses, stats, insight, pulse (UI-data)
])
→ calculateMomentumScore(allActivityDates, recentCycles, recentImpulses)
→ getMomentumLevel(score)

Cache invalideres av regen-worker etter nye impulser/sesjoner.

{
"transformation_momentum": {
"momentum_score": 42,
"momentum_level": "steady",
"current_cycle": {
"captured": 3,
"transforming": 1,
"transformed": 2
},
"cycles_completed": 12,
"latest_insight": {
"id": "uuid",
"title": "pattern",
"preview": "Du viser et mønster..."
}
}
}

Klientintegrasjon

heading.anchorLabel
PlattformFilTilgang
KMPHomeResponse.ktTransformationMomentummomentumScore, momentumLevel
iOSHomeStoreWrapper.swifthomeStore.momentumScore, homeStore.momentumLevel
AndroidHomeScreen.ktVia Compose state fra KMP store