Skip to main content

Key tables

Tasks: task definitions (relativeCost, duration, body/mind weights, timing fields, essential flag, and default details copy). TaskOccurrences: canonical per-instance schedule and state rows (dueDatetime, completion/cancellation/deferral, per-instance edits). For recurring tasks, due slots are generated at runtime from the task rule and matched against persisted rows. CheckIns: daily body/mind/mood ratings, check-in tier, day-type classification. Events: append-only audit log; source of truth for rebuilding derived state.
manaCost is not stored as source-of-truth. It is computed at read time from task properties and today’s axis factor.

Key fields

TableFieldTypeNotes
Taskdetailsstring?default details/instructions for this task
TaskrelativeCostdoubleeffort relative to baseline
TaskisEssentialbooluser-controlled
TasktimingTypeenumnone, deadline, scheduled
TaskrecurrenceRulestring?RFC 5545 RRule
TaskOccurrenceidintauto-increment local surrogate ID
TaskOccurrencetaskIdstringFK to task definition (Tasks.id)
TaskOccurrencedueDatetimedatetimecanonical due timestamp for this instance
TaskOccurrencedetailsstring?per-instance override; if null, UI can use Task.details
CheckIncheckInDatedatetimelocal calendar day identity, normalized to local midnight
CheckIncheckInTierenumfull, quick, momentum, none with confidence weights 1.0, 0.7, 0.4, 0.1
CheckIndayTypeenumnormal, packed, blocked, unusual; carries opportunity weights 1.0, 0.25, 0.0, 0.0
UserProfilelastCheckInDatedatetimeabsence detection
UserProfilereturnRecoveryDaysRemainingint3-step return recovery
PemEventattributionConfidencedouble0.4-1.0 window-based
PemEventuserAttributedTriggerstring?optional user attribution
PersonalTaskEntryobservedHRResponseJSON?reserved for wearable integration

Storage model

  • Encrypted local SQLite via Drift + SQLCipher
  • No remote DB, no cloud sync
  • Offline-first behavior
  • Encryption key in platform keystore (not cloud backed up)
Settings and feature flags are in shared_preferences and persist across health-data reset.

Runtime recurrence query

dueSlots = generateFromRRule(task.recurrenceRule, queryStart, queryEnd)
occurrences = merge(dueSlots, TaskOccurrences.persistedRows)
activeOccurrence = nearest unresolved future dueDatetime

Recurrence parsing contract

  • Stored recurrence input may be either:
    • a full content line (RRULE:FREQ=...)
    • or a bare rule value (FREQ=...)
  • If DTSTART exists in the stored string, it is used as the recurrence anchor.
  • If DTSTART is absent, the anchor defaults to Task.createdAt.
  • Line parsing splits on both Unix and Windows line endings (\\n, \\r\\n).
  • Implementation detail: the parser uses regex pattern [\\r\\n]+ to split lines.
    • \\r matches carriage return, \\n matches newline, + collapses one-or-more line break chars into one separator.
    • Example: DTSTART:...\\r\\nRRULE:... and DTSTART:...\\n\\nRRULE:... both split into the same two logical lines.
  • Invalid recurrence strings fail soft:
    • no generated slots are returned
    • a typed warning is emitted by the recurrence service
Example:
DTSTART:20260310T090000
RRULE:FREQ=DAILY;COUNT=2
Generates:
2026-03-10 09:00
2026-03-11 09:00

Derived status contract

  • missed is derived at read time in domain scheduling logic.
  • missed is not persisted in TaskOccurrences.
  • deferredUntil shifts an occurrence’s effective due time used by scheduling.