Calendar: aggregated range and year endpoints (server side of #39) #56

Closed
opened 2026-09-24 15:42:30 +00:00 by kayg · 9 comments
Owner

Server part of #39 (Calendar mode): a fast aggregated range endpoint so the week/day/month/year/today views load in one request.

GET /api/v1/calendar/range?from=YYYY-MM-DD&to=YYYY-MM-DD&tz=Area/City returns compact per-day buckets: log entries (time, range, title, tags, attachments incl. tasks and notes, zone logged in), external events (from the CalDAV cache once #40 lands: leave a typed provider hook now, empty until then), notes saved (id, title, time), files saved (by mtime/creation, grouped per hour with counts and up to N representative items), photos (image/video files in library roots, grouped per hour with thumbnail hashes), bookmarks (notes with kind bookmark). Also GET /api/v1/calendar/year?year= returning per-day activity counts for the heat map and a photo-day flag, and a per-day cover-photo pick for month cells.
Budgets: week range < 30 ms server time p95 at 100k files; year < 50 ms. Index-backed only, no directory scans on request. Permission-filtered (home + shares).

Context: DESIGN §29–§31, issue #39. Comment on this issue as you work; never close it.

Server part of #39 (Calendar mode): a fast aggregated range endpoint so the week/day/month/year/today views load in one request. `GET /api/v1/calendar/range?from=YYYY-MM-DD&to=YYYY-MM-DD&tz=Area/City` returns compact per-day buckets: log entries (time, range, title, tags, attachments incl. tasks and notes, zone logged in), external events (from the CalDAV cache once #40 lands: leave a typed provider hook now, empty until then), notes saved (id, title, time), files saved (by mtime/creation, grouped per hour with counts and up to N representative items), photos (image/video files in library roots, grouped per hour with thumbnail hashes), bookmarks (notes with kind bookmark). Also `GET /api/v1/calendar/year?year=` returning per-day activity counts for the heat map and a photo-day flag, and a per-day cover-photo pick for month cells. Budgets: week range < 30 ms server time p95 at 100k files; year < 50 ms. Index-backed only, no directory scans on request. Permission-filtered (home + shares). Context: DESIGN §29–§31, issue #39. Comment on this issue as you work; never close it.
Author
Owner

Starting issue #56 in worktree calendar-range on branch job/calendar-range at base SHA 63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f.

Starting issue #56 in worktree calendar-range on branch `job/calendar-range` at base SHA `63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f`.
Author
Owner

Finding: the current Index cannot serve #56's range query as specified. note_items stores only user, id, path, title, and edited; Daily note Log entries remain inside Markdown and are not indexed as rows. files_index stores mtime and MIME but has no (owner_id, modified) index, and Note kind: bookmark is not projected into the Index. I will add a Calendar plugin at the requested /api/v1/calendar paths, add a rebuildable Log/Note projection in the Notes Index, and add the date index for files. The Calendar plugin will validate incoming shared roots through the Files share identity check. No CalDAV event-cache table exists yet, so the new typed provider hook will return an empty list until #40 supplies it.

Finding: the current Index cannot serve #56's range query as specified. `note_items` stores only user, id, path, title, and `edited`; Daily note Log entries remain inside Markdown and are not indexed as rows. `files_index` stores mtime and MIME but has no `(owner_id, modified)` index, and Note `kind: bookmark` is not projected into the Index. I will add a Calendar plugin at the requested `/api/v1/calendar` paths, add a rebuildable Log/Note projection in the Notes Index, and add the date index for files. The Calendar plugin will validate incoming shared roots through the Files share identity check. No CalDAV event-cache table exists yet, so the new typed provider hook will return an empty list until #40 supplies it.
Author
Owner

Finding: parse_attachments does not index the agreed task child format - [ ] → [Title](Tasks/…md). It finds the nested Markdown link after the checkbox and arrow, but the general Note attachment parser requires the link to occupy the whole list-item content. The Calendar projection now recognizes that task format while indexing a Daily note and returns it as an attachment with kind task; the existing Note parser remains unchanged. A regression test covers both task and Note attachments.

Finding: `parse_attachments` does not index the agreed task child format `- [ ] → [Title](Tasks/…md)`. It finds the nested Markdown link after the checkbox and arrow, but the general Note attachment parser requires the link to occupy the whole list-item content. The Calendar projection now recognizes that task format while indexing a Daily note and returns it as an attachment with kind `task`; the existing Note parser remains unchanged. A regression test covers both task and Note attachments.
Author
Owner

The first required workspace Clippy run found three issues in the new Calendar code: load_file_and_note_rows and its test fixture helper exceeded Clippy's argument limit, and event day lookup matched .ok() instead of Ok. I grouped the query bounds and fixture values into structs and matched the Result directly. Re-run CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo clippy --all-targets -- -D warnings passed.

The first required workspace Clippy run found three issues in the new Calendar code: `load_file_and_note_rows` and its test fixture helper exceeded Clippy's argument limit, and event day lookup matched `.ok()` instead of `Ok`. I grouped the query bounds and fixture values into structs and matched the `Result` directly. Re-run `CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo clippy --all-targets -- -D warnings` passed.
Author
Owner

Finished kayg/calternal#56 on job/calendar-range at head 41a4d85c29689d774f73e1f27df4bbfc62ef83a5 (working tree clean).

Gates:

  • CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo fmt --all -- --check — exit 0; no output.
  • CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo clippy --all-targets -- -D warnings — exit 0. Final output:
    Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/calendar-range/crates/calternal-server)
        Checking calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/calendar-range/crates/plugins/calendar-view)
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.74s
    
  • CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo test — exit 0. Calendar/Files/Notes summaries:
    test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s
    test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.41s
    test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.70s
    
  • bash packages/api-client/check-generated.sh — exit 0. Final output:
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 32.93s
    Running `target/debug/calternal-server openapi`
    $ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts
    ✨ openapi-typescript 7.13.0
    🚀 ../../contracts/openapi.json → src/generated.ts [1.3s]
    
  • bash tests/adversarial/run.sh — exit 0. Final output:
    finish 200
    login web 200 installation 200
    cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ]
    A entries after mischief: 200 1
    tus create ok: 201 True
    concurrent PATCH: 1 succeeded of 20
    ok.bin content: 200 10
    mkdir storm: 200 created, 0 errors
    rename race: 1 succeeded, 0 errors
    top-level entries: ['.cas', '.system', 'users']
    server alive at end: True
    
    ==== FINDINGS 0
    

