DELETE /api/pods/:id now also calls closeRoom (RoomServiceClient.deleteRoom)
to end the live LiveKit room and disconnect anyone connected. PodView listens
for RoomEvent.Disconnected and returns the user to the pod list, so a deleted
pod cleanly kicks everyone out instead of stranding them in a dead room.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use the member name as the LiveKit identity (was random per join) so a
refresh/rejoin replaces the existing session instead of leaving a ghost
participant in the room. Set roomConfig on the token (emptyTimeout 60s,
departureTimeout 20s) so empty rooms and departed participants clean up
promptly. Pods map 1:1 to a deterministic room name, so joins reuse the
same room rather than spawning new ones.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Join UX: click a member's name to join directly, or type a name and "Join"
(adds to roster + joins) — removes the redundant dropdown step. "Add" still
adds to the roster without joining.
Presence: new GET /api/presence (LiveKit RoomServiceClient) reports who's
connected per pod. The pod list polls it and shows a "N in room" badge plus a
green dot on members currently connected, so people can see active pods and jump in.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- PodView now shows LIVE LiveKit participants (updates as people join/leave,
active-speaker highlight) instead of the static roster — no refresh needed.
- "Play beat" button publishes a generated 4/4 beat into the room so every
participant hears the same audio (speaker lights up) — a real connectivity test.
- "Share my screen" is now a deliberate button; joining only connects to the
room (fast/reliable), so a denied/slow screen prompt no longer fails the join.
- Session persisted in sessionStorage with auto-reconnect, so a refresh keeps
you in the room instead of dropping back to the pod list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend: atomic create with insert-retry-on-duplicate-key (removes slug
TOCTOU race + wrong 400s), resilient per-index creation so a unique-index
failure can't silently drop the constraint or block seeding, idempotent
race-safe seeding via $setOnInsert, type validation + size caps on all pod
fields/members, removeMember no-ops without a write, /api/outcome guarded.
Frontend: per-pod busy state (one mutation no longer disables every card),
joined pod derived from the list (can't go stale), create keeps inputs on error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The precaching PWA SW was serving an old bundle after deploys (stale UI).
Switch to selfDestroying so it unregisters + clears caches on all clients;
deploys are now always fresh. Re-add a tuned PWA before the demo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pods are persisted in MongoDB and CRUD'd via /api/pods (list/create/get/
update/delete + add/remove member). Shared Pod reshaped to a persisted entity
(members: string[], description, timestamps) + PodInput. Backend seeds Demo/
Frontend/Backend Pods on first run (Squad -> Pod rename). Frontend replaces
hardcoded teams with live data: PodCard (member chips add/remove, inline edit,
delete, join) + CreatePodForm; App fetches+mutates via API. Removes teams.ts/
TeamCard; gitignores test artifacts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the single join form with a richer UI: team cards (members as
avatars, repo, description), a member picker, and a post-join PodView showing
pod members (you highlighted) plus a PodMan panel with a slot for live
intervention cards. Adds Avatar/TeamCard/PodView components.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
If the backend returns the placeholder LiveKit URL, skip the real connect and
screen capture and drop into the post-join state (with a DEV MODE banner) so
the connected UI is developable without LiveKit creds or HTTPS. Real joins
also guard getDisplayMedia behind isSecureContext.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The canonical-architecture rewrite dropped app.use(cors()) and moved the
token route to POST /api/token ({room,identity,name}), but the frontend
still called the old /pods/:id/token — causing "Failed to fetch" in the
browser. Re-add CORS and update fetchPodToken to the new contract.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace free-text name/pod inputs with select-only dropdowns (pick a team,
then yourself from its members) for fast R&D iteration. Demo teams live in
lib/teams.ts until pods are persisted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vite + React + TS + Tailwind v4, installable PWA. Join-pod UI that fetches a
LiveKit token from the backend, connects to the pod room, and publishes screen
+ mic so PodMan can watch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set up top-level structure (backend, frontend, database, infra, shared,
docs) with .gitkeep placeholders, root README, and .gitignore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>