What it solves
“When is that meeting, and is it in this sprint?” is a question with no single place to look. The answer is spread across calendar invites you may not be on, a doc someone updated once, and whatever was said in chat — and each of those states a time in somebody’s timezone, rarely yours. A recurring meeting described as “every second sprint week” can’t be resolved at all without knowing the sprint calendar, which lives somewhere else again.
Three things fix that here. Meetings and PIs are one set of data, so there is a single place to look. Recurrence rules are resolved against the actual sprint calendar, so “every second sprint week” becomes real dates rather than an instruction you have to apply yourself. And every time passes through a view-timezone picker, so the schedule is rendered in yours instead of the author’s.
The fourth problem is keeping it current. A hub nobody can edit rots, but a login and a backend are more than this needs — so edits stage in an in-app Changes cart and open a single prefilled PR. No account, no server, and because every change file gets a unique name, two people editing at once cannot conflict.
Recurrence is resolved against the real sprint calendar rather than left as an instruction, and the view-timezone picker means the schedule is rendered in the reader’s clock rather than the author’s. Corrections arrive as pull requests, so the hub stays editable without a server.
What it does
A tabbed hub (header tabs plus subtabs) with two areas:
- Meetings → Meeting Tracker — List, month Calendar with sprint bands, and Categories views; search plus team and sprint/PI filters; times rendered in your chosen timezone.
- Sprints & PIs → PI Roadmap — the program-increment picture.
Recurrence is real: weekly / monthly / sprint / sprint-week / TBD rules resolve to actual dates
against the sprint calendar. Every time is timezone-aware — each meeting carries its own schedule.tz
and a global view-tz picker in the header converts everything, remembered in localStorage.
Where the data comes from
Hand-curated data/meetings.json and data/pis.json, bundled at build time via import.meta.glob.
There is no runtime fetch.
Edits (meetings and PIs) stage in an in-app Changes cart and then open a single prefilled
new-file PR creating dse-hub/data/changes/<ts>.json. The loaders merge those change files over the
canonical arrays at load — upsert by id, newest ts wins — which makes the flow token-free and
conflict-free, since every filename is unique. On merge, dse-hub-compact.yml folds the change files
back into the canonical arrays.
Run it locally
cd dse-hub
npm install
npm run dev
Related
- Tabs, recurrence, timezone handling and the Changes cart:
docs/DSE_HUB.md