Kumbatio
Concepts

Deferral

The "not now" primitive - pure snooze presets with energy-aware default ordering.

Deferring an item is an energy statement: it declares insufficient capacity for it right now and names when it should resurface. The SDK models this with pure presets - (now) => Date functions - plus a strategy that orders them by energy level so the one-tap default matches capacity.

The presets

createDeferralPresets builds the standard set. Times are computed in local time - "tomorrow morning" means the user's morning:

import { createDeferralPresets } from '@kumbatio/energy-system'

const presets = createDeferralPresets({ morningHour: 9, eveningHour: 18 })
// both options optional; defaults are 9 and 18
Preset idLabelResurfaces
in-1-hourIn 1 hournow + 60 minutes
this-eveningThis evening (18:00)today at eveningHour, or tomorrow if already past
tomorrow-morningTomorrow morning (9:00)tomorrow at morningHour
next-workdayNext workday (9:00)next non-weekend day at morningHour
next-mondayNext Monday (9:00)next Monday at morningHour

The stable ids are exported as DEFERRAL_PRESET_IDS so configs and strategies can reference them without magic strings:

import { DEFERRAL_PRESET_IDS } from '@kumbatio/energy-system'

DEFERRAL_PRESET_IDS.tomorrowMorning // 'tomorrow-morning'

Resolving a deferral

resolveDeferral maps a preset id to a resurface timestamp (epoch ms). Unknown ids return null - the caller decides whether that's an error:

import { resolveDeferral } from '@kumbatio/energy-system'

const resurfaceAt = resolveDeferral(presets, 'tomorrow-morning')
// number (epoch ms), or null for an unknown id

// Deterministic in tests: pass the reference moment
resolveDeferral(presets, 'next-monday', new Date('2026-07-21T10:00:00'))

Storing and resurfacing the item at that timestamp is your app's job - the SDK computes when, deliberately staying out of where your items live.

Energy-aware ordering

deferralStrategy resolves the presentation order and the one-tap default for the current level:

import { deferralStrategy } from '@kumbatio/energy-system'

const { defaultPresetId, orderedPresetIds } = engine.resolve(deferralStrategy)
LevelDefaultReasoning
100in 1 hourHigh capacity - short deferrals are realistic
75in 1 hour
50this eveningPush past the current stretch
25tomorrow morningResurface when capacity has plausibly recovered
0tomorrow morningNot in an hour - you're resting

orderedPresetIds lists all five, most-prominent first, so a snooze menu can render them in capacity-appropriate order.

Putting it together

import {
  createDeferralPresets,
  deferralStrategy,
  resolveDeferral,
} from '@kumbatio/energy-system'

const presets = createDeferralPresets()

function snooze(itemId: string) {
  const { defaultPresetId } = engine.resolve(deferralStrategy)
  const resurfaceAt = resolveDeferral(presets, defaultPresetId)
  if (resurfaceAt !== null) {
    saveSnooze(itemId, resurfaceAt)
  }
}

Deferral presets compute times; they hold no state. If you also route the item's notification through the gate, the defer-not-drop guarantee applies when it resurfaces at a low-energy moment.