Dynamic habit aliases (system architecture)¶
The capture/coordinator brain prompt no longer hardcodes habit phrases. Instead, the Habit Definitions DB owns the canonical list, and both bots read it at runtime.
Why¶
Hardcoding "washed face → Wash Face" in the brain prompt meant every new habit Justin added required a code edit. On 2026-04-30 he flagged this: "think about how these systems work together." Coordinator should write the schema, capture should read it.
Pieces¶
| Piece | Location |
|---|---|
| Notion property | Aliases (rich_text) on Habit Definitions DB 3510950b-d7a9-8112-a196-cb92f2e5d7df |
| Tool that writes aliases | tool_create_habit_definition in forge/scripts/forge_telegram_inbox_brain.py (param aliases: list[str] required; Sonnet generates 4-7 phrases per habit) |
| Runtime loader | core.load_active_habits_block() in forge/scripts/forge_telegram_inbox_brain.py, 15-min process cache |
| Prompt injection | _build_xml_prompt in forge/scripts/forge_telegram_brain.py injects the block into <reference_data> for both personas |
| Backfill cron | forge-habits-alias-backfill.timer (daily 03:30 CT, before 04:00 instantiate). Fills aliases on any active def lacking them. Idempotent. |
| One-shot script | forge/scripts/forge_habits_alias_backfill.py (also runnable manually) |
Format injected into prompt¶
Justin's active habits (use the EXACT name in log_habit; match Aliases against his message):
- 💧 Hydrate (Health, Daily, goal 15 cups). Aliases: drank water, had water, cups of water, hydrated, ...
- 🏋️ Gym (Health, 3 days/week, goal 1 session). Aliases: hit the gym, did my workout, lifted, ...
Add-a-habit flow (end-to-end)¶
- Justin tells
@forge_lifeos_coordinator_bot: "add a habit, stretch every morning 5 minutes" - Coordinator brain calls
tool_create_habit_definition(name='Morning Stretch', icon='🧘', category='Health', color='green', cadence_target='Daily', goal_qty=5, goal_unit='minutes', aliases=['stretched', 'did my stretch', 'morning stretching', ...]). - Tool creates the def with full schema + aliases + icon, AND creates today's log row.
- Capture brain's 15-min cache refreshes within 15 minutes (or instantly on next bot restart). If aliases were omitted, the 03:30 cron fills them overnight.
- Justin texts
@forge_inbox_capture_bot: "stretched this morning". Brain matches against the alias list, callslog_habit(habit_name='Morning Stretch').
Failure modes + safeguards¶
- Cache stale.
load_active_habits_block()15-min TTL means a brand-new habit might not be recognized for up to 15 min. Bot restart orforce=Trueinvalidates immediately. - Notion query fails. Loader returns last-good cache, else a stub. Bot falls back to fuzzy
title containsmatching onlog_habit. - Sonnet writes empty aliases. Backfill cron catches it the next 03:30 CT. Tool description requires aliases param; brain prompt won't omit it.
- Conflicting aliases between morning/nightly variants (e.g. "brushed teeth" matches both Brush Teeth defs). Brain prompt rule disambiguates by time of day.
Extending the pattern¶
Same shape applies to anything with a fixed taxonomy: food categories, time-block categories, knowledge tags. Each gets a Notion-side canonical list with aliases, a runtime loader, and a prompt-injection slot. Coordinator writes once, capture reads every session.
[Claude Code]