diff --git a/docs/plans/16-ambient-calendar.md b/docs/plans/16-ambient-calendar.md new file mode 100644 index 0000000..9478c6a --- /dev/null +++ b/docs/plans/16-ambient-calendar.md @@ -0,0 +1,87 @@ +# Plan: What the ambient calendar path should be + +**The decision Vikunja #432 asked for. Written 04-08-2026.** + +**Verdict: keep the endpoint, change the contract.** The relay app sends structured +fields, not a notification blob. The free-text parser stays as the degraded path, because +there is a real case where the phone cannot produce structure. Delete the endpoint only +if the answer to the one open question below is no. + +## First, the task's premise is out of date + +#432 states as confirmed that every ambient event lands on the day the notification was +posted, because there is no date parsing at all. That was true when the task was filed +and it is not true now. + +`4e4c917` added `dayWords` and `dayOffset` (`internal/calendar/ambient.go:53`), so +"завтра в 15:00" now dates to tomorrow. The same commit added `ambientPastGrace`, which +refuses an event landing more than two hours before the notification, on the reasoning +that the day was inferred and a stale inference is wrong rather than late. `45a5e37` +(#482, this week) fixed a second dating bug the task did not know about: the wall clock +was resolved against the notification's own zone, so every ambient meeting on a non-UTC +box landed off by the deploy's UTC offset. + +What is still missing is an explicit date. "5 августа в 15:00" and "12.08 15:00" carry no +day word, so they date to today and the past-grace check drops them. That is a smaller +defect than the one filed, and it fails safe rather than storing a wrong meeting. + +The task's third question also has an answer, and the answer is yes. `internal/morning/plan.go:174` +prefixes an uncertain item with "похоже, " and `cmd/mavend/actions_query.go:334` carries +`Confidence < 1.0` into the calendar recital. Both readers hedge. + +## The open question, and it is the owner's + +**Can the phone read Android's calendar provider, or only the notification text?** + +Everything follows from this and nothing in this repo can answer it. + +A `NotificationListenerService` sees a title and a body. It cannot know a meeting's real +start, end or organiser, because those are not in the notification. So if the relay is +limited to the notification stream, free-text parsing on this side is not a choice, it is +the only thing available, and #432's suggestion that the phone send structured JSON +cannot be honoured. + +If the app may instead read `CalendarContract`, it has the actual event rows, and the +whole parser stops being necessary. That is the better shape by a wide margin: a real +start and end, a real title, an explicit date, no clock-reading heuristic and no +past-grace guard, because nothing is inferred. + +Reading the phone's calendar provider does not break the constraint the design was built +around. The refusal in `internal/calendar/ambient.go:10` is about holding a work +credential **on the homelab**, which is what ties the box's blast radius to the employer. +The phone already holds that session. Nothing new lands on homesrv either way. + +**Assumption, and it needs his answer:** a managed work profile may block a third-party +app from reading work calendar rows. If it does, the notification stream is all there is. + +## The decision + +**Keep the endpoint.** Deleting it costs the parser, the tests and the wg-facing token, +and buys nothing while the question above is open. It is off unless `-ambient-token` is +set, so an unbuilt relay carries no surface today. + +**Make structured the primary shape.** `/api/ambient` should accept an event with an +explicit start, end and title, and store it without parsing anything. Confidence stays +below 1.0 and the source stays `ambient:notif`, because the provenance claim is unchanged: +this is the phone telling Maven what it sees, not Maven reading a calendar. + +**Keep the free-text shape as the degraded path.** It is what a notification-only relay +can send, and it is already written and tested. + +**Delete it instead if** the relay is not going to be built. That is the one answer that +closes this without code, and it is his to give. + +## What not to do + +Do not add date parsing to the free-text path yet. That is the patch #432 explicitly +refuses to accept as closure, and it is the wrong order: if the relay can send a date, no +date parser is needed, and if it cannot, the parser is guessing at a date from text that +was never meant to carry one. + +## Follow-on, unrelated to the decision + +`cmd/mavweb/ambient.go:33` records a known gap worth keeping visible: an ambient meeting +writes `calendar_event_*` and never `calendar_busy`, so it is good enough to recite and +not good enough to suppress a nudge. That is backwards. Suppressing a nudge is the +lower-risk use of a low-confidence signal, and reciting one is the higher-risk use. It +needs an expiry on the busy level, so it is its own task either way.