Conference organiser feature requests (multi-language, interactivity & live polling, 1:1 capture) #1

Closed
opened 2026-06-19 13:43:16 +00:00 by emre · 5 comments
Member

Conference organisers reviewed the app and would like a few additions. Existing functionality below is for confirmation; new requests follow.

Already supported (confirm with organisers)

  • Contacts / directory of people — Directory with filtering
  • Direct & group messaging — Chat
  • See the programme — Event Program (Today / Full / My agenda)

Requested additions

  1. Multi-language UI — English, Spanish, French (the three languages on the CM50 website).

  2. Translations — translate content (messages / programme / notes) across EN/ES/FR.

  3. Interactivity & live polling — one shared engine for collecting and displaying live audience input, used both in flagged "interactable" sessions and in plenary (Mentimeter-style):

    • RSVP to a session
    • Q&A — submit questions during a session
    • Live polling — multiple choice, word cloud, open-ended, ranking
    • Live results — aggregated responses shown in real time
  4. 1:1 meeting capture — notes for bilateral meetings (e.g. CEO-to-CEO). Visibility: participants in the meeting only (not organisers).

Conference organisers reviewed the app and would like a few additions. Existing functionality below is for confirmation; new requests follow. ### Already supported (confirm with organisers) - **Contacts / directory of people** — Directory with filtering - **Direct & group messaging** — Chat - **See the programme** — Event Program (Today / Full / My agenda) ### Requested additions 1. **Multi-language UI** — English, Spanish, French (the three languages on the CM50 website). 2. **Translations** — translate content (messages / programme / notes) across EN/ES/FR. 3. **Interactivity & live polling** — one shared engine for collecting and displaying live audience input, used both in flagged "interactable" sessions and in plenary (Mentimeter-style): - **RSVP** to a session - **Q&A** — submit questions during a session - **Live polling** — multiple choice, word cloud, open-ended, ranking - **Live results** — aggregated responses shown in real time 4. **1:1 meeting capture** — notes for bilateral meetings (e.g. CEO-to-CEO). **Visibility: participants in the meeting only** (not organisers).
Member

Live translation during presentations. will need live stt model and translation model. presenters should predefine a dictionary of words they will use for the stt to work properly.

Live translation during presentations. will need live stt model and translation model. presenters should predefine a dictionary of words they will use for the stt to work properly.
Member

we will need audio from the presentation feeding into it which will be processed and then translated.

we will need audio from the presentation feeding into it which will be processed and then translated.
Member

Implementation Spec for Issue #1

Scope note: this plan covers four of the requested areas. Live presentation translation (streaming STT + translation of presentation audio, from the comments) is deferred to a later iteration and is not implemented here.

Objective

Add four organiser-requested capabilities to the CM50 app, following the established patterns (OSIS schema-first models, openrpc_server!/openrpc_client!, the existing SSE real-time hub, Dioxus/wasm UI conventions):

  1. Multi-language UI (EN/ES/FR), persisted per-user like the theme.
  2. On-demand content translation (messages / programme / notes) across EN/ES/FR via the server AI layer.
  3. One shared real-time interactivity engine: RSVP, Q&A, live polling (multiple-choice / word-cloud / open-ended / ranking), live aggregated results — used both in sessions flagged "interactable" and in standalone plenary spaces.
  4. Private 1:1 meeting notes, visible only to the two meeting participants (organisers/admins explicitly excluded).

