feat(dashboard): VEVENTs (meetings) alongside VTODOs #4

Open
opened 2026-05-20 13:35:58 +00:00 by mAi · 2 comments
Collaborator

Carried over from the mgmt.msbls.de teardown plan §3 Gap 1.

Gap

The /dashboard Tasks card surfaces VTODOs only. The mgmt cockpit (now slated for teardown) also surfaces VEVENTs from the same Work+Plan calendars, ordered by start-time, with location + categories. "Meetings today" is a real feature projax doesn't yet replicate.

Once this lands, the last functional reason to keep mgmt.msbls.de alive disappears and the §4 teardown sequence can run end-to-end.

Scope

Small. The CalDAV client already lists VEVENTs (caldav.ListVEVENTs); the dashboard handler needs either:

  • a fourth card (Today's meetings), or
  • a merged Today's schedule card that combines VTODOs + VEVENTs ordered by due/start-time.

Sort: chronological — overdue VTODOs first, then today's events by start, then no-due VTODOs at the end.

Reuse the existing 4-worker pool the Tasks card uses (don't hammer the DAV server). Same 60s TTL cache as the rest of the dashboard.

Out of scope

  • Week / quarter VEVENT views — mgmt never shipped these either, parked indefinitely.
  • Writeback on VEVENTs (the cmd/projax CalDAV writeback path is VTODO-only).
  • RRULE expansion — render single occurrences only.

References

  • Teardown plan §3 Gap 1: docs/plans/mgmt-teardown.md on mai/knuth/phase-3k-mgmt-survey.
  • Original superseded design: m/msbls.de#5 (closed).
  • CalDAV client: caldav/caldav.goListVEVENTs already exists.
Carried over from the [mgmt.msbls.de teardown plan](https://mgit.msbls.de/m/projax/src/branch/mai/knuth/phase-3k-mgmt-survey/docs/plans/mgmt-teardown.md) §3 Gap 1. ## Gap The `/dashboard` Tasks card surfaces VTODOs only. The mgmt cockpit (now slated for teardown) also surfaces VEVENTs from the same `Work`+`Plan` calendars, ordered by start-time, with location + categories. "Meetings today" is a real feature projax doesn't yet replicate. Once this lands, the last functional reason to keep mgmt.msbls.de alive disappears and the §4 teardown sequence can run end-to-end. ## Scope Small. The CalDAV client already lists VEVENTs (`caldav.ListVEVENTs`); the dashboard handler needs either: - a fourth card (`Today's meetings`), or - a merged `Today's schedule` card that combines VTODOs + VEVENTs ordered by due/start-time. Sort: chronological — overdue VTODOs first, then today's events by start, then no-due VTODOs at the end. Reuse the existing 4-worker pool the Tasks card uses (don't hammer the DAV server). Same 60s TTL cache as the rest of the dashboard. ## Out of scope - Week / quarter VEVENT views — mgmt never shipped these either, parked indefinitely. - Writeback on VEVENTs (the `cmd/projax` CalDAV writeback path is VTODO-only). - RRULE expansion — render single occurrences only. ## References - Teardown plan §3 Gap 1: `docs/plans/mgmt-teardown.md` on `mai/knuth/phase-3k-mgmt-survey`. - Original superseded design: `m/msbls.de#5` (closed). - CalDAV client: `caldav/caldav.go` — `ListVEVENTs` already exists.
Author
Collaborator

Already shipped — but functionally inert. The real blocker is data, not code.

tl;dr: the code this issue asks for landed in d49ad21 on 2026-05-16, four days before this issue was filed (2026-05-20). But m still sees "No upcoming events" — because the calendars with his meetings (Work, Plan) aren't linked to any projax item. One /admin/caldav action away from done.

Why the issue premise was stale

Carried over from mgmt-teardown.md §3 Gap 1, whose gap list was already out of date when the issue was cut. Two details in the body are wrong:

  • caldav.ListVEVENTs doesn't exist — the real function is ListEvents (caldav/caldav.go:258), a server-side time-range-filtered REPORT.
  • "the dashboard Tasks card shows VTODOs only" — no longer true. There is an Events card and a dedicated Events tab.

What's on main today: dashboardEvent + projectEvents (web/dashboard.go:887), aggregate.Events (internal/aggregate/aggregator.go:253), rendered via dashboard_section.tmpl, grouped by day over a next-7-day window (a superset of "meetings today").

Every constraint in this issue is already satisfied: 4-worker pool (aggregate.defaultWorkers, shared with the Todos path — no new fan-out), 60s TTL, no RRULE expansion (flagged Recurring=true, never expanded), read-only (no VEVENT writeback).

The actual gap — verified end-to-end

I ran the real binary locally against m's live DAV server (auth off, migrations off) and probed the calendars directly:

Calendar VEVENTs next 7d Linked to a projax item?
Plan 9 no
Work 2 no
default 2 no
mhome 0 yes — the only one

The only caldav-list link in the DB points at mhome, which has zero events. So the Events card correctly renders "No events in the next 7 days" — it's working exactly as designed, on an empty input.

The pipeline is provably healthy: mhome has 15 VTODOs / 8 completed = 7 open, and the Tasks card renders exactly 7 rows. Reads work; nothing is linked.

What closes this

The teardown plan already prescribes the answer:

projax's dashboard aggregates every caldav-list-linked calendar, not just Work+Plan. To restrict to those two, m links only those calendars to a "work" projax item — already supported via /admin/caldav.

A work root project already exists. So: link https://dav.msbls.de/dav/calendars/m/Work/ and .../Plan/ to it via /admin/caldav. That surfaces 11 meetings immediately.

Side effect m should know about before clicking: linking also pulls those calendars' VTODOs into the Tasks card — Work adds 7 open tasks, Plan adds 0 (all 15 completed). Modest, and reversible via the unlink button. Flagging it rather than deciding m's data model for him — that's why I didn't link it myself.

This commit

50d0ac5test(dashboard): pin VEVENT sort + day-grouping order (#4)

No card was built; that would have duplicated deployed code. The genuine gap was coverage: projectEvents had zero unit tests — sort/grouping was only exercised indirectly by the render test. Now pinned: start-ascending with summary tiebreak, day-grouping preserving that order, ganztägig for all-day, and RRULE staying one row at the literal DTSTART (so a future expansion attempt fails loudly instead of silently multiplying rows). Verified the assertions fail against an inverted sort — they have teeth.

Shape decision: the issue offered a fourth card or a merged "Today's schedule". The shipped code is the fourth-card route, and that's also what the cost criterion picks: the Tasks card carries done/edit/delete VTODO writeback while VEVENTs are read-only, so merging would park that asymmetry inside one card. Keeping the split.

Teardown status

Per this issue's framing, Gap 1 was the last functional blocker for the mgmt.msbls.de teardown. The code side is done and deployed (healthz bcf3412 == HEAD). The §4 teardown sequence is unblocked once Work+Plan are linked — that link is what makes "meetings today" actually appear, and it's the thing to confirm before trusting the parity claim.

Unrelated pre-existing failures (not from this work)

go build ./... + go vet ./... clean. Beyond the known TestProjectFilter*/TestTimeline* route-drift, I hit two more and verified they fail on clean HEAD with my file removed — pre-existing DB drift, logged by kahn on 2026-06-01, worth filing separately:

  • db: TestBackfillTagsFromArea — "12 live children still empty after 0012"
  • store: TestParityListAll (store=73 vs mbrian=66) + TestParitySpotChecks

Not closing — per protocol only m closes. Labelling needs-review: the code ask is done, but m should decide the Work+Plan link before this counts as delivered.

## Already shipped — but functionally inert. The real blocker is data, not code. **tl;dr:** the code this issue asks for landed in [d49ad21](https://mgit.msbls.de/m/projax/commit/d49ad21) on **2026-05-16**, four days *before* this issue was filed (2026-05-20). But m still sees *"No upcoming events"* — because the calendars with his meetings (`Work`, `Plan`) aren't linked to any projax item. **One `/admin/caldav` action away from done.** ### Why the issue premise was stale Carried over from `mgmt-teardown.md` §3 Gap 1, whose gap list was already out of date when the issue was cut. Two details in the body are wrong: - `caldav.ListVEVENTs` doesn't exist — the real function is `ListEvents` (`caldav/caldav.go:258`), a server-side time-range-filtered REPORT. - "the dashboard Tasks card shows VTODOs only" — no longer true. There is an Events card *and* a dedicated Events tab. What's on main today: `dashboardEvent` + `projectEvents` (`web/dashboard.go:887`), `aggregate.Events` (`internal/aggregate/aggregator.go:253`), rendered via `dashboard_section.tmpl`, grouped by day over a **next-7-day** window (a superset of "meetings today"). Every constraint in this issue is already satisfied: **4-worker pool** (`aggregate.defaultWorkers`, shared with the Todos path — no new fan-out), **60s TTL**, **no RRULE expansion** (flagged `Recurring=true`, never expanded), **read-only** (no VEVENT writeback). ### The actual gap — verified end-to-end I ran the real binary locally against m's live DAV server (auth off, migrations off) and probed the calendars directly: | Calendar | VEVENTs next 7d | Linked to a projax item? | |---|---|---| | `Plan` | **9** | ❌ no | | `Work` | **2** | ❌ no | | `default` | 2 | ❌ no | | `mhome` | **0** | ✅ **yes — the only one** | The *only* `caldav-list` link in the DB points at `mhome`, which has zero events. So the Events card correctly renders "No events in the next 7 days" — it's working exactly as designed, on an empty input. The pipeline is provably healthy: `mhome` has 15 VTODOs / 8 completed = **7 open**, and the Tasks card renders **exactly 7 rows**. Reads work; nothing is linked. ### What closes this The teardown plan already prescribes the answer: > projax's dashboard aggregates *every* `caldav-list`-linked calendar, not just `Work`+`Plan`. To restrict to those two, m links only those calendars to a "work" projax item — already supported via `/admin/caldav`. A **`work` root project already exists**. So: link `https://dav.msbls.de/dav/calendars/m/Work/` and `.../Plan/` to it via `/admin/caldav`. That surfaces **11 meetings** immediately. **Side effect m should know about before clicking:** linking also pulls those calendars' VTODOs into the Tasks card — `Work` adds **7 open** tasks, `Plan` adds **0** (all 15 completed). Modest, and reversible via the unlink button. Flagging it rather than deciding m's data model for him — that's why I didn't link it myself. ### This commit [50d0ac5](https://mgit.msbls.de/m/projax/commit/50d0ac5) — `test(dashboard): pin VEVENT sort + day-grouping order (#4)` No card was built; that would have duplicated deployed code. The genuine gap was coverage: `projectEvents` had **zero** unit tests — sort/grouping was only exercised indirectly by the render test. Now pinned: start-ascending with summary tiebreak, day-grouping preserving that order, `ganztägig` for all-day, and RRULE staying **one** row at the literal DTSTART (so a future expansion attempt fails loudly instead of silently multiplying rows). Verified the assertions fail against an inverted sort — they have teeth. **Shape decision:** the issue offered a fourth card *or* a merged "Today's schedule". The shipped code is the fourth-card route, and that's also what the cost criterion picks: the Tasks card carries done/edit/delete VTODO writeback while VEVENTs are read-only, so merging would park that asymmetry inside one card. Keeping the split. ### Teardown status Per this issue's framing, Gap 1 was the last functional blocker for the **mgmt.msbls.de teardown**. The code side is done and deployed (healthz `bcf3412` == HEAD). The §4 teardown sequence is unblocked **once `Work`+`Plan` are linked** — that link is what makes "meetings today" actually appear, and it's the thing to confirm before trusting the parity claim. ### Unrelated pre-existing failures (not from this work) `go build ./...` + `go vet ./...` clean. Beyond the known `TestProjectFilter*`/`TestTimeline*` route-drift, I hit two more and verified they fail on **clean HEAD with my file removed** — pre-existing DB drift, logged by kahn on 2026-06-01, worth filing separately: - `db`: `TestBackfillTagsFromArea` — "12 live children still empty after 0012" - `store`: `TestParityListAll` (store=73 vs mbrian=66) + `TestParitySpotChecks` *Not closing — per protocol only m closes. Labelling `needs-review`: the code ask is done, but m should decide the `Work`+`Plan` link before this counts as delivered.*
mAi added the
status:needs-review
label 2026-07-17 09:47:18 +00:00
Author
Collaborator

Small disclosure on the label above — m/projax had zero labels defined (no issue in this repo has ever carried one), so there was no needs-review to apply. Rather than skip the status, I created the standard mAi set per docs/gitea-workflow.md in m/otto: status:in-progress / status:done / status:needs-review / status:failed / status:partial. #4 now carries status:needs-review.

I used the status:-prefixed form because that's what m/otto and m/mAi use and what /clean-session expects. Worth flagging that the convention has drifted across repos, though — m/msbls.de uses the bare form (done, needs-review), m/mAi carries both, and gitea-workflow.md itself is inconsistent (its table says needs-review, its lifecycle section says status:needs-review). Not something I'm going to unilaterally standardise across your repos — flagging it for a separate decision.

Revert is trivial if you'd rather projax stayed label-free (DELETE /repos/m/projax/labels/{376..380}).

Small disclosure on the label above — `m/projax` had **zero labels defined** (no issue in this repo has ever carried one), so there was no `needs-review` to apply. Rather than skip the status, I created the standard mAi set per `docs/gitea-workflow.md` in `m/otto`: `status:in-progress` / `status:done` / `status:needs-review` / `status:failed` / `status:partial`. #4 now carries **`status:needs-review`**. I used the `status:`-prefixed form because that's what `m/otto` and `m/mAi` use and what `/clean-session` expects. Worth flagging that the convention has drifted across repos, though — `m/msbls.de` uses the bare form (`done`, `needs-review`), `m/mAi` carries **both**, and `gitea-workflow.md` itself is inconsistent (its table says `needs-review`, its lifecycle section says `status:needs-review`). Not something I'm going to unilaterally standardise across your repos — flagging it for a separate decision. Revert is trivial if you'd rather projax stayed label-free (`DELETE /repos/m/projax/labels/{376..380}`).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/projax#4
No description provided.