Commit graph

2 commits

Author SHA1 Message Date
randogoth
0c0558c24c Let an event choose which source it is filed under
An event created in an aggregate went to the configured default_sink and nowhere
else, so with several writable sources there was no way to say which calendar a
new event belonged in.

A line reading @endpoint-id in the description now picks the source, and a
matching CATEGORIES value does too. The description rather than the title because
every calendar client exposes a notes field and editing it does not disfigure the
event's name; CATEGORIES as well because that is the field iCalendar intends,
even though many mobile clients hide it. The marker is stripped before the event
reaches the calendar, being calcalist's bookkeeping rather than content.

A marker naming something that is not a writable source of that aggregate is
refused and reported, not redirected to the default: a typo should not quietly
file an event in the wrong calendar. A bare address in prose is not a marker
either, since a marker must be a line of its own.

Also covers the shapes beyond many-into-one: a source feeding several aggregates,
several aggregates sharing one target, a cycle between two aggregates, a delete
cascading across aggregates, and competing edits arriving through two aggregates
at once — the last being caught by the existing conflict detection rather than
silently overwriting.

Error display no longer repeats itself; thiserror already prints the cause chain.

Verified live against real accounts: two Google calendars aggregating into a
Posteo calendar, an edit in the aggregate reaching the originating Google
calendar, an event routed to a chosen source by its description marker, and a
deletion propagating from the aggregate through to Google.

129 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 13:38:30 +03:00
randogoth
2bc93262f8 Scaffold calcalist: repo, toolchain, config model and doctor
Initialise the project per the approved implementation plan (M0).

- Pin the toolchain with devbox: Rust 1.97.1, pimsync 0.5.11, jujutsu 0.44.0,
  and radicale 3.7.8 for later integration tests.
- Add the configuration model with full referential validation, reporting
  every problem in one pass rather than short-circuiting on the first.
- Add `calcalist doctor`, verifying pimsync's presence and version series,
  the state directory, and the configuration.
- Define the whole CLI surface; only `doctor` acts, the rest exit 2 rather
  than pretending to work.
- Rewrite SPECS.md: Rust naming conventions in place of the JS/TS style
  lines, a `devbox run check` gate instead of a pre-commit hook (jj runs no
  git hooks), and the sync semantics the spec previously left unstated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 09:31:32 +03:00