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). Every task is seeded with at least one persisted occurrence row at creation time (including timingType=none) so ingest paths are uniform. For recurring tasks, due slots are generated in the active horizon window at read time and inserted idempotently using deterministic occurrence keys before compute runs. CheckIns: daily body/mind/mood ratings, check-in tier, day-type classification. Events: append-only audit log; source of truth for rebuilding derived state. ManaCalibrationSnapshots: per-day derived calibration state (algorithmVersion, pool/coefficient EWMA, confidence, alpha/bias, daily spend ratio inputs) used for deterministic fast reads and migration checkpoints. ManaCalibrationMigrations: append-only algorithm migration/rollback records (fromVersion, toVersion, migration type, timestamp, optional rollback checkpoint reference).
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, day_quality
TaskrecurrenceRulestring?RFC 5545 RRule, optionally with DTSTART and EXDATE lines
TaskdayQualityLevelenum?required only for timingType = day_quality
TaskdayQualityModeenum?required only for timingType = day_quality
TaskOccurrenceidintauto-increment local surrogate ID
TaskOccurrencetaskIdstringFK to task definition (Tasks.id)
TaskOccurrencedueDatetimedatetimecanonical due timestamp for this instance
TaskOccurrenceoccurrenceKeystringdeterministic identity key: `taskIddueDatetimeUtcMillis`
TaskOccurrencestateenumactive, completed, cancelled; missed is derived, not stored
TaskOccurrencedetailsstring?per-instance override; if null, UI can use Task.details
TaskOccurrencedeferredUntildatetime?shifts the effective due time without changing the canonical key
TaskOccurrencecompletedAtdatetime?required when state = completed
TaskOccurrencedueDatetimeOverridedatetime?edit-this-only recurrence exception; takes precedence over dueDatetime
TaskOccurrencepartialCompletionFractiondouble?0.0-1.0 fraction already completed; null means no partial completion
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
ManaCalibrationSnapshotdaydatetimenormalized local day identity for the snapshot
ManaCalibrationSnapshotalgorithmVersionintexplicit algorithm state version tag
ManaCalibrationSnapshotpoolEwmadoublepersisted pool EWMA state
ManaCalibrationSnapshotcoefficientEwmadoublepersisted coefficient EWMA state
ManaCalibrationSnapshotconfidencedoublepersisted confidence value used for bias
ManaCalibrationSnapshotbaselineSpenddoubleday-level baseline spend input for EWMA updates
ManaCalibrationSnapshotdailyRatiodoublebaselineSpend / allocatedMana ratio used for coefficient learning
ManaCalibrationMigrationfromVersion / toVersionintversion transition boundary
ManaCalibrationMigrationtypeenum-like stringparameter_only, state_transform, recompute, rollback
UserProfilelastCheckInDatedatetimeabsence detection
UserProfilereturnRecoveryDaysRemainingint3-step return recovery
PemEventattributionConfidencedouble0.4-1.0 window-based
PemEventuserAttributedTriggerstring?optional user attribution
PersonalTaskEntryobservedHRResponseJSON?reserved for wearable integration

Task schema v1

Canthus uses product names in planning and persisted names in code. Keep both in sync when writing specs or implementation tasks.
Product termPersisted representationUser-facing meaning
Flexible tasktimingType = noneCan be done when it fits today’s capacity. A seeded occurrence keeps ingest and completion paths uniform.
Deadline-window tasktimingType = deadlineCan be completed before a due date/time. The due timestamp defines the latest useful completion point, not a shame state.
Appointment tasktimingType = scheduledHappens on a specific date/time. It may appear early for planning, but it is only completable on its due day.
Day-quality tasktimingType = day_qualityImplementation-specific flexible task gated by a day-quality bucket. It is part of the persisted enum and must be documented when schema code references it.
Task definitions describe recurring intent and cost inputs. Task occurrences describe concrete instances, state, and per-instance edits. A task can be recurring or non-recurring in any temporal type that has a due timestamp.

Schema examples by task type

Flexible task (timingType = none)

Task:
  id:              "task-flexible-uuid"
  timingType:      "none"
  recurrenceRule:  null
  relativeCost:    1.5
  durationMinutes: 30

TaskOccurrence (seeded at creation, due = createdAt):
  occurrenceKey:   "task-flexible-uuid|1762214400000"
  dueDatetime:     2026-05-04T00:00:00
  state:           "active"

Deadline-window task (timingType = deadline, non-recurring)

Task:
  timingType:      "deadline"
  recurrenceRule:  null

TaskOccurrence (seeded at creation):
  dueDatetime:     2026-05-10T17:00:00   # latest useful completion point
  state:           "active"
Missed state is derived at read time: if dueDatetime is in the past and state = active, surfacing derives missed.

Appointment task (timingType = scheduled, recurring)

Task:
  timingType:      "scheduled"
  recurrenceRule:  "DTSTART:20260505T090000\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"

Generated due slots (horizon window): 2026-05-05, 2026-05-07, 2026-05-09…

TaskOccurrence (persisted when state changes):
  occurrenceKey:              "task-id|1746435600000"
  dueDatetime:                2026-05-05T09:00:00
  state:                      "completed"
  completedAt:                2026-05-05T09:45:00
  partialCompletionFraction:  null
Appointment previews can appear before the due day for planning. They are not completable until the due day.

Recurrence exceptions

Recurring series use deterministic occurrence keys so each generated slot can be matched to persisted state.
User actionStored representationNotes
Skip this occurrencePersist the occurrence with state = cancelledFuture generated slots are unaffected.
Pause seriesStore a bounded pause as cancelled occurrences or equivalent EXDATE lines for the paused slotsPause does not delete history.
Edit this occurrence onlySet dueDatetimeOverride on the occurrenceThe override changes effective scheduling but not occurrenceKey.
Edit this and all futureAdd EXDATE for the old slot on the original task and create a sibling task from the new DTSTARTThis preserves old history and gives future slots a new rule.

Edit this occurrence only

TaskOccurrence:
  dueDatetime:         2026-05-07T09:00:00   # original slot
  dueDatetimeOverride: 2026-05-07T14:00:00   # moved to afternoon
  state:               "active"

Edit this and all future

Original Task:
  recurrenceRule:  "DTSTART:20260505T090000\nRRULE:FREQ=WEEKLY;BYDAY=MO\nEXDATE:20260512T090000"

Sibling Task:
  timingType:      "scheduled"
  recurrenceRule:  "DTSTART:20260512T140000\nRRULE:FREQ=WEEKLY;BYDAY=MO"

Splitting and partial completion

Partial completion records that some work happened while the occurrence remains open.
TaskOccurrence:
  state:                      "active"   # still open
  partialCompletionFraction:  0.5        # half done
  completedAt:                null
Mana cost displayed = full cost × (1 - 0.5) = 50% of base cost. Spend accounting happens when an occurrence is completed. A partial completion alone does not write completed spend for the day. If the user defers the remaining work, deferredUntil shifts the same occurrence’s effective due time and the visible remaining cost is still based on 1 - partialCompletionFraction. Splitting a task into separate future work should create separate occurrences or sibling tasks with their own due timestamps. Do not duplicate completed spend from the original occurrence into the split work.
The schema and costing rules support partial completion and recurrence exceptions. Some app-level editing flows are still implementation follow-ups; the contract above is the source of truth for future work.

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, horizonStart, horizonEnd)
TaskOccurrences.insertMissing(dueSlots, key = taskId|dueDatetimeUtcMillis)
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.