Skip to content

Habits crons + Option B schema

What it does

Every day at 04:00 America/Chicago, forge-habits-instantiate.service runs forge_habits_instantiate.py and: 1. Queries Habit Definitions DB where Active? = true 2. Queries Habits log DB for today's existing rows 3. For each Active Definition without a today-row, creates a blank Habits row (Done? = false, Quantity null, Habit = relation to Definition)

Justin then opens Habits "Today" view (Date = today, board grouped by Habit) on iPhone or Mac and ticks each card as he completes habits during the day.

Why this shape

TickTick instantiates daily checkboxes per habit; closes the loop with a tap. Notion has no native equivalent without manual row creation. The cron makes Notion mobile feel TickTick-like by pre-creating the rows, so Justin's interaction is just toggle + edit Quantity, no row creation.

Idempotent: re-running mid-day won't create duplicates (filters by Date + Habit relation).

Adding a new habit

  1. Open Habit Definitions
  2. New row: Name, Category, Cadence, Goal Frequency, Goal Quantity, Goal Unit, Color, Active? (checked), Linked Area
  3. Done. Tomorrow's 04:00 cron picks it up automatically.

Removing or pausing a habit

Toggle Active? = false on its Habit Definition. Cron will skip it next day. Existing log rows stay intact for history.

How brain interacts (post bot-refiner fixes)

tool_log_habit(habit_name, done, quantity, notes) finds today's cron-created row by Habit relation + Date = today and PATCHes Done?, Quantity, Notes onto it. If the row doesn't exist (cron hasn't run, or you added the habit today after 04:00), it falls back to creating a row.

Prevents the dual-source problem where brain creates new rows alongside cron-created ones.

Operational reference

Thing Path / value
Instantiate script forge/scripts/forge_habits_instantiate.py
Instantiate service / timer forge-habits-instantiate.{service,timer} (oneshot, daily 04:00 CT)
Streaks script forge/scripts/forge_habits_streaks.py
Streaks service / timer forge-habits-streaks.{service,timer} (oneshot, daily 23:55 CT)
Logs journalctl -u forge-habits-instantiate -u forge-habits-streaks -n 100
Force instantiate sudo systemctl start forge-habits-instantiate.service
Force streaks sudo systemctl start forge-habits-streaks.service
Habits log DB id 3500950b-d7a9-8125-8571-ca97e2369ea6
Habit Definitions DB id 3510950b-d7a9-8112-a196-cb92f2e5d7df

Schema (Option B, quantity-driven)

Habits log

Property Type Source
Name title cron sets to "{date}, {habit}"
Habit relation -> Habit Definitions cron sets to def_id
Date date cron sets to today
Done? checkbox legacy; kept but not primary signal
Quantity number user enters during the day
Notes rich_text user
Goal Quantity (target) rollup, sum of Habit.Goal Quantity auto
Goal Unit (target) rollup, show_original of Habit.Goal Unit auto
Hit goal formula: prop("Quantity") >= prop("Goal Quantity (target)") auto
Display formula: format(qty) + " / " + format(goal) + " " + format(unit) auto, shows "5 / 15 cups"
Category select inherited from definition

Habit Definitions

Property Type Source
Name title manual
Category select manual
Cadence select (Daily / Weekly / Custom) manual
Goal Frequency select (Daily / N days/week / Weekly / Custom) manual
Goal Quantity number manual
Goal Unit rich_text manual
Reminder Time rich_text manual (display only; no notification system yet)
Color select manual
Active? checkbox manual; toggle off to pause without deleting
Linked Area relation -> Areas manual
Days per week target number 1-7 derived from Goal Frequency on creation
Days hit this week number streaks cron writes
Current streak number streaks cron writes
On track this week formula: prop("Days hit this week") >= prop("Days per week target") auto

Streak / weekly logic (forge_habits_streaks.py)

  • Walk Habits log for each habit, last 90 days
  • Days hit this week: count days in current ISO week (Mon-today inclusive) where Hit goal = true
  • Current streak: walk back from yesterday, count consecutive days where Hit goal = true. Stops at first miss. Caps at 90.
  • For Weekly habits, "Current streak" reads as days-not-weeks; week-streak is a future enhancement.
  • Today's incomplete state is intentionally excluded from streak (you might still hit it later in the day).

Mobile UX setup (Justin clicks once)

In Notion app, open the Habits DB and create three views: - Today (default): filter Date = today, group by Habit relation, layout Board - Calendar: layout Calendar by Date (gives the month-grid green/red TickTick visual) - By habit: group by Habit, sort Date desc (history per habit)

Pin the Habits DB to Favorites. Today view becomes the daily-tick surface.