Ambient calendar intake is fragile and probably does not work end to end (PR 57) #157
@@ -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.
|
||||
Reference in New Issue
Block a user