Specfork

@specfork/habits

Personal, local-first habit tracker with yes/no and measurable habits, flexible frequencies, skips, streaks, habit strength score, charts, reminders, export and backup.

personalhealth Version 0.1.1 78 requirements Licence CC-BY-4.0 Specfork/habits

Habits

One person tracks their own habits on their own device. A habit is either yes/no ("did I do it?") or measurable ("how much?"), repeats daily, every N days, N times per week or month, and earns a strength score that rises with consistency and decays slowly with misses. History, streaks, charts and reminders show progress; all data stays local and can be exported, imported and backed up. Behaviour derived from Loop Habit Tracker's open-source code (GPL-3.0-or-later), written fresh and stack-free. Not affiliated with or endorsed by Álinson Santos Xavier. The name is used only to credit the source.

Area Prefix Covers
habits.md HAB Habit fields, types, frequency, colours, archive, delete, order, sort, filter, search
checkins.md CHK Days, entries, yes/no/skip/unknown, amounts, notes, toggling, auto-completed days, completion
scoring.md SCR Habit strength score: formula, windows, measurable targets, skips
streaks.md STK Streaks, current streak, best streaks
charts.md CHT Habit list row, overview, history calendar, score, totals, target progress, weekday frequency
reminders.md REM Reminder time and weekdays, time zones, suppression, actions, snooze
data.md DATA Local storage, full export, spreadsheet export, import, backups
settings.md SET Preferences and defaults

Single user, no account, no server: every rule here runs on one device with no network. Dates are local calendar dates (YYYY-MM-DD); instants are UTC ISO 8601; names are snake_case.

Charts

Status: Draft · Prefix: CHT

Purpose

How the habit list and each habit's detail page show history and progress.

Requirements

ID Requirement Verified by
CHT-1 Habit list row: name, a ring filled to today's score (SCR-1), and the states of the most recent days that fit the width, newest first (oldest first when reverse_day_order is on). Measurable cells show the amount; empty for unknown. 5 columns, today Mar 10 → Mar 10..6
CHT-2 Detail page: question, frequency in words, target and unit (measurable), reminder time if set; then today's score; change versus 30 and 365 days ago in percentage points; total check-ins (STK-6). 3/7 → "3 times per week"; today 60%, 30 days ago 45% → "+15"
CHT-3 History calendar: one cell per day from the start day to today in week columns starting first_weekday. Yes/no cells: yes full, auto dimmed, skip hatched, no/unknown empty. Measurable: skip hatched; completed with amount > 0 full; completed with no amount dimmed; amount > 0 but not completed grey; else empty. Days with notes carry a marker. 3/7 with yes 1, 3, 5 → days 2, 4, 6, 7 dimmed
CHT-4 Tapping a calendar cell for today or a past day edits it (CHK-5 or CHK-6, per one_tap_toggle); future cells do nothing. tap tomorrow → no change
CHT-5 Score chart: buckets day|week|month|quarter|year (default week); each point = mean of the daily scores in the bucket. Weeks start first_weekday; quarters start Jan, Apr, Jul, Oct. daily scores 0.2, 0.4, 0.6 in one week → point 0.4
CHT-6 Totals chart: yes/no = number of yes days per bucket (week|month|quarter|year, default week); measurable = sum of amounts per bucket (day|week|month|quarter|year, default day); skip and unknown add 0. amounts 3, skip, 4 in one week → 7
CHT-7 Target progress (measurable): current day (daily habits only), week (daily and weekly habits), month, quarter, year; each shows sum versus target. Targets by target_period: day → daily_target × 1, 7, days in month, 91, days in year; week → target × 1, 4, 13, 52; month → target × 1, 3, 12. Each target minus daily_target × skip days in that period, floored at 0. 70/week, 2 skips this month → month target 280 − 20 = 260
CHT-8 Weekday frequency: for each calendar month and weekday, the number of yes days (yes/no) or the sum of amounts (measurable). yes on 3 Mondays in March → March/Monday = 3
CHT-9 Chosen buckets persist across restarts; all charts refresh after any change (CHK-14) and when the date changes. pass midnight → new day column appears

Out of scope

  • Home-screen widgets and watch faces (platform features; they show the same data as CHT-1, CHT-3, CHT-5..8 and best streaks).
  • Celebration animations and haptic feedback.
  • Cross-habit dashboards.

