# calcalist Aggregates events between CalDAV, Google Calendar and iCal feeds. `README.md` is the user-facing guide; the design rationale lives in the module doc comments, beside the code it governs. These conventions were `SPECS.md`'s, which has been retired now the roadmap is finished. Git history holds it, and `TODO.md`, if the record is ever wanted. ## Development Use devbox for dependency management and scripts. Use jj for version control (colocated with git). - `devbox run fmt` — format. - `devbox run check` — format check, clippy with warnings denied, and tests. This is the gate; jj has no commit step to hang a hook off, since the working copy is itself a commit. - `devbox run test` — tests only. Keep `/target` in `.gitignore`: jj snapshots the working copy on every command, with no staging step. The integration tests in `tests/` need `radicale` and `pimsync`, both of which devbox provides. Outside that shell they report what is missing and pass. ## Coding style - Simple, readable, idiomatic. - Explicit types for public APIs and important fields. - Prefer immutable data structures. - No global mutable state. - No speculative features or dependencies. - Keep functions short and single-purpose; extract helpers to avoid nesting. - File and module naming: `snake_case.rs`. Types and traits: `PascalCase`. Functions, variables and fields: `snake_case`. Constants: `SCREAMING_SNAKE_CASE`. - Run `devbox run check` before describing a change. - Comment only when intent is not obvious from the code. - Prefer composition and traits over deep type hierarchies. ## Decisions already taken Settled after real design work. Reopen only with new information, not from first principles. **No configuration web interface.** Designed in full, then dropped. Its audience would be people who find TOML hard — but every user must first create a Google Cloud project, configure a consent screen and put a secret in a keyring, which is a far higher bar than editing the config. Anyone who clears it can edit the file; anyone who cannot never reaches the file. `doctor`, `status` and `discover` cover what the interface was actually for. **Bring-your-own OAuth client, not a shipped verified one.** Verification for the Calendar scope needs a domain you own — a `github.io` address is rejected — and Calendar API quota is per Cloud project, with charges announced for exceeding it. A shipped client would pool every user's syncing into the publisher's project, and their bill. Shipping an *unverified* shared client is worse still: the 100-new-user cap is permanent for the project's lifetime. **pimsync cannot reach Google, and never will.** It has no REST storage, and its config exposes only HTTP Basic auth, which Google's CalDAV endpoint rejected in March 2025. The Google leg is calcalist's own in every design. **The config file is never machine-rewritten.** It is the portable, hand-editable artefact. Anything that writes it must preserve comments, ordering and omitted defaults — `toml::to_string` on the `Serialize` derive does none of those things. **WebCal and Google are calcalist's own; CalDAV is being taken back too.** A feed is one iCalendar document fetched over HTTP and read-only by protocol, so routing it through a generated config, a subprocess and a status database bought nothing — it cost three minutes a cycle in observed stalls. CalDAV has followed, behind a per-endpoint `native` flag so the two run side by side until the native client has earned the default. The argument is not that pimsync is bad but that calcalist was paying for generality it never used: one server, against 528 lines of adapter, a 59.6 MB flake closure, an undocumented `discover` format that is scraped, a version range to maintain, and a `vstorage` bug that unlinked `order` relative to the working directory. **Radicale is the CalDAV target; other servers are untested, not unsupported.** Narrowing is what makes owning the protocol reasonable: the integration tests already run against Radicale, so every assumption is checked on every `devbox run check`, and when the RFC is ambiguous its source settles it. The discipline that keeps this honest is that server-specific accommodations stay *named*, never absorbed into the general path — Radicale answers an initial `sync-collection` with a token and no members, which is written as "always load in full first, then go incremental", correct anywhere, rather than as a quirk worked around in place. `doctor` reports what a server advertises so that pointing calcalist elsewhere fails legibly. ## Known gap A failing `pimsync sync` stops every CalDAV endpoint pimsync still owns. It is one process covering every pair, and a failure does not say which pair it belongs to, so the endpoints cannot be told apart the way the Google ones are. `native = true` takes an endpoint out of its reach entirely, which is the way out rather than a narrowing of pimsync's own reporting.