Inspiration
Every day starts the same way: open Teams, get hit with a wall of messages, and lose track of who's actually waiting on you versus who's just looping you in. We wanted a signal you could read from across the room, without opening the app.
At the same time, we noticed the more honest tell of a wasted afternoon isn't the calendar, it's social Media. We wanted one character that could hold both truths at once: are you behind on people, and are you actually working right now.
What it does
Rocky is a macOS desk companion — a boxer whose physical condition is a live readout of your work state.
- Message debt. Every unread, unanswered Teams message adds weight. Clean queue, he shadowboxes. Let it pile up and he takes body shots, backs onto the ropes, and eventually goes down for the count.
- Focus rounds. Start a round and Rocky sits on the corner stool, holding the queue so nothing interrupts you. When the round ends, he stands up and gives a spoken 15-second cornerman briefing on what landed while you were heads-down.
- One-tap drafts. Anything scored "blocked on you" gets a ready-to-send draft, deep-linked back into Teams.
- Distraction intervention. Rocky watches what's frontmost. If you've been on a known distraction site (YouTube, Reddit, etc.) for several minutes straight and someone is actually waiting on you, he walks in from the screen edge, names the site and how long you've been there, tells you specifically who needs you, and sends you back in with a boxer's line before walking back out. Clean queue means he never bothers you — the nudge is debt-gated, not a blanket screen-time timer.
- Ambient system awareness. Separately, Rocky reacts to the machine itself — CPU load, thermal state, battery, disk, connectivity — with small ambient cues (a strained face, a bubble) when something's under real load. It's flavor, not a trigger for intervention.
How we built it
Distraction detection. ActivityMonitor tracks the frontmost app via NSWorkspace activation events and reads window titles through the Accessibility API — enough to tell a YouTube tab from a Docs tab in the same browser, without ever transmitting the title anywhere. ActivityClassifier runs each session through an ordered, user-editable rule list (defaults: dev tools/Teams/Slack/Docs → focus; YouTube/Reddit/Twitter/Instagram/Netflix/TikTok → distraction).
The intervention gate. InterventionCoordinator only fires when a continuous distraction streak clears a threshold (default 5 minutes, with a 30-second grace window so a quick Slack glance doesn't reset it) and the debt score exceeds a gate — so Rocky never nags you about YouTube when your queue is already clean. An escalating cooldown (15 minutes, doubling per additional trigger within the hour) and a "leave me alone" suppression keep it from feeling naggy.
The nudge itself. When the gate passes, Rocky walks in from the nearest screen edge, states how long you've been on the distraction and what it is, speaks a generated line naming exactly who's waiting and for what (fed by the same triage context as the debt score), delivers a motivational quote from a curated set of real boxers and athletes, then walks back out.
Challenges we ran into
- Losing the character we started with. Upstream's minifig was the whole visual identity, and we had to redesign it from scratch, mid-build, without breaking the animation system it plugged into.
- Zero dependencies, real auth. Implementing OAuth device code by hand over URLSession instead of reaching for MSAL meant writing and testing our own polling/refresh logic under deadline pressure.
- Graph API drift. The Teams chat message and delta endpoints don't always behave like the docs say — $orderby/$filter support is inconsistent, so we had to verify behavior empirically rather than trust the spec.
- Keeping "unanswered" simple. It was tempting to make the detection rule cleverer (thread context, @mentions, read receipts). We forced ourselves to keep it to one rule and resisted scope creep under time pressure.
- Making silence feel earned. Tuning the debt-score decay and the 3-second smoothing so Rocky reads as alive rather than jittery took more iteration than we expected.
- Keeping the nudge honest, not naggy. Early on it was tempting to trigger on screen time alone. We deliberately coupled it to actual debt — Rocky only interrupts you if someone is really waiting — which meant building streak tracking, grace windows, and escalating cooldowns instead of a single timer.
Accomplishments that we're proud of
- Shipped a fully original character under a hard IP constraint, on a hackathon clock, without it becoming the bottleneck.
- Auth worked end-to-end on night one the riskiest part of the build which de-risked everything after it.
- The debt-score-to-stance mapping actually feels right: glance at Rocky and you know your state before you've consciously parsed why.
- Reused upstream's animation gating system instead of fighting it, which let us add new states cheaply.
What we learned
- How much signal is hiding in system-level telemetry (CPU, thermals) that most productivity tools ignore in favor of screen-time counters.
- That structured outputs make triage pipelines dramatically simpler to reason about no prompt-parsing brittleness.
- That constraints (no third-party deps, no borrowed IP) force better design decisions than they cost in convenience.
- That a single well-tuned number (debt score) can replace an entire dashboard if the character selling it is expressive enough.
What's next for Rocky
- Channel messages and multi-account support, cut from v1 to protect the deadline.
- Sending replies directly from Rocky instead of deep-linking into Teams.
- More nudge types beyond CPU/heat — calendar overload, meeting-back-to-back detection.
- Slack and Gmail as additional message sources behind the same triage pipeline.
- Notarisation and auto-update, so Rocky can leave hackathon-mode and live on real desks.
Built With
- accessibility-api
- appkit
- avspeechsynthesizer
- combine
- cryptokit
- elevenlabs
- gpt-5.6
- iokit
- keychain
- macos
- microsoft-graph-api
- network-framework
- oauth2
- swift
- swiftui
Log in or sign up for Devpost to join the conversation.