Impulse AI Docs
Intern dokumentasjon
skipLink.label

Tilstandsmaskiner

Impulse AI bruker to fundamentalt forskjellige tilnærminger til tilstandshhandtering, avhengig av kontekst. API-laget bruker imperative tilstandsmaskiner for a sikre gyldige overganger. SwiftUI-laget bruker derivert tilstand via computed enums for reaktiv UI-oppdatering.

API-lagets tilstandsmaskin

heading.anchorLabel

Unified Session Machine

heading.anchorLabel

Sesjonsoverganger styres av en enkel, eksplisitt tilstandsmaskin i apps/api/src/state-machines/session-machine.ts.

Maskinen definerer gyldige overganger for 5-stegs transformasjonssesjoner — identisk struktur for bade contraction og expansion.

Sesjonsstatus

heading.anchorLabel
created ──[START]──> active ──[COMPLETE]──> completed
└──[ADVANCE_STEP]──> active (neste steg)
StatusBeskrivelse
createdSesjon opprettet, ikke startet
activeBruker er i sesjonen
completedAlle 5 steg fullfort
abandonedSesjon forlatt (timeout eller brukervalg)
EventFraTilEffekt
STARTcreatedactiveSetter current_step til 1
ADVANCE_STEPactiveactiveOker current_step med 1 (1->2->3->4->5)
COMPLETEactive (steg 5)completedMarkerer sesjon som fullfort
NrEngelsk navnNorsk navnFormaal
1FEELKjenn i kroppenSomatisk bevissthet
2OWNErkjenn energi-investeringenErkjennelse
3SHIFTTilstand definerer virkelighetFilosofisk skifte
4ENVISIONSe for deg ny tilstandVisualisering
5RELEASELoskoble og affirmerLosrivelse

Valideringsfunksjoner

heading.anchorLabel

Tilstandsmaskinen eksponerer rene funksjoner for validering:

// Sjekk om en overgang er gyldig
canTransition(currentStatus: SessionStatus, event: SessionEvent): boolean
// Hent neste status etter et event (null = ugyldig)
getNextStatus(currentStatus: SessionStatus, event: SessionEvent): SessionStatus | null
// Sjekk om steg kan avanseres
canAdvanceStep(currentStep: number | null): boolean
// Sjekk om sesjon kan fullføres (ma vaere pa steg 5)
canComplete(currentStep: number | null): boolean

Komplett validering

heading.anchorLabel

validateTransition() kombinerer alle sjekker og returnerer et strukturert resultat:

interface ValidationResult {
valid: boolean
nextStatus: SessionStatus | null
nextStep: number | null
error?: string
}
// Eksempel
const result = validateTransition(
{ id: '...', status: 'active', currentStep: 3, classification: 'contraction', ... },
'ADVANCE_STEP'
)
// → { valid: true, nextStatus: 'active', nextStep: 4 }
const invalid = validateTransition(
{ id: '...', status: 'active', currentStep: 3, classification: 'contraction', ... },
'COMPLETE'
)
// → { valid: false, error: 'Cannot complete session from step 3 (must be on step 5)' }

Stegformaal per klassifisering

heading.anchorLabel

AI-konteksten for hvert steg varierer basert pa om sesjonen er contraction eller expansion:

getStepPurpose(step: number, classification: SessionClassification): string
StegContraction-formaalExpansion-formaal
1Legg merke til de fysiske sansningeneLegg merke til hva du foler nar du tar intensjonen pa alvor
2Erkjenn at du investerer energi i fryktenKlar opp hvorfor dette betyr noe for deg
3Din indre tilstand skaper din virkelighetDin indre tilstand skaper din virkelighet
4Visualiser frihet — hvor i kroppen foler du det?Se hvem du er nar dette allerede er sant
5Slipp frykten. Bekreft din nye sannhetSlipp behovet for kontroll. Stol pa prosessen

SwiftUI: Derivert tilstand

heading.anchorLabel

I SwiftUI oppdaterer flere @Observable stores seg uavhengig og asynkront. If-else-kjeder i view body er ordensavhengige, ikke-uttommende, og produserer race condition-bugs.

