feat: calendars that stay current, a daemon that stays running, and the email after the meeting - #14
Merged
Merged
Conversation
…he email after the meeting Six things were listed as missing and are here. Each was built against the running artefact, and two of them only revealed their real bug that way. **Calendar subscriptions.** Adding a calendar meant typing the path of an `.ics` file, which a browser cannot produce and which is a snapshot — the agenda described whatever the calendar looked like on export day, and kept describing it. Now a URL: Google's secret iCal address, Apple's public calendar, an Outlook publish link. The daemon re-fetches every fifteen minutes and on startup, keeps the file it already has when a fetch fails, and shows the failure on the row, because a subscription that stopped working looks exactly like a week with no meetings. Deliberately not OAuth. That would mean a client secret inside an open-source binary anyone can read, plus an account-wide scope, so a notes app can learn what time the standup is. A URL grants one calendar and is revocable from the calendar's own settings. `file://`, `ftp://` and bare paths are refused: the daemon fetches this with the user's permissions, and "fetches whatever string it is handed" is how a local-first app becomes a way to read the disk. **"Your meeting is starting — take notes?"** A nudge in the five minutes before an event and the ten after, once per occurrence, never while recording, and only when `suggest_on_meeting` is on. Its button starts the recording instead of navigating, since walking the user to the record button wastes the minute the prompt exists to save. Nothing here ever records on its own; the therapy appointment in a work calendar is why. **A daemon that outlives the terminal.** `summo serve --background`, `summo status`, `summo stop`. Stopping is an HTTP request rather than a signal, so the daemon can refuse while a meeting is being recorded (`--force` overrides), and so it is the same code on Windows. `engine.json` stays the only record of a running daemon, and is removed on the way out rather than left for the next command to discover as stale. **The follow-up you have to send.** An email, a chat message, a recap for people who missed it, or the decisions and actions as a list — from the confirmed summary, not the raw transcript. Nothing is sent: it is editable text with copy, `mailto:` and keep-as-a- note. Running it end to end through a fake provider caught the prompt inheriting the summariser's ground rules, so the first draft was a polite customer email with `[t=03:12]` in the middle of it. **Notes that start in a shape.** Idea, decision, to-do, journal — blank first, since most notes are. Driving that in a browser found a data-loss bug that predates it: the notes editor showed only the text above the first `##`, and saving wrote that back *beside* the sections it had never displayed, duplicating them on every save. `note::as_text` and `note::split_sections` fix it, with a round-trip test. **Where a cloud would go**, as ADR 0007: a relay that stores bytes it cannot read, sync before sharing before teams, cloud models as a setting rather than a tier, and no account for the local app, ever. Also: the path-traversal test asserted a case no client can send — every URL parser collapses `..` before the request leaves — so it failed in any build with the interface bundled, which CI never builds. It now tests the encoded forms that do arrive intact. Verified: 1300+ Rust tests, 297 web tests, 17 browser suites including two new ones, and a real run — a calendar served over HTTP, subscribed, refreshed, broken, and unsubscribed; a background daemon started, queried and stopped; an email composed through a stub provider and saved as a note.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six things were on the list of what was missing. All six are here, each built against the running artefact rather than against the tests alone — which is how two of them revealed the bug that mattered.
Calendar subscriptions
Adding a calendar meant typing the path of an
.icsfile: something a browser cannot produce, and a snapshot besides — the agenda described whatever the calendar looked like on export day and quietly kept describing it.Now a URL. Google's secret address in iCal format, Apple's public calendar, an Outlook publish link;
webcal://is rewritten rather than refused, because that is the scheme Apple and Outlook actually hand out. The daemon re-fetches on startup and every fifteen minutes, and:file://,ftp://and bare paths. The daemon fetches this with the user's permissions, and "fetches whatever string it is handed" is how a local-first app becomes a way to read the disk.No OAuth, on purpose. It would mean a client secret inside an open-source binary anyone can read, plus an account-wide scope, so that a notes app can learn what time the standup is. A URL grants exactly one calendar and is revocable from the calendar's own settings.
"Your meeting is starting — take notes?"
A nudge in the five minutes before an event and the ten after, once per occurrence, never while recording, and only when
suggest_on_meetingis on. Its button starts the recording rather than navigating, since walking the user to the record button wastes the minute the prompt exists to save. Nothing here records on its own, and nothing here should be changed so that it can.A daemon that outlives the terminal
summo serve --background,summo status,summo stop.Stopping is an HTTP request, not a signal: the daemon can then refuse while a meeting is being recorded (
--forceoverrides), and it is the same code on Windows.engine.jsonstays the only record of a running daemon, and is removed on the way out rather than left for the next command to find and mistrust.The follow-up you have to send
An email, a chat message, a recap for people who missed it, or the decisions and actions as a list — written from the confirmed summary, not the raw transcript, because the summary is the version of events the user agreed with.
Nothing is sent. It is editable text with copy, a
mailto:built from what is on screen, and keep-as-a-note.Running it end to end through a stub provider caught the prompt inheriting the summariser's ground rules — so the first draft was a perfectly polite customer email with
[t=03:12]in the middle of it.Notes that start in a shape
Idea, decision, to-do, journal. Blank first, since most notes are.
Driving that in a browser found a data-loss bug that predates it: the notes editor showed only the text above the first
##, and saving wrote that back beside the sections it had never displayed — duplicating them on every save, invisibly. Fixed innote::as_text/note::split_sections, with a round-trip test.Where a cloud would go
ADR 0007. A relay that stores bytes it cannot read; sync before sharing before teams; cloud models as a setting rather than a tier; and no account required for the local app, ever.
Also
The path-traversal test asserted a case no client can send — every URL parser collapses
..before the request leaves — so it failed in any build with the interface bundled, which CI never builds. It now tests the encoded forms that do arrive intact.Verified
cargo fmt --check,clippy -D warnings, the whole Rust suite, 297 web tests, and 17 browser suites includinge2e/calendar.mjsande2e/notes.mjs.And for real, against the built binary: a calendar served over HTTP — subscribed, refreshed, broken mid-flight, unsubscribed; a background daemon started, queried and stopped; an email composed through a stub provider and saved as a note.
🤖 Generated with Claude Code