Decisions

  • Widgets are reduced to "the same views elsewhere"; the source has checkmark, history, score, streak, frequency and target widgets.

Check-ins

Status: Draft · Prefix: CHK

Purpose

Recording what happened on each day, and deriving each day's state and completion from the entries and the habit's frequency or target.

Requirements

ID Requirement Verified by
CHK-1 A day is a local calendar date. today = the device's current local date; with late_day_start on (SET-1), times 00:00–02:59 still belong to the previous date. 02:30 on 2026-03-02, late_day_start on → today 2026-03-01
CHK-2 Entry: {habit_id, date, value, notes}, at most one per (habit_id, date); writing an entry for a date replaces the previous one. notes is trimmed free text and may sit on an entry whose value is unknown. set yes then no on 2026-03-01 → one entry, no; notes " sore knee " → "sore knee"
CHK-3 Yes/no value: yes|no|skip|unknown. Measurable value: an amount (decimal 0..1,000,000, rounded to 3 decimals), skip or unknown. Writing unknown with empty notes removes the entry. amount 2.34567 → stored 2.346; −1 → rejected
CHK-4 Entries may be written for today or any past date, never a future date. write for tomorrow → rejected; for 2019-05-01 → saved
CHK-5 Quick toggle (yes/no habits) moves a day to the next value and keeps its notes: yes → skip if skip_enabled, else no; skip → no; no → unknown if unknown_enabled, else yes; unknown → yes; an auto day → yes. both settings off: yes → no → yes; both on: yes → skip → no → unknown → yes
CHK-6 Entry editor sets value and notes together: yes/no offers yes, no, and skip / unknown only when enabled; measurable offers an amount (blank = unknown) plus the same options. Closing without confirming saves changed notes only. Invalid input keeps the old value. amount "abc" → previous amount kept
CHK-7 Yes/no day states: yes (entered), auto (covered by frequency, CHK-8), no, skip, unknown. A day with no entry is unknown; when unknown_enabled is off it is shown as no. no entry, setting off → shown as no
CHK-8 Covered windows (yes/no, frequency n/d, d > 1): take yes days newest first; for each run of n consecutive ones, if newest − oldest < L days, the window [oldest, oldest + L − 1] is covered. L = d, except d = 30 or 31: L = length of the oldest day's month, or of the next month when the oldest day is its month's last day. 3/7, yes on days 1, 3, 5 → window days 1–7; 2/7, yes on days 1 and 9 → no window
CHK-9 Windows are adjusted newest to oldest: when a window ends on or after the start of the next newer window, it moves earlier by min(end − newer_start + 1, end − its newest yes day) days. A window may then start before the first yes. 1/7, yes on 2026-01-01 and 2026-01-05 → windows 2025-12-29..2026-01-04 and 2026-01-05..01-11
CHK-10 In a covered window, days that are no, unknown or empty become auto; yes and skip stay. Every-day (1/1) habits never have auto days. 3/7, yes 1, 3, 5, explicit no on day 6 → day 6 auto
CHK-11 A yes/no day is completed when its state is yes, auto or skip. skip day → completed
CHK-12 Measurable completion is per period: day (1/1), week starting first_weekday (1/7) or calendar month (1/30). daily_target = target_value ÷ denominator; period_target = max(0, target_value − daily_target × skip days in period); sum = amounts in period (skip, unknown = 0). Met when sum ≥ period_target (at_least) or sum ≤ period_target (at_most). 70 km/week, Mon 20, Tue 30, Wed skip → target 60, sum 50 → not met; Thu 10 → met
CHK-13 Every day of a met period, from the habit's first entry up to today, is completed; days of other periods are not. "Completed today" = today is completed; "entered today" = today's value is not unknown. at most 2 coffees/day, entries on earlier days, none today → completed today, not entered today
CHK-14 Day states, completion, scores and streaks are re-derived right after any entry or habit change. toggle yesterday to yes → score and streak change at once

Out of scope

  • Multiple check-ins per day with times of day; geolocation or photo proof.
  • Automatic check-ins from other apps or devices (health, fitness sensors).

Decisions

  • Covered windows reproduce the source exactly, including an explicit no inside a window becoming auto and windows reaching back before the first yes; this keeps streaks as long as the frequency allows.
  • Amount limit 1,000,000 replaces the source's implicit integer-overflow ceiling (about 2.1 million).
  • Removing an unknown entry with no notes is equivalent to the source keeping an empty row.

