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¶
- Open Habit Definitions
- New row: Name, Category, Cadence, Goal Frequency, Goal Quantity, Goal Unit, Color, Active? (checked), Linked Area
- 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) whereHit goal = trueCurrent streak: walk back from yesterday, count consecutive days whereHit 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.