From 80ebb74e4f3f35c2204ba5fee8c48bf5d18f0559 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 18 Jul 2026 18:22:32 -0500 Subject: feat(daily-prep): add upcoming-birthdays block from contacts.org New stdlib-only upcoming_birthdays.py reads ~/sync/org/contacts.org for :BIRTHDAY: properties and prints a daily-prep block: name, date, days-away, and the age turned when the birth year is known (1900 is the org-contacts unknown-year placeholder, rendered date-only). Birthdays inside 7 days come back flagged so a gift or plan gets prompted. 19 pytest cases cover the window boundaries, leap-day fallback to Feb 28, the placeholder year, and the CLI. daily-prep gains a Phase A source that runs the script and a Heads-Up line that folds in the block, so contact birthdays the calendar doesn't carry still surface. Built and tested in the home project. Promoted here because the daily-prep hook lives in this synced workflow and the contacts file is present on every machine. --- .ai/workflows/daily-prep.org | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to '.ai/workflows') diff --git a/.ai/workflows/daily-prep.org b/.ai/workflows/daily-prep.org index c708410..3f21214 100644 --- a/.ai/workflows/daily-prep.org +++ b/.ai/workflows/daily-prep.org @@ -74,7 +74,7 @@ The separate =* Standup Briefs= and =* Upcoming Deadlines= sections are *retired Items that frame the day. Four standing items are *always* present, plus the look-ahead: 1. *Meeting-density framing.* One line on the day's shape, e.g. "Meeting-dense morning: 09:00 team discussion, 10:00 standup, 11:00 general standup. Real focus time only opens at noon." -2. *Calendar events from BOTH calendars* (work + personal): birthdays, holidays, anniversaries, vacations, trips, big events. "Your trip begins Friday." "It's 's birthday today." +2. *Calendar events from BOTH calendars* (work + personal): birthdays, holidays, anniversaries, vacations, trips, big events. "Your trip begins Friday." "It's 's birthday today." Fold in the =upcoming_birthdays.py= block from Phase A (source 8) for contact birthdays the calendar doesn't carry; keep its callout entries (within 7 days) so a gift or plan gets prompted. 3. *Reminders due, imminently due, or past trigger* — from notes.org Active Reminders plus scheduled/deadline tasks. A reminder tied to a rescheduled meeting reports against the new date. 4. *Requested metrics* — a slot for any metric Craig has asked to track in the daily prep. Render the slot only when a metric is active; none are active by default. (Metric design is a separate discussion — don't invent metrics.) 5. *5-Day Look-Ahead* — one day per line, format =Fri 12:= / =Mon 15:=, including clear days marked =clear=. Built by Phase 1's forward scan with the invite quick-read and decline gate applied. @@ -192,6 +192,7 @@ Pull every source in a *single batch of parallel tool calls*: 5. Pull the project tracker's view of Craig's plate (assigned issues, items in review, blocked items) where the project has one. 6. List + read the *previous* prep doc. Glob =daily-prep/*-daily-prep.org=, sort by date, take the file *before* the one the root =daily-prep.org= symlink resolves to. If the symlink doesn't resolve yet, take the most recent file. The standup lookback anchors on this file's date. 7. Read the most recent =.ai/sessions/= summary (for the standup brief's lookback). +8. Run =.ai/scripts/upcoming_birthdays.py= (reads =~/sync/org/contacts.org=). Its block feeds the Heads-Up birthday line — contacts carry birthdays the calendar doesn't, and anything inside 7 days comes back flagged so a gift/plan gets prompted. This fetch *is* the live calendar read for build time. In Update mode, re-run the calendar fetches — never reuse the build-time snapshot. -- cgit v1.2.3