Data

Status: Draft · Prefix: DATA

Purpose

Where data lives, exactly what "export all my data" returns, spreadsheet export, import, and automatic backups.

Requirements

ID Requirement Verified by
DATA-1 Local-first, single user: all data lives on the device in one data set; no account, sign-in or network is needed or used. Data leaves the app only through a user-started export or backup, to a location the user picks. airplane mode → every feature works; no network requests
DATA-2 Full export ("export all my data") is one UTF-8 JSON file habits-export-YYYY-MM-DD-HHmmss.json (local time): {format: "spechub-habits", version: 1, exported_at (UTC), settings ([SET-1](#SET-1)), habits: [...]}, with every habit, archived ones included, in position order. 3 habits, 1 archived → habits has 3 items
DATA-3 Each habit in the export: {id, name, question, notes, type, color, frequency {numerator, denominator}, unit, target_value, target_type, archived, position, created_at, reminder {hour, minute, weekdays: ["mon".."sun"]} | null, entries: [{date, value, notes}]}. Text fields are "" when empty; unit, target_value, target_type are null for yes/no habits. yes/no habit → "unit": null
DATA-4 entries are the stored entries only (CHK-2), oldest first; yes/no value is "yes"|"no"|"skip"|"unknown", measurable value is a number or "skip"|"unknown". Derived states, auto days, scores and streaks are not exported. amount 2.5 → "value": 2.5; covered day absent
DATA-5 Spreadsheet export: a zip habits-csv-YYYY-MM-DD.zip with habits.csv (one row per habit, fields of DATA-3 except entries and reminder, frequency as numerator, denominator), days.csv (habit_id, date, state, amount, completed, notes, one row per habit per day from start day to today) and scores.csv (habit_id, date, score with 4 decimals). UTF-8, comma-separated, quoted per RFC 4180, header row first. daily habit started 10 days ago → 11 days.csv rows
DATA-6 Import accepts a full export or backup with format "spechub-habits" and version ≤ the app's; any other file is rejected with a message. Import is all-or-nothing. version: 2 → rejected, data unchanged
DATA-7 Import merges by habit id: a known habit takes the imported fields except position; an unknown habit is created at the end of the manual order. Entries are written per (habit, date) where value or notes differ; habits and entries missing from the file are kept. file has habit A with 1 changed entry → A updated, only that entry changes
DATA-8 Settings from the file are applied only when the app had no habits before the import. fresh install, import → settings restored; existing data → settings unchanged
DATA-9 Round trip: export, then import into an empty app, gives identical habits, entries and settings, hence identical scores and streaks. Importing the same file twice changes nothing the second time. compare exports before and after → equal except exported_at
DATA-10 Backup = a full export (DATA-2) named habits-backup-YYYY-MM-DD-HHmmss.json, written to the backup folder: a user-chosen folder, else the app's private storage. "Back up now" writes one at once; restoring = importing a chosen backup (DATA-6..8). back up now → new file; restore it into a fresh install → all habits back
DATA-11 Automatic backup: each time the app opens or returns to the foreground, if the newest backup is older than 24 hours or none exists, write one; then keep only the newest 5 files matching the backup name pattern in that folder. 5 backups, newest 25 h old, open app → 6th written, oldest deleted

Out of scope

  • Sync between devices or with a server; accounts (@specfork/auth is not required).
  • Importing other apps' formats (the source reads three other habit apps' files and its own database file).
  • Cloud backup services; encryption of export files.

Decisions

  • The export is a documented JSON file instead of the source's raw database copy, so a user can read it and any rebuild can import it; the source's CSV zip becomes DATA-5 with long-format tables.
  • Imported new habits go to the end of the list; the source copied their stored position, which can collide.
  • Export, import and backup stay here instead of @specfork/privacy: PRV needs a signed-in user (@specfork/auth:AUTH-23 re-auth, per-user download links, email confirmation) and requires @specfork/auth, which this account-free app does not use; and PRV-5 never merges, while DATA-7 merges by id so a backup restores in place.
  • Two devices can be kept in step by hand through export/import, since ids are stable; real sync is left out to keep the app account-free.

Habits

Status: Draft · Prefix: HAB

Purpose

What a habit is, how its frequency and target are set, and how the habit list is archived, deleted, ordered, sorted, filtered and searched.

Requirements

ID Requirement Verified by
HAB-1 Habit: {id (uuid, 32 lowercase hex, generated), name, question?, notes?, type: yes_no|measurable, color (0..19), frequency {numerator, denominator}, unit?, target_value?, target_type?: at_least|at_most, archived (default false), position, reminder? (REM-1), created_at}. create "Read" → 32-hex id, archived false
HAB-2 type is chosen at creation and never changes; unit, target_value, target_type exist only for measurable habits and are empty for yes/no habits. edit yes/no habit with type: measurable → rejected
HAB-3 name, question, notes, unit are trimmed on save. name is required, 1..50 characters after trimming; the others have no limit. name " Run " → "Run"; name " " → rejected; 51 chars → rejected
HAB-4 Yes/no frequency choices and stored value: every day → 1/1; every N days → 1/N (N 1..999); N times per week → N/7 (N 1..9); N times per month → N/30 (N 1..99); X times per Y days → X/Y (1..999 each). Defaults to 1/1. Numerator < 1, a blank part, or numerator ≥ denominator stores 1/1; a denominator of 30 or 31 is shown as "per month". "3 times per week" → 3/7; "7 times per week" → 1/1
HAB-5 Measurable habit: target_value required, decimal ≥ 0 (default 0); target_type default at_least; target_period: day|week|month stores frequency 1/1, 1/7 or 1/30 (default day). target 10 km per week → frequency 1/7, target_value 10
HAB-6 color is a palette index: 0 red, 1 deep orange, 2 orange, 3 amber, 4 yellow, 5 lime, 6 light green, 7 green, 8 teal, 9 cyan, 10 light blue, 11 blue, 12 indigo, 13 deep purple, 14 purple, 15 pink, 16 brown, 17 dark grey, 18 grey, 19 light grey. New habits default to 11 and get position = number of existing habits (end of the manual order). 3 habits exist, create one → color 11, position 3
HAB-7 Editing a habit keeps all its entries; changes to frequency, target or target type re-derive day states, completion, scores and streaks for every past day (CHK, SCR). daily with yes on Mon, Wed → change to 1/7 → Tue now auto-completed
HAB-8 Archive and unarchive apply to one or more selected habits. An archived habit keeps its entries, is hidden unless show_archived is on, gets no reminders, and is included in exports. archive "Read" → gone from list; show_archived on → shown, marked archived
HAB-9 Delete applies to one or more habits after a confirmation that it cannot be undone; it removes the habits, all their entries and any pending reminder. No undo. delete "Read" → confirm → habit and its 40 entries gone; cancel → unchanged
HAB-10 Manual reorder is possible only when the list is sorted manually: moving a habit from index i to j shifts the habits between them by one, and positions stay contiguous 0..n−1. order A,B,C,D; move D to index 1 → A,D,B,C, positions 0..3
HAB-11 Sort orders: manual (by position), name, color (palette index), score (today's score), status. Choosing the current order again reverses it. First choice direction: name, color, status ascending; score descending. The previous order becomes the tie-breaker. Default manual, tie-breaker name ascending. Order, tie-breaker and filters (HAB-13) persist across restarts. sort by score → highest first; choose score again → lowest first
HAB-12 status descending: habits completed today first, then measurable before yes/no, then today's value high to low. status ascending is the exact reverse. A done, B not done, sort by status (first choice) → B, A
HAB-13 Filters: show_archived (default false), show_completed (default true). With show_completed off, hide habits completed today (CHK-13); when unknown_enabled is on, hide habits with any value entered today instead. show_completed off, A done today → A hidden
HAB-14 Search keeps habits whose name, question or notes contain the trimmed query, case-insensitive; an empty query keeps all. query " run " → "Morning Run" kept, "Read" hidden

Out of scope

  • Habit groups or categories, sub-habits, shared or public habits.
  • Habit templates and onboarding hints.

Decisions

  • Blank-after-trim names are rejected; the source checked the untrimmed text and could save an empty name.
  • Habit type is fixed after creation, as in the source (it is chosen before the form opens).
  • created_at is added for export clarity; the source has no creation time.

Reminders

Status: Draft · Prefix: REM

Purpose

Local notifications that prompt for a habit at a chosen local time on chosen weekdays, with quick answers and snooze. No server is involved.

Requirements

ID Requirement Verified by
REM-1 Reminder (optional, default off): {hour 0..23, minute 0..59, weekdays: set of mon..sun}. An empty weekday set means every day. A new reminder defaults to 08:00, every day. turn on → 08:00 all days; clear all weekdays → every day
REM-2 A reminder fires at its hour:minute in the device's current time zone on each selected weekday, within 1 minute. 07:30 Mon–Fri → fires Fri 07:30, not Sat
REM-3 When the device time zone or clock changes, the next fire is recomputed for the new local time. Daylight saving: a skipped local time fires at the first minute after the gap; a repeated one fires once, at its first occurrence. move Berlin → New York → 08:00 fires 08:00 New York time; 02:30 on a 02:00→03:00 day → fires 03:00
REM-4 No notification for an archived habit, an unselected weekday, or a habit already completed that day, except at_most habits, which are always reminded. yes/no habit done at 07:00, reminder 08:00 → nothing
REM-5 Notification: title = habit name; text = the question, or a generic "did you do it today?" prompt when empty; opening it shows the habit. Actions: yes/no habits → yes, no; measurable → enter amount; all → snooze. blank question → generic prompt
REM-6 An action writes the entry for the date the reminder fired for, not the date it is tapped. 23:50 reminder, tap yes at 00:10 → entry for previous date
REM-7 At most one notification per habit; it is dismissed when any entry is written for that habit on that date from anywhere, or the habit is deleted. check habit in the list → its notification disappears
REM-8 Snooze: 15, 30, 60, 120, 240, 480 or 1440 minutes, or a chosen clock time (its next occurrence). Snooze dismisses the notification and replaces only the next fire; it is dropped if its time has passed. snooze 30 at 08:00 → fires 08:30, next day back to 08:00
REM-9 With sticky_reminders on, a notification dismissed without an action reappears at once; off by default. setting on, swipe away → shown again
REM-10 Reminders are rescheduled on app start and device restart; a reminder whose time passed while the device was off is not fired late. device off 07:00–09:00, reminder 08:00 → no notification at 09:00

Out of scope

  • Push notifications from a server, email or SMS reminders.
  • Location- or context-based reminders; multiple reminder times per habit.
  • Platform permission flows (exact-alarm, notification permission) beyond: if denied, the habit shows that reminders are off.

Decisions

  • Time-zone and clock-change rescheduling (REM-3) is new: the source kept a scheduled alarm's absolute instant until the next restart or edit.
  • Reminders are local and not @specfork/notifications: that base is per account and delivered by a server.
  • Snooze is offered everywhere; the source hid it on newer platform versions.

Scoring

Status: Draft · Prefix: SCR

Purpose

The habit strength score: one number per habit per day that rises with consistent repetition and decays slowly with misses, so one missed day after a long run barely hurts.

Requirements

ID Requirement Verified by
SCR-1 Each habit has a score in [0, 1] for every day from its start day to today. Start day = the oldest day with a derived state (CHK-7, including covered windows); days before it and habits with no entries score 0. Shown as a whole percent, rounded half-up. score 0.4740 → "47%"; no entries → 0%
SCR-2 Recurrence: score_d = score_(d−1) × m + p_d × (1 − m), with m = 0.5 ^ (√f ÷ 13), f = numerator ÷ denominator of the stored frequency, p_d in [0, 1] the day's progress. The score before the start day is 0, except measurable at_most habits, which start from 1. daily: m = 0.948078, first day yes → 0.051922; weekly (1/7): m = 0.980049
SCR-3 A skip day keeps the previous day's score and is left out of every window below. daily: yes then skip → 0.051922 on both days
SCR-4 Window: the last W non-skip days up to and including day d, counting only days on or after the start day. Yes/no: W = denominator and n = numerator; when f < 1, W = 2 × denominator and n = 2 × numerator. Measurable: W = denominator. 1/7 → W 14, n 2; 3/7 → W 14, n 6; 1/1 → W 1, n 1
SCR-5 Yes/no progress: p_d = min(1, number of yes days in the window ÷ n). auto days count 0. daily, 13 yes in a row from start → 0.500000; then one no → 0.474039
SCR-6 Measurable at_least progress: p_d = min(1, sum ÷ target_value), sum = amounts in the window (unknown = 0); target_value 0 → p_d = 1. daily target 10, first day 5 → 0.025961
SCR-7 Measurable at_most progress: p_d = clamp(2 − sum ÷ target_value, 0, 1); with target_value 0, p_d = 1 when sum = 0, else 0. daily max 10, first day 15 → 0.974039; first day 25 → 0.948078
SCR-8 Scores for all days are recomputed from the start day after any entry or habit change (CHK-14). change frequency 1/1 → 3/7 → every past score uses m = 0.965697
SCR-9 Reference values (daily yes/no, every day yes from the start): day 1 = 5.2%, day 13 = 50.0%, day 30 = 79.8%. Weekly habit, one yes on the start day = 1.0%. compute → 0.051922, 0.500000, 0.798017; weekly 0.009975

Out of scope

  • Configurable score formula or half-life.
  • Comparing scores across habits beyond sorting (HAB-11).

Decisions

  • The doubled window for non-daily yes/no habits is kept from the source: it smooths uneven schedules such as a weekly habit done on different weekdays.
  • Measurable habits use a rolling window of denominator days for the score but calendar periods for completion (CHK-12), as in the source.
  • The source computes scores up to 30 days past today; only days up to today are specified because later days are never shown.

Settings

Status: Draft · Prefix: SET

Purpose

The user's preferences, their defaults and what each one changes.

Requirements

ID Requirement Verified by
SET-1 Settings and defaults: first_weekday: mon..sun (device locale), skip_enabled false, unknown_enabled false, reverse_day_order false, one_tap_toggle false, late_day_start false, theme: system|light|dark (system), sticky_reminders false, reminder_sound (system default), backup_folder (none = private storage), plus list order and filters (HAB-11) and chart buckets (CHT-9). All persist across restarts and are part of the full export (DATA-2). fresh install, US locale → first_weekday sun; theme dark → export has "theme": "dark"
SET-2 first_weekday sets where weeks start for measurable completion, the history calendar and weekly chart buckets. 70/week, switch mon → sun → week sums regroup
SET-3 one_tap_toggle off: tapping a yes/no day opens the entry editor and a long press toggles; on: the reverse. Measurable days always open the editor. setting off, tap → editor opens
SET-4 Turning skip_enabled or unknown_enabled off only removes those choices from toggling and the editor; existing skip and unknown entries stay and keep counting. skip day exists, turn skip off → still hatched and completed
SET-5 Changing late_day_start or first_weekday re-derives today and all periods at once, without restart. 01:00, turn late_day_start on → today becomes yesterday's date

Out of scope

  • Per-habit display settings, custom colour palettes, languages beyond following the device.

Decisions

  • late_day_start keeps the source's fixed 03:00 cutoff; the source needed a restart to apply it, here it applies at once.

Streaks

Status: Draft · Prefix: STK

Purpose

Runs of consecutive completed days: the current one and the best ones.

Requirements

ID Requirement Verified by
STK-1 Streak: {start, end, length} = a maximal run of consecutive completed days (CHK-11, CHK-13) on or before today; length = end − start + 1 days. completed Mar 1–5 and Mar 8 → streaks of 5 and 1
STK-2 Skip days and auto days are completed, so they extend a streak. daily: yes, skip, yes → one streak of 3
STK-3 Current streak = the streak ending today; if today is not completed yet, the streak ending yesterday; otherwise 0. completed Mar 1–5, today Mar 6 not entered → current 5; today Mar 7 → 0
STK-4 Best streaks: the 10 longest streaks; on equal length the one ending later ranks higher; listed newest end first, each with start, end and a bar scaled to the longest. 12 streaks → 10 shown by end date descending; lengths 10, 5 → bars 100%, 50%
STK-5 Streaks are recomputed after any entry or habit change (CHK-14). set Mar 6 to no in a 10-day run → streaks 5 and 4
STK-6 Total check-ins: yes/no habits count yes days (not auto or skip); measurable habits count days with an amount > 0. 3/7 with yes on days 1, 3, 5 → total 3, longest streak 7

Out of scope

  • Streak freezes, rewards, badges or other gamification.

Decisions

  • "Current streak" tolerates a not-yet-entered today; the source only lists streaks and has no current-streak value.
  • Measurable totals count days with an amount > 0; the source counted a raw stored marker that does not apply to measurable habits.

From Specfork/habits at fe536fc, licence CC-BY-4.0.