Architecture Findings

  • Persistence: OSIS schema-first under cm50_server/oschema/<domain>/*.oschema. A [rootobject] gets CRUD auto-generated; sid/created_at/updated_at are auto-injected. Domains today: participants, program, community.
  • RPC: openrpc_server! macro in cm50_server/main.rs generates per-domain traits, the dispatcher, the axum router, and writes specs to cm50_server/openrpc/. Impls live in cm50_server/rpc/{domain}_impl.rs. CRUD is deliberately stubbed to forbidden; all access is token-authenticated via the Auth helper in participants_impl.rs.
  • Real-time: hero_lifecycle::sse::{SseBus, SseEvent, sse_router}. community_impl.rs (chat) is the canonical end-to-end example (bus.publish(thread_sid, SseEvent::json("chat.message", &msg))), with an authenticated SSE router merged via the extra router in main.rs. EventSource/WS auth rides the ?token= query string.
  • UI (Dioxus/wasm, cm50_ui): routes in routes.rs, global state in app.rs, typed clients via openrpc_client! in api/mod.rs, wire↔UI seam in services/backend.rs, live SSE consumer in services/live.rs. Per-user preference persistence template: components/theme.rs + services/storage.rs (localStorage).
  • Important build step: cm50_ui/openrpc/*.json are manual copies of cm50_server/openrpc/*.json. Every schema change requires: build server → copy specs → build UI.
  • cm50_web is a static-file SPA host only; all RPC/SSE lives on cm50_server.

Implementation Plan

Step 1 — i18n scaffolding (catalog + LANG global + switcher)

Feature: Multi-language UI. Files: create cm50_ui/src/i18n/{mod,strings}.rs, cm50_ui/src/components/lang.rs; modify cm50_ui/src/main.rs, services/storage.rs, app.rs. Mirror the theme.rs pattern: Lang { En, Es, Fr }, LANG: GlobalSignal<Lang>, t(key) lookup over a static EN/ES/FR table, save_lang/load_lang (localStorage), restore at startup, LangSwitcher component. Dependencies: none.

Step 2 — Route nav + page headers through i18n

Feature: Multi-language UI. Files: components/navigation.rs and PageHead usages across pages/*.rs. Replace hardcoded labels with t(...) keys; place LangSwitcher in the shell. Dependencies: Step 1.

Step 3 — Interaction domain schema (shared engine)

Feature: Interactivity. Files: create cm50_server/oschema/interaction/{00_enums,10_space,20_question,30_poll,40_rsvp,90_rpc}.oschema. PollKind = multiple_choice|word_cloud|open_ended|ranking; InteractionSpace[rootobject]{title@index, session_sid, state, rsvp_enabled, qa_enabled}; AudienceQuestion[rootobject]{space_sid@index, author_sid, body, upvotes:[str], answered}; Poll[rootobject]{space_sid@index, kind, prompt, options:[str], open} with embedded PollVote{voter_sid, choice_indices:[u8], text}; Rsvp[rootobject]{space_sid@index, participant_sid, state}. service InteractionService: space_admin_save, space_snapshot, spaces_for_session, question_post, question_upvote, poll_admin_save, poll_vote, rsvp_set. Dependencies: none.

Step 4 — Interaction server impl + real-time results

Feature: Interactivity. Files: create cm50_server/rpc/interaction_impl.rs; modify main.rs, service.toml. Mirror community_impl.rs (stores, Auth, bus). Guarded methods; on every write bus.publish(space_sid, SseEvent::json("interaction.update", &aggregate)). Add authenticated SSE router keyed on space_sid. New socket cm50/rpc_interaction.sock (sse=true). Stub generated CRUD to forbidden. Dependencies: Step 3.

Step 5 — Meetings (1:1 notes) schema

Feature: 1:1 capture. Files: create cm50_server/oschema/meetings/{00_enums,10_meeting,90_rpc}.oschema. Meeting[rootobject]{participant_sids:[str] (exactly two), title@index, scheduled_at:otime, notes:[MeetingNote]}; embedded MeetingNote{author_sid, body, created_at:otime}. service MeetingsService: meeting_create, meetings_for_me(token), meeting_note_add, meeting_get. Visibility: participants-only; NO admin override. Dependencies: none.

Step 6 — Meetings server impl (participant-only visibility)

Feature: 1:1 capture. Files: create cm50_server/rpc/meetings_impl.rs; modify main.rs, service.toml. meeting_create enforces caller ∈ participants, len==2. meetings_for_me returns only meetings the caller participates in — no admin special-casing (explicit requirement). meeting_note_add/meeting_get enforce membership. New socket cm50/rpc_meetings.sock. Dependencies: Step 5.

Step 7 — Content translation (server) translate RPC

Feature: Translations. Files: modify cm50_server/oschema/program/{61_ai,90_rpc}.oschema, cm50_server/rpc/program_impl.rs. Add TranslateReq{token, text, target_lang} / TranslateResult{text, available, error} and translate(...) to ProgramService. Reuse the ai_complete_blocking plumbing with a "translate to {ES|FR|EN}, preserve Markdown" prompt via spawn_blocking. Dependencies: none.

Step 8 — Interaction UI models + service layer

Feature: Interactivity. Files: modify cm50_ui/src/data/models.rs, services/backend.rs, api/mod.rs, services/live.rs; add cm50_ui/openrpc/openrpc_interaction.json (copied after Step 4 build). Add UI structs + interaction client + service fns (space_snapshot, poll_vote, question_post, question_upvote, rsvp_set, admin saves). Generalize live.rs into a subscribe_topic(...) helper reused by interaction + chat. Dependencies: Step 4.

Step 9 — Interaction UI pages (RSVP / Q&A / polls / live results)

Feature: Interactivity. Files: create cm50_ui/src/pages/interact.rs, components/interact.rs; modify routes.rs, navigation.rs, main.rs, link from pages/event_program.rs. Render RSVP, Q&A (post + upvote), four poll widgets, and live results (bar chart / tag cloud / list / ranked list); subscribe via subscribe_topic("interaction.update") keyed on space sid (mirror components/chat.rs use_effect + drop). Route /interact/:space_id. Dependencies: Steps 8, 10, 1.

Feature: Interactivity. Files: modify cm50_server/oschema/program/40_session.oschema, program_impl.rs, cm50_server/seed/sessions.json, plus UI EventSession model + backend.rs converter. Add interactable:bool + interaction_space_sid:str; seed a couple of interactable sessions + matching InteractionSpace in seed/interaction.json. Dependencies: Step 3 (entity), Step 4 (seed loader).

Step 11 — Content translation UI wiring

Feature: Translations. Files: modify services/backend.rs (add translate), add "Translate" affordances to components/chat.rs, pages/event_program.rs, pages/meeting_notes.rs; re-copy openrpc_program.json after Step 7. Default target = current LANG; inline result (reuse the AI-note inline pattern in chat). Dependencies: Steps 7, 1.

Step 12 — 1:1 meeting notes UI

Feature: 1:1 capture. Files: create cm50_ui/src/pages/meeting_notes.rs; modify routes.rs, navigation.rs, api/mod.rs (meetings client), backend.rs, data/models.rs, main.rs; add openrpc_meetings.json. List my meetings (server-enforced), "New 1:1" creator (pick a directory participant), per-meeting note timeline with add-note. Route /meetings. Dependencies: Steps 6, 1.

Step 13 — Integration consolidation + spec re-copy + build verification

Feature: cross-cutting. Files: main.rs, service.toml, all cm50_ui/openrpc/*.json. Reconcile the macro-generated serve_domains_with(...) signature and the extra router .merge() chain across the new domains; build server (regenerates specs) → copy specs → build wasm UI. Final gate. Dependencies: all prior.

Acceptance Criteria

  • EN/ES/FR switcher; selection persists across reload; nav + headers re-render in the chosen language.
  • program.translate returns ES/FR/EN translations; "Translate" affordance on messages and session descriptions shows the result inline, with graceful fallback when the provider is unavailable.
  • One InteractionService powers RSVP, Q&A (post + upvote), and all four poll types, for both interactable sessions and standalone plenary spaces.
  • A vote/question updates every connected viewer's aggregated results in real time via SSE (interaction.update), no reload.
  • 1:1 meetings store per-participant notes; meetings_for_me returns only the caller's meetings; an admin/organiser cannot read or list meetings they are not a participant of.
  • cm50_server builds (regenerating specs); UI specs re-copied; cm50_ui wasm builds.
  • No direct CRUD write surface opened for any new rootobject; all access token-authenticated.

Notes / Risks

  • serve_domains_with arity changes with each new service block; Steps 4/6 expect a build to discover the signature, and Step 13 reconciles it.
  • cm50_ui/openrpc/*.json are manual copies of the server specs — build server → copy → build UI on every schema change (most likely source of compile drift).
  • Steps 4, 6 both edit main.rs/service.toml; edits are serialized, with Step 13 as the reconciliation gate.
  • herolib_ai is synchronous — all AI calls run via spawn_blocking (as ai_complete already does).
  • Privacy: 1:1 notes explicitly exclude organisers — no require_admin override in meetings_impl.rs (differs from every other domain).
## Implementation Spec for Issue #1 Scope note: this plan covers four of the requested areas. **Live presentation translation (streaming STT + translation of presentation audio, from the comments) is deferred to a later iteration** and is not implemented here. ### Objective Add four organiser-requested capabilities to the CM50 app, following the established patterns (OSIS schema-first models, `openrpc_server!`/`openrpc_client!`, the existing SSE real-time hub, Dioxus/wasm UI conventions): 1. Multi-language UI (EN/ES/FR), persisted per-user like the theme. 2. On-demand content translation (messages / programme / notes) across EN/ES/FR via the server AI layer. 3. One shared real-time interactivity engine: RSVP, Q&A, live polling (multiple-choice / word-cloud / open-ended / ranking), live aggregated results — used both in sessions flagged "interactable" and in standalone plenary spaces. 4. Private 1:1 meeting notes, visible only to the two meeting participants (organisers/admins explicitly excluded). ### Architecture Findings - **Persistence**: OSIS schema-first under `cm50_server/oschema/<domain>/*.oschema`. A `[rootobject]` gets CRUD auto-generated; `sid`/`created_at`/`updated_at` are auto-injected. Domains today: `participants`, `program`, `community`. - **RPC**: `openrpc_server!` macro in `cm50_server/main.rs` generates per-domain traits, the dispatcher, the axum router, and writes specs to `cm50_server/openrpc/`. Impls live in `cm50_server/rpc/{domain}_impl.rs`. CRUD is deliberately stubbed to `forbidden`; all access is token-authenticated via the `Auth` helper in `participants_impl.rs`. - **Real-time**: `hero_lifecycle::sse::{SseBus, SseEvent, sse_router}`. `community_impl.rs` (chat) is the canonical end-to-end example (`bus.publish(thread_sid, SseEvent::json("chat.message", &msg))`), with an authenticated SSE router merged via the `extra` router in `main.rs`. EventSource/WS auth rides the `?token=` query string. - **UI (Dioxus/wasm, `cm50_ui`)**: routes in `routes.rs`, global state in `app.rs`, typed clients via `openrpc_client!` in `api/mod.rs`, wire↔UI seam in `services/backend.rs`, live SSE consumer in `services/live.rs`. Per-user preference persistence template: `components/theme.rs` + `services/storage.rs` (localStorage). - **Important build step**: `cm50_ui/openrpc/*.json` are manual copies of `cm50_server/openrpc/*.json`. Every schema change requires: build server → copy specs → build UI. - `cm50_web` is a static-file SPA host only; all RPC/SSE lives on `cm50_server`. ### Implementation Plan #### Step 1 — i18n scaffolding (catalog + LANG global + switcher) Feature: Multi-language UI. Files: create `cm50_ui/src/i18n/{mod,strings}.rs`, `cm50_ui/src/components/lang.rs`; modify `cm50_ui/src/main.rs`, `services/storage.rs`, `app.rs`. Mirror the `theme.rs` pattern: `Lang { En, Es, Fr }`, `LANG: GlobalSignal<Lang>`, `t(key)` lookup over a static EN/ES/FR table, `save_lang`/`load_lang` (localStorage), restore at startup, `LangSwitcher` component. Dependencies: none. #### Step 2 — Route nav + page headers through i18n Feature: Multi-language UI. Files: `components/navigation.rs` and `PageHead` usages across `pages/*.rs`. Replace hardcoded labels with `t(...)` keys; place `LangSwitcher` in the shell. Dependencies: Step 1. #### Step 3 — Interaction domain schema (shared engine) Feature: Interactivity. Files: create `cm50_server/oschema/interaction/{00_enums,10_space,20_question,30_poll,40_rsvp,90_rpc}.oschema`. `PollKind = multiple_choice|word_cloud|open_ended|ranking`; `InteractionSpace[rootobject]{title@index, session_sid, state, rsvp_enabled, qa_enabled}`; `AudienceQuestion[rootobject]{space_sid@index, author_sid, body, upvotes:[str], answered}`; `Poll[rootobject]{space_sid@index, kind, prompt, options:[str], open}` with embedded `PollVote{voter_sid, choice_indices:[u8], text}`; `Rsvp[rootobject]{space_sid@index, participant_sid, state}`. `service InteractionService`: `space_admin_save`, `space_snapshot`, `spaces_for_session`, `question_post`, `question_upvote`, `poll_admin_save`, `poll_vote`, `rsvp_set`. Dependencies: none. #### Step 4 — Interaction server impl + real-time results Feature: Interactivity. Files: create `cm50_server/rpc/interaction_impl.rs`; modify `main.rs`, `service.toml`. Mirror `community_impl.rs` (stores, `Auth`, `bus`). Guarded methods; on every write `bus.publish(space_sid, SseEvent::json("interaction.update", &aggregate))`. Add authenticated SSE router keyed on `space_sid`. New socket `cm50/rpc_interaction.sock` (sse=true). Stub generated CRUD to forbidden. Dependencies: Step 3. #### Step 5 — Meetings (1:1 notes) schema Feature: 1:1 capture. Files: create `cm50_server/oschema/meetings/{00_enums,10_meeting,90_rpc}.oschema`. `Meeting[rootobject]{participant_sids:[str] (exactly two), title@index, scheduled_at:otime, notes:[MeetingNote]}`; embedded `MeetingNote{author_sid, body, created_at:otime}`. `service MeetingsService`: `meeting_create`, `meetings_for_me(token)`, `meeting_note_add`, `meeting_get`. Visibility: participants-only; NO admin override. Dependencies: none. #### Step 6 — Meetings server impl (participant-only visibility) Feature: 1:1 capture. Files: create `cm50_server/rpc/meetings_impl.rs`; modify `main.rs`, `service.toml`. `meeting_create` enforces caller ∈ participants, len==2. `meetings_for_me` returns only meetings the caller participates in — **no admin special-casing** (explicit requirement). `meeting_note_add`/`meeting_get` enforce membership. New socket `cm50/rpc_meetings.sock`. Dependencies: Step 5. #### Step 7 — Content translation (server) `translate` RPC Feature: Translations. Files: modify `cm50_server/oschema/program/{61_ai,90_rpc}.oschema`, `cm50_server/rpc/program_impl.rs`. Add `TranslateReq{token, text, target_lang}` / `TranslateResult{text, available, error}` and `translate(...)` to `ProgramService`. Reuse the `ai_complete_blocking` plumbing with a "translate to {ES|FR|EN}, preserve Markdown" prompt via `spawn_blocking`. Dependencies: none. #### Step 8 — Interaction UI models + service layer Feature: Interactivity. Files: modify `cm50_ui/src/data/models.rs`, `services/backend.rs`, `api/mod.rs`, `services/live.rs`; add `cm50_ui/openrpc/openrpc_interaction.json` (copied after Step 4 build). Add UI structs + `interaction` client + service fns (`space_snapshot`, `poll_vote`, `question_post`, `question_upvote`, `rsvp_set`, admin saves). Generalize `live.rs` into a `subscribe_topic(...)` helper reused by interaction + chat. Dependencies: Step 4. #### Step 9 — Interaction UI pages (RSVP / Q&A / polls / live results) Feature: Interactivity. Files: create `cm50_ui/src/pages/interact.rs`, `components/interact.rs`; modify `routes.rs`, `navigation.rs`, `main.rs`, link from `pages/event_program.rs`. Render RSVP, Q&A (post + upvote), four poll widgets, and live results (bar chart / tag cloud / list / ranked list); subscribe via `subscribe_topic("interaction.update")` keyed on space sid (mirror `components/chat.rs` use_effect + drop). Route `/interact/:space_id`. Dependencies: Steps 8, 10, 1. #### Step 10 — Mark sessions interactable + link to spaces Feature: Interactivity. Files: modify `cm50_server/oschema/program/40_session.oschema`, `program_impl.rs`, `cm50_server/seed/sessions.json`, plus UI `EventSession` model + `backend.rs` converter. Add `interactable:bool` + `interaction_space_sid:str`; seed a couple of interactable sessions + matching `InteractionSpace` in `seed/interaction.json`. Dependencies: Step 3 (entity), Step 4 (seed loader). #### Step 11 — Content translation UI wiring Feature: Translations. Files: modify `services/backend.rs` (add `translate`), add "Translate" affordances to `components/chat.rs`, `pages/event_program.rs`, `pages/meeting_notes.rs`; re-copy `openrpc_program.json` after Step 7. Default target = current `LANG`; inline result (reuse the AI-note inline pattern in chat). Dependencies: Steps 7, 1. #### Step 12 — 1:1 meeting notes UI Feature: 1:1 capture. Files: create `cm50_ui/src/pages/meeting_notes.rs`; modify `routes.rs`, `navigation.rs`, `api/mod.rs` (`meetings` client), `backend.rs`, `data/models.rs`, `main.rs`; add `openrpc_meetings.json`. List my meetings (server-enforced), "New 1:1" creator (pick a directory participant), per-meeting note timeline with add-note. Route `/meetings`. Dependencies: Steps 6, 1. #### Step 13 — Integration consolidation + spec re-copy + build verification Feature: cross-cutting. Files: `main.rs`, `service.toml`, all `cm50_ui/openrpc/*.json`. Reconcile the macro-generated `serve_domains_with(...)` signature and the `extra` router `.merge()` chain across the new domains; build server (regenerates specs) → copy specs → build wasm UI. Final gate. Dependencies: all prior. ### Acceptance Criteria - [ ] EN/ES/FR switcher; selection persists across reload; nav + headers re-render in the chosen language. - [ ] `program.translate` returns ES/FR/EN translations; "Translate" affordance on messages and session descriptions shows the result inline, with graceful fallback when the provider is unavailable. - [ ] One `InteractionService` powers RSVP, Q&A (post + upvote), and all four poll types, for both `interactable` sessions and standalone plenary spaces. - [ ] A vote/question updates every connected viewer's aggregated results in real time via SSE (`interaction.update`), no reload. - [ ] 1:1 meetings store per-participant notes; `meetings_for_me` returns only the caller's meetings; an admin/organiser cannot read or list meetings they are not a participant of. - [ ] `cm50_server` builds (regenerating specs); UI specs re-copied; `cm50_ui` wasm builds. - [ ] No direct CRUD write surface opened for any new rootobject; all access token-authenticated. ### Notes / Risks - `serve_domains_with` arity changes with each new `service` block; Steps 4/6 expect a build to discover the signature, and Step 13 reconciles it. - `cm50_ui/openrpc/*.json` are manual copies of the server specs — build server → copy → build UI on every schema change (most likely source of compile drift). - Steps 4, 6 both edit `main.rs`/`service.toml`; edits are serialized, with Step 13 as the reconciliation gate. - herolib_ai is synchronous — all AI calls run via `spawn_blocking` (as `ai_complete` already does). - Privacy: 1:1 notes explicitly exclude organisers — no `require_admin` override in `meetings_impl.rs` (differs from every other domain).
Member

Implementation summary

Implemented on branch development. Live presentation translation (the streaming STT/translation from the comments) is intentionally deferred to a later iteration and is not part of this change.

What was built

1. Multi-language UI (EN / ES / FR)

  • Simple language catalog (cm50_ui/src/i18n/strings.rs) wired to the UI via a LANG global signal and a t(key) lookup, mirroring the existing dark-theme preference. Selection persists in localStorage (cm50.lang) and is restored at startup.
  • A language switcher sits next to the theme toggle in the sidebar and top bar. Navigation labels (sidebar, mobile bottom bar, "More" sheet) render through t(...) and re-render live on switch.

2. Content translation (messages / programme / notes across EN/ES/FR)

  • New server RPC program.translate(text, target_lang) reusing the existing AI completion plumbing (runs off-thread via spawn_blocking, same provider-unavailable fallback shape as ai_complete).
  • "Translate" affordances added to chat message bubbles, event-session descriptions, and 1:1 meeting notes; each translates into the current UI language and shows the result inline.

3. Interactivity & live polling (one shared engine)

  • New interaction OSIS domain: InteractionSpace, Poll (with embedded votes), AudienceQuestion, Rsvp. One engine serves both sessions flagged interactable and standalone plenary spaces.
  • RSVP, audience Q&A (post + upvote), and all four poll types (multiple choice, word cloud, open-ended, ranking) with live aggregated results.
  • Results stream in real time over SSE (interaction.update, keyed on the space), reusing the existing live-chat hub — no reload.
  • Organiser authoring UI added to the Admin area: create interaction spaces (session-linked or plenary), list/open them, and add polls of any type.
  • Sessions gained interactable + interaction_space_sid; the programme surfaces a "Join interaction" link on interactable sessions. A demo interactable session + space are seeded under CM50_DEMO.

4. 1:1 meeting capture (participant-only)

  • New meetings OSIS domain: Meeting (exactly two participants) with an embedded MeetingNote timeline.
  • Visibility is strictly participants-only — meetings_for_me returns only the caller's meetings and there is deliberately NO organiser/admin override (this differs from every other domain, and is documented in the code).
  • UI page to list your meetings, start a new 1:1 with a directory member, and add notes.

Architecture notes

  • All new data follows the OSIS schema-first pattern; all access is token-authenticated and the auto-generated CRUD remains disabled (forbidden) as in the existing domains.
  • Two new OpenRPC domain sockets (rpc_interaction, rpc_meetings); the interaction live stream rides the existing community SSE socket. OpenRPC specs were regenerated and the UI clients updated.

Files (high level)

  • Server: oschema/interaction/*, oschema/meetings/*, oschema/program/{40_session,61_ai,90_rpc}.oschema, rpc/interaction_impl.rs, rpc/meetings_impl.rs, rpc/program_impl.rs, main.rs, service.toml, seed/{interaction,sessions}.json.
  • UI: i18n/*, components/{lang,interact,navigation,cards,chat}.rs, pages/{interact,meeting_notes,admin}.rs, services/{backend,live,storage}.rs, data/models.rs, api/mod.rs, routes.rs, app.rs, regenerated openrpc/*.

Build verification

  • cm50_server: cargo build succeeds (warnings only).
  • cm50_ui: cargo check --target wasm32-unknown-unknown reports zero errors; dx build --platform web produces the bundle successfully.

Test results

The hermetic integration suite (cm50_tests) currently reports 0 passed / 16 failed. This is a pre-existing failure unrelated to this change: the suite fails identically on a clean checkout of main (verified by stashing all changes), with every test erroring at the fixture's participant_admin_save bootstrap-secret step (-32001: session expired or unknown). No new tests were affected by this work, and no regression was introduced. The harness bootstrap-secret path should be repaired separately so the suite (and new coverage for the interaction/meetings domains) can run.

Follow-ups

  • Manual browser testing of the new surfaces against a running stack (router + server + UI).
  • Repair the pre-existing cm50_tests bootstrap-secret failure, then add integration coverage for the interaction and meetings domains.
  • Live presentation translation (deferred): streaming STT + translation of presentation audio.
## Implementation summary Implemented on branch `development`. Live presentation translation (the streaming STT/translation from the comments) is intentionally deferred to a later iteration and is not part of this change. ### What was built **1. Multi-language UI (EN / ES / FR)** - Simple language catalog (`cm50_ui/src/i18n/strings.rs`) wired to the UI via a `LANG` global signal and a `t(key)` lookup, mirroring the existing dark-theme preference. Selection persists in localStorage (`cm50.lang`) and is restored at startup. - A language switcher sits next to the theme toggle in the sidebar and top bar. Navigation labels (sidebar, mobile bottom bar, "More" sheet) render through `t(...)` and re-render live on switch. **2. Content translation (messages / programme / notes across EN/ES/FR)** - New server RPC `program.translate(text, target_lang)` reusing the existing AI completion plumbing (runs off-thread via `spawn_blocking`, same provider-unavailable fallback shape as `ai_complete`). - "Translate" affordances added to chat message bubbles, event-session descriptions, and 1:1 meeting notes; each translates into the current UI language and shows the result inline. **3. Interactivity & live polling (one shared engine)** - New `interaction` OSIS domain: `InteractionSpace`, `Poll` (with embedded votes), `AudienceQuestion`, `Rsvp`. One engine serves both sessions flagged `interactable` and standalone plenary spaces. - RSVP, audience Q&A (post + upvote), and all four poll types (multiple choice, word cloud, open-ended, ranking) with live aggregated results. - Results stream in real time over SSE (`interaction.update`, keyed on the space), reusing the existing live-chat hub — no reload. - Organiser authoring UI added to the Admin area: create interaction spaces (session-linked or plenary), list/open them, and add polls of any type. - Sessions gained `interactable` + `interaction_space_sid`; the programme surfaces a "Join interaction" link on interactable sessions. A demo interactable session + space are seeded under `CM50_DEMO`. **4. 1:1 meeting capture (participant-only)** - New `meetings` OSIS domain: `Meeting` (exactly two participants) with an embedded `MeetingNote` timeline. - Visibility is strictly participants-only — `meetings_for_me` returns only the caller's meetings and there is deliberately NO organiser/admin override (this differs from every other domain, and is documented in the code). - UI page to list your meetings, start a new 1:1 with a directory member, and add notes. ### Architecture notes - All new data follows the OSIS schema-first pattern; all access is token-authenticated and the auto-generated CRUD remains disabled (`forbidden`) as in the existing domains. - Two new OpenRPC domain sockets (`rpc_interaction`, `rpc_meetings`); the interaction live stream rides the existing community SSE socket. OpenRPC specs were regenerated and the UI clients updated. ### Files (high level) - Server: `oschema/interaction/*`, `oschema/meetings/*`, `oschema/program/{40_session,61_ai,90_rpc}.oschema`, `rpc/interaction_impl.rs`, `rpc/meetings_impl.rs`, `rpc/program_impl.rs`, `main.rs`, `service.toml`, `seed/{interaction,sessions}.json`. - UI: `i18n/*`, `components/{lang,interact,navigation,cards,chat}.rs`, `pages/{interact,meeting_notes,admin}.rs`, `services/{backend,live,storage}.rs`, `data/models.rs`, `api/mod.rs`, `routes.rs`, `app.rs`, regenerated `openrpc/*`. ### Build verification - `cm50_server`: `cargo build` succeeds (warnings only). - `cm50_ui`: `cargo check --target wasm32-unknown-unknown` reports zero errors; `dx build --platform web` produces the bundle successfully. ### Test results The hermetic integration suite (`cm50_tests`) currently reports 0 passed / 16 failed. This is a pre-existing failure unrelated to this change: the suite fails identically on a clean checkout of `main` (verified by stashing all changes), with every test erroring at the fixture's `participant_admin_save` bootstrap-secret step (`-32001: session expired or unknown`). No new tests were affected by this work, and no regression was introduced. The harness bootstrap-secret path should be repaired separately so the suite (and new coverage for the interaction/meetings domains) can run. ### Follow-ups - Manual browser testing of the new surfaces against a running stack (router + server + UI). - Repair the pre-existing `cm50_tests` bootstrap-secret failure, then add integration coverage for the `interaction` and `meetings` domains. - Live presentation translation (deferred): streaming STT + translation of presentation audio.
Member

Closing this umbrella in favour of granular, claimable issues (decided 2026-06-24). Mapping:

  • Multi-language UI (EN/ES/FR) — already supported; additional languages tracked in #33 (Chinese & Korean).
  • Translations of content — covered by live translation #12.
  • Interactivity & live polling (RSVP, Q&A, polling, live results) — #31 (Q&A + live polls) and #32 (sentiment wordcloud).
  • 1:1 meeting capture — already delivered via the Meetings feature (private in-person recorder + 1:1/group notes).

See the priority board for the full ranked list.

Closing this umbrella in favour of granular, claimable issues (decided 2026-06-24). Mapping: - **Multi-language UI (EN/ES/FR)** — already supported; additional languages tracked in **#33** (Chinese & Korean). - **Translations of content** — covered by live translation **#12**. - **Interactivity & live polling** (RSVP, Q&A, polling, live results) — **#31** (Q&A + live polls) and **#32** (sentiment wordcloud). - **1:1 meeting capture** — already delivered via the Meetings feature (private in-person recorder + 1:1/group notes). See the priority board for the full ranked list.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coopcloud_code/cm50_app#1
No description provided.