parseDT stamped a zoned or floating DTSTART as UTC while every window
around it is built in local time, so the two sides of every comparison
were in different frames. On a +03 box a 22:00 local event parsed as
22:00Z, past the end of the local day, and the whole evening dropped out
of the busy gate and the day plan. A 13:00 Moscow meeting read on a +04
box was recited at 17:00 next to its own printed 13:00.
DTSTART now resolves three ways: a Z suffix is UTC, a TZID is loaded from
the zone database, and a floating value is read in the caller's location.
tzdata is embedded because the deploy image carries none, and a silent
fallback to the box offset is the bug being fixed. FactKey and FactValue
stamp the owner's clock, so the key date the store range-scans is the
same day the plan asks for. FactSummary drops the time tail for callers
that print the hour themselves.
Found in review of #56 and #58.
Maven does not get a work credential. A corp mail or calendar session living on
the homelab ties the box's blast radius to the employer's data, which is the
thing this task exists to refuse. What she reads instead is the signal: an
Android notification-listener on the phone relays meeting notifications over
wg/LAN to POST /api/ambient, and the ones that clearly describe a meeting become
calendar events at source=ambient:notif, confidence 0.6.
The provenance is the point. A notification is evidence about a meeting, not a
reading of a calendar, so it is never indistinguishable from one: it is stored
below full confidence, store.CalendarEvents keeps the source and confidence on
every row it returns, and the query path hedges — "похоже, Планёрка @ 14:00" for
a relayed event, plain text for a CalDAV read.
The parse is deliberately conservative (internal/calendar/ambient.go). It needs
a real clock reading and a summary that is not just that clock reading;
otherwise it stores nothing at all. A bare hour is not a time, an unread count
is not a time, and "срок 2026.08.15" does not offer 08:15 as a meeting — loose
digits in a notification are far more often a badge or a date, and a mailbox of
noise rendered as invented meetings is worse than a gap.
The ingest is off unless configured: no -ambient-token, no route registered. The
token is a shared secret compared in constant time, because the poster is a
background Android service and WebAuthn has no answer for one. The endpoint is
write-only, accepts one shape of write, and cannot read anything back out.
Reposts of the same notification dedupe against the latest fact for that
key+source, the same append-only discipline cmd/mavcaldav follows.
Not shipped: the Android relay app itself, which is a separate artifact and a
device, not Go in this repo.