Losningen: Computed enums

heading.anchorLabel

Utled visningstilstand gjennom en computed enum — aldri gjennom imperative overganger.

// 1. Enum -- en fil, fanger ALLE mulige tilstander
enum FooContentState {
case loading
case empty
case content
case error(String)
}
// 2. Computed property -- leser @Observable stores
private var contentState: FooContentState {
if store.isLoading && !store.hasData { return .loading }
if let error = store.error, !store.hasData { return .error(error) }
if !store.hasData { return .empty }
return .content
}
// 3. Exhaustive switch -- kompilatoren sikrer fullstendighet
var body: some View {
switch contentState {
case .loading: ProgressView()
case .empty: EmptyState()
case .content: ContentView()
case .error(let msg): ErrorView(msg)
}
}
EgenskapBeskrivelse
ReaktivComputed property leser @Observable stores — SwiftUI reevaluerer automatisk
UttommendeNy enum-case gir kompilerfeil overalt switchen brukes
Ingen overgangerTilstand utledes, ikke muteres — ingen race conditions
Prioritet kodetForste matchende betingelse vinner

Aktive implementasjoner

heading.anchorLabel
ViewEnumCasesFil
ImpulseDetailViewSessionSectionState5Features/Impulses/SessionSectionState.swift
InsightsViewInsightsContentState + InsightsProductVisibility3 + 3Features/Insights/InsightsContentState.swift
JourneyViewJourneyContentState6Features/Journey/JourneyContentState.swift
UnifiedSessionViewSessionLayer8Definert inline

Kontrast: API vs SwiftUI

heading.anchorLabel

De to tilnaermingene ligner, men er fundamentalt forskjellige i kontrollmodell:

DimensjonAPI (session-machine)SwiftUI (computed enum)
KontrollKaller kontrollerer mutasjonStores muterer uavhengig
ValideringcanTransition() guardsKompilator-hhandheving
TilstandEksplisitt mutertUtledet fra stores
OvergangerEvent -> ny statusIngen — reevaluering
FeilhhandteringRuntime-feil (ugyldig overgang)Compile-time feil (manglende case)
Race conditionsForhindret av guardsForhindret av design

Hvorfor to tilnaerminger?

heading.anchorLabel

API-laget trenger eksplisitte overganger fordi:

  • Flere klienter kan sende samtidige foresporsler
  • Databasetilstand ma vaere konsistent
  • Ugyldige overganger ma avvises med forklarende feilmelding
  • Audit trail krever sporbare tilstandsendringer

SwiftUI-laget trenger derivert tilstand fordi:

  • Stores oppdateres uavhengig (HomeStore, SessionStore, InsightsStore)
  • Data ankommer til forskjellige tidspunkt (loading -> data -> error)
  • UI ma alltid vise noe — aldri en ugyldig mellomtilstand
  • View body ma vaere uttommende og deklarativ

Prosessuell status (impulser)

heading.anchorLabel

I tillegg til sesjonsmaskinen har impulser en enkel kanban-status:

captured ──> transforming ──> transformed
StatusBeskrivelse
capturedImpuls fanget, ingen sesjon startet
transformingAktiv sesjon pagar
transformedMinst en sesjon fullfort

Denne statusen oppdateres av API-et nar sesjoner starter og fullføres, og driver filtreringslogikk i Journey-viewet.

AI Workload Status

heading.anchorLabel

AI-operasjoner (klassifisering, veiledning, oppsummering) har sin egen livssyklus:

idle ──> pending ──> processing ──> complete
└──> failed
StatusBeskrivelse
idleIngen AI-operasjon etterspurt
pendingJobb lagt i BullMQ-koen
processingAI-kall under utforelse
completeResultat lagret i database
failedAI-kall feilet (retry uttomt)

Denne statusen brukes pa:

  • impulses.ai_classification_status — klassifisering av impulser
  • sessions.step_N_ai_status — veiledning per steg (1-5)
  • sessions.ai_template_status — aggregert status for alle steg
  • check_ins.ai_assessment_status — sjekk-inn vurdering