docs: decide the ambient calendar path — keep it, change the contract (V-432)
The task's confirmed defect is out of date.4e4c917added day words and a past-grace refusal, so "завтра в 15:00" dates correctly, and45a5e37(V-482, this week) fixed a zone bug the task did not know about. What is left is explicit dates ("5 августа"), which fail safe by being dropped rather than stored on the wrong day. The task's third question also has an answer: both readers hedge, plan.go:174 prefixes "похоже, ". Everything else hangs on one question that this repo cannot answer, so the doc names it as his: can the relay app read Android's calendar provider, or only the notification text? A NotificationListenerService sees a title and a body and cannot know a meeting's real start, so if that is all there is, free-text parsing here is not a choice. If it can read CalendarContract, the parser stops being necessary and nothing is inferred at all. Reading the phone's calendar does not break the design constraint, which is about holding a work credential on the homelab. Decision: keep the endpoint, make a structured event the primary shape, keep the free-text parse as the degraded path, delete only if the relay is not being built. And do not patch the date parser first — that is the patch the task explicitly refuses as closure, and it is the wrong order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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