fix(calendar-tool): anchor today's weekday in prompts + verify expected_weekday on create/update

A user asked Fable to schedule "this Friday at 8am" on Wednesday 4/29
2026. The model picked 4/30 (Thursday) and confidently labeled it
"Friday." The TZ pipeline did everything correctly given the model's
date — the bug was upstream: the model was guessing weekdays from ISO
dates without an anchor, and the calendar tools had no way to verify.

Three layered fixes:

1. **System prompts now name the weekday alongside the ISO date.**
   Both the journal-conversation prompt and the general chat prompt
   used to say "Today is 2026-04-29 (America/New_York)." They now say
   "Today is Wednesday, 2026-04-29 (...)." LLMs are unreliable at
   deriving weekday names from ISO dates; supplying the name removes
   the guess.

2. **`expected_weekday` parameter on create_event / update_event.**
   When the model passes `expected_weekday="friday"`, the backend
   computes the resolved start_date's weekday in the user's local
   timezone and rejects mismatches with a self-correcting error
   ("Date 2026-04-30 falls on Thursday, not Friday. Recompute..."),
   without creating the event. The check is local-aware: a Friday
   23:00 event in Tokyo crosses midnight UTC but the local view
   stays Friday, and the validator respects that.

3. **Tool descriptions instruct echo-and-confirm.** create_event and
   update_event descriptions now tell the model: when the user names
   a weekday, state the resolved date in the reply BEFORE calling
   the tool, and pass `expected_weekday`. Costs nothing in code,
   reinforces the validator.

6 new tests — match success, mismatch rejection (with create/update
not invoked), omitted-param backcompat, invalid weekday name, local-
not-UTC weekday computation, and the update_event variant. All 18
calendar-tool tests + 33 event-related tests pass; ruff clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 08:43:32 -04:00
parent 611c940527
commit 03d725ea3e
4 changed files with 276 additions and 5 deletions
+7 -2
View File
@@ -602,7 +602,12 @@ async def build_context(
}
assistant_name = await get_setting(user_id, "assistant_name", "Fable")
today = date_type.today().isoformat()
_today_obj = date_type.today()
today = _today_obj.isoformat()
# Day-of-week paired with the ISO date so the model doesn't have to
# derive the weekday — that derivation is a documented failure mode
# for "this Friday" / "next Monday"-style requests.
today_weekday = _today_obj.strftime("%A")
has_caldav = await is_caldav_configured(user_id)
# Build tool usage guidance based on available integrations
@@ -678,7 +683,7 @@ async def build_context(
entities_context = await get_people_and_places_context(user_id)
entities_section = f"\n\n{entities_context}" if entities_context else ""
dynamic_tail = f"\n\nToday's date is {today}.{tz_line}{profile_section}{entities_section}"
dynamic_tail = f"\n\nToday is {today_weekday}, {today}.{tz_line}{profile_section}{entities_section}"
# --- System message: stable content only ---
# Workspace context and history summary stay here because they carry