nspiration
Getting a group together is hard; getting a group together across a split campus is harder. Polls tell you when people are free but not where they'll be — so we built BookMi, a scheduler that treats location as a first-class citizen. North campus, Central campus, or remote — it figures out which times work and which side of campus makes sense.
What it does
BookMi lets a group drop an event link, paint their availability on a shared grid, and get ranked time suggestions with campus labels (North / Central / Either / Remote), including alternatives that account for travel time between campuses. Participants can optionally link Google Calendar to auto-fill their availability — and rather than guessing with keyword rules, each event is classified by Jev, TypeSafe's System One model, into busy, flexible (movable — still counts as available), preferred (deliberate work time — also painted into the preferred grid), or ignore (reminders, deadlines, noise).
How we built it
Next.js + TypeScript on the frontend, Prisma + SQLite for storage, shareable event links instead of accounts — no sign-up friction. The availability logic lives in pure, heavily unit-tested functions. For calendar import we run an OAuth 2.0 flow with signed state, fetch the calendars the user can write on, then send every event to Jev — one typed choice question per event batched into a single API call. Jev returns structured judgments with calibrated confidence; answers below our confidence threshold (and anything unreachable) fall back to deterministic rules, so the feature degrades gracefully.
Challenges we ran into
Calendar relevance was the hard part: a real calendar mixes genuine commitments, subscribed feeds like TA office hours, and noise like "application opens" reminders that would block the whole day. Access-role filtering (writable calendars by default, a manual picker on top) handled the calendar-level problem; event-level ambiguity — is "group work session" a commitment or flexible time? — is what Jev's semantic judgments handle, where the regex couldn't. We also needed a third availability state: preferred blocks now write into a separate grid round.
Accomplishments that we're proud of
The import holds up on real data — Jev blocked classes and coaching sessions with high confidence while correctly ignoring a deadline reminder that keyword rules treated as busy. The whole flow stays optional and one-click: no accounts, link Google or don't. And imported marks coexist cleanly with hand-painted ones across re-syncs and unlinking.
What we learned
Typed, confidence-carrying AI primitives fit real app logic better than prompt-and-parse: we kept deterministic rules for what's deterministic and gave the model exactly one narrow judgment per event, with confidence as the escape hatch. Also — developer experience matters: caching a database client on globalThis seems clever until it silently holds a stale schema.
What's next for bookmi
Deploying for real (off localhost SQLite), writing the chosen meeting back to participants' calendars, and two-way sync for schedule changes. On the AI side, Jev's score primitive could rank suggested times by group fit, and confidence-gated review could flag genuinely ambiguous events instead of guessing.
Built With
- eslint
- google-cloud
- jev
- next.js
- playwright
- prisma
- react
- sqlite
- tailwind
- typesafe-ai
- vitest
- zod
Log in or sign up for Devpost to join the conversation.