Remaining decisions/gaps for owner confirmation: Photos are identified by Photos/ plus image/video MIME because no library-root configuration API exists yet; other image/video paths are counted as files. File activity uses indexed modification time because the Files Index has no creation-time field. The year endpoint defaults to UTC, detailed hourly groups return up to four representatives, and the newest modified photo is the cover (ties sort by owner then path). Existing Logs with no captured time zone return null; the typed external Event hook is empty until the CalDAV adapter lands.

Finished `kayg/calternal#56` on `job/calendar-range` at head `41a4d85c29689d774f73e1f27df4bbfc62ef83a5` (working tree clean). Gates: - `CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo fmt --all -- --check` — exit 0; no output. - `CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo clippy --all-targets -- -D warnings` — exit 0. Final output: ``` Compiling calternal-server v0.0.1 (/home/kayg/Developer/calternal-wt/calendar-range/crates/calternal-server) Checking calternal-plugin-calendar v0.0.1 (/home/kayg/Developer/calternal-wt/calendar-range/crates/plugins/calendar-view) Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.74s ``` - `CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 cargo test` — exit 0. Calendar/Files/Notes summaries: ``` test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.41s test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.70s ``` - `bash packages/api-client/check-generated.sh` — exit 0. Final output: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 32.93s Running `target/debug/calternal-server openapi` $ bunx --package openapi-typescript@7.13.0 openapi-typescript ../../contracts/openapi.json -o src/generated.ts ✨ openapi-typescript 7.13.0 🚀 ../../contracts/openapi.json → src/generated.ts [1.3s] ``` - `bash tests/adversarial/run.sh` — exit 0. Final output: ``` finish 200 login web 200 installation 200 cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ] A entries after mischief: 200 1 tus create ok: 201 True concurrent PATCH: 1 succeeded of 20 ok.bin content: 200 10 mkdir storm: 200 created, 0 errors rename race: 1 succeeded, 0 errors top-level entries: ['.cas', '.system', 'users'] server alive at end: True ==== FINDINGS 0 ``` Remaining decisions/gaps for owner confirmation: Photos are identified by `Photos/` plus image/video MIME because no library-root configuration API exists yet; other image/video paths are counted as files. File activity uses indexed modification time because the Files Index has no creation-time field. The year endpoint defaults to UTC, detailed hourly groups return up to four representatives, and the newest modified photo is the cover (ties sort by owner then path). Existing Logs with no captured time zone return `null`; the typed external Event hook is empty until the CalDAV adapter lands.
Author
Owner

Calendar Items adversarial evidence from #303 (2026-09-28): the probe uploaded 120 JPEGs under Photos/2026/2026-09-28/burst-*.jpg, then paged the Calendar Items endpoint. It reported expected 120 burst photos, saw 0. The uploads were part of a high-load run with several other file and search fixtures; please check whether this is projection freshness or fixture load.

Calendar Items adversarial evidence from #303 (2026-09-28): the probe uploaded 120 JPEGs under `Photos/2026/2026-09-28/burst-*.jpg`, then paged the Calendar Items endpoint. It reported `expected 120 burst photos, saw 0`. The uploads were part of a high-load run with several other file and search fixtures; please check whether this is projection freshness or fixture load.
Author
Owner

Hygiene review: a real-server Calendar Items probe uploaded 120 Photos items and then received zero from the endpoint. The run did not isolate projection freshness from fixture load, so #56 stays open for a controlled reproduction.

Hygiene review: a real-server Calendar Items probe uploaded 120 Photos items and then received zero from the endpoint. The run did not isolate projection freshness from fixture load, so #56 stays open for a controlled reproduction.
Author
Owner

Already implemented on origin/dev. git log origin/dev --grep='(#56)' shows 57118d964, the merged calendar range and year API. Current crates/plugins/calendar/src/view.rs defines GET /api/v1/calendar/range and GET /api/v1/calendar/year, with the indexed projections in the Calendar and Notes code. This issue is the server part of #39, so I recommend recording the merged implementation here. The wider Calendar UI is outside this finding. Do not close the issue in this audit.

Already implemented on origin/dev. git log origin/dev --grep='(#56)' shows 57118d964, the merged calendar range and year API. Current crates/plugins/calendar/src/view.rs defines GET /api/v1/calendar/range and GET /api/v1/calendar/year, with the indexed projections in the Calendar and Notes code. This issue is the server part of #39, so I recommend recording the merged implementation here. The wider Calendar UI is outside this finding. Do not close the issue in this audit.
Author
Owner

Fixed in 57118d964 (origin/dev); Calendar range and year routes are in crates/plugins/calendar-view, with range abuse coverage in tests/adversarial/attack.py.

Fixed in 57118d964 (origin/dev); Calendar range and year routes are in `crates/plugins/calendar-view`, with range abuse coverage in `tests/adversarial/attack.py`.
kayg closed this issue 2026-10-03 11:55:16 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#56
No description provided.