Skip to content

Live tab: refuse an agenda row that needs a role instead of timing it under the default (#91 follow-up) - #140

Merged
ShuhaoZQGG merged 8 commits into
devfrom
fix/91-live-tab-needs-role
Nov 13, 2026
Merged

ShuhaoZQGG merged 8 commits into
devfrom
fix/91-live-tab-needs-role

Conversation

@ShuhaoZQGG

@ShuhaoZQGG ShuhaoZQGG commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Refs #91. Follow-up to #139, and built on its branch: merge #139 first. Until then this PR's diff also shows #139's commits (now up to 95fbaeb, merged in by c65998a); this PR's own commits are 5de8185 and 73217f4.

Why

#139 stopped Simple Format import from guessing roles: a row whose bracket names none of your roles keeps that text as role, has no rules, and the Agenda tab won't open it. The Live tab still had two other ways to load such a row:

  • its agenda picker: the panel's speaker suggestions (handleSelectSuggestion) and the Full-Screen Timer's speaker list (StageSpeakerPicker, onSelectSpeaker={loadSpeakerFromAgenda});
  • the auto-advance after FINISH (handleFinish).

Either loaded the row, and the effect from e876ca8 ("the selected role left the list") then moved it to Standard Speech or the first listed role without a word. That is the same silent guess #91 removed from the import, one step later.

What changes

  • Suggestions and the stage's speaker list refuse the row. Nothing loads; a warning toast reads "Pick a role for {name} on the Agenda tab first". A row with a listed role loads as before.
  • FINISH stops at the row. If the next uncompleted row needs a role, it is not loaded and not skipped. The name field is cleared, the role stays on the finished speaker's role, and the same toast names the waiting speaker.
  • The e876ca8 effect keeps its purpose. A hand-picked role that leaves the list (Edit Rules, a club publish) still moves to the default role. A loaded agenda row whose role leaves the list is let go instead: the timer detaches from it (clearActiveSpeaker), the field clears, and the same toast shows. The row keeps its role for the Agenda tab to flag, and finishing whatever is timed next neither completes it nor advances past it.
  • Zoom app and web timer. The web TimerContext gains clearActiveSpeaker (the Zoom app already had it). The check is Simple Format import: ask for a role when the bracket doesn't name one, instead of guessing (#91) #139's agendaItemNeedsRole, so the Live tab and the Agenda tab agree on which rows need a role.

Security

None. Client-side UI only.

Production impact

Ships with or after #139, whose agendaItemNeedsRole it uses. With both, master users who import a loosely named role get a toast instead of a speaker timed under the default role, from the Live tab's suggestions, the Full-Screen Timer's list and FINISH. No flags, no migration, no API or sync change. Rollback is a revert.

Testing

  • npm test: 2275 pass (116 files). New: "an agenda row that needs a role (Simple Format import: ask for a role when the bracket doesn't name one, instead of guessing #91)" in apps/zoom-app/src/components/LiveTab.test.jsx (suggestions, stage picker, FINISH, hand-picked role leaving the list, loaded row whose role leaves the list) and the same set minus the stage in apps/web/src/components/LiveTab.test.jsx. Against the pre-change LiveTab.jsx the four refusal tests fail in the Zoom app and three in the web app; the e876ca8 test passes on both, as intended.
  • npm run test:e2e: 44 pass. New in e2e/zoom.timer-flow.spec.js: "the Live tab does not time a row that needs a role: suggestions, FINISH, stage picker (Simple Format import: ask for a role when the bracket doesn't name one, instead of guessing #91)".
  • npm run test:specs: valid (8 specs). timer-core gains TC-13; timer-sounds and club-card-sets reviewed (notes under Not covered); the follow-up line in agenda-import's Not covered is gone, and its intro points to TC-13.
  • Oracle: none. A follow-up session, not a factory build.

Evidence (real Zoom client)

timer-core TC-13 ✅ on this branch @ 5de8185, deployed to dev as version 5b28ba86 (committed vars: ENTITLEMENT_ENFORCE=1, FLAGS_FORCE=0). Run log: test-runs/2026-11-13-91-live-tab-needs-role/run.md (local).

  • Import "Bob (1min Role) / Alex (Table Topics) / Alice (Tech Tip)": toast "Imported 3 speaker(s). 1 needs a role"; only Alex highlighted.
  • 1, suggestions: picking Alex shows "Pick a role for Alex on the Agenda tab first"; name stays empty, role stays on ChairToast1 (this device's default).
  • 2, FINISH: Bob (1min Role) ran 5 s; after FINISH the same toast, empty name, role still 1min Role. One speech upload (POST /api/club/meetings/20261113/speeches 200). PostHog: timer_started and speech_finished for Bob / 1min Role only.
  • 3, Full-Screen Timer: picking Alex shows the toast above the stage and the stage name stays empty; picking Alice loads Alice / Tech Tip.
  • After: Alex still highlighted "(Table Topics) is not one of your roles", not completed.

The device runs Claude Test Club's role list (no Standard Speech or Ice Breaker), so the lines used listed roles; TC-13 now says how to substitute.

Not verified

  • timer-core TC-01…TC-12 were not rerun on this PR's code. Simple Format import: ask for a role when the bracket doesn't name one, instead of guessing (#91) #139's run passed TC-01…TC-08, TC-10 and TC-11 on b029a29, before this PR's LiveTab.jsx change; this PR changes only the agenda paths there, and Bob's START → FINISH → upload in TC-13 went through the same start/finish code. The spec keeps verified_at: b029a29 and is marked stale here until those steps rerun on this code.
  • timer-sounds and club-card-sets were reviewed, not run: no step loads an agenda row, and both need a second account or computer.
  • The web timer has no Zoom-client spec; its unit tests cover it.
  • Not part of this change: the web timer's suggestion pick still doesn't link the agenda row (it sets the speaker without loadSpeakerFromAgenda), as before.

Look here first

🤖 Generated with Claude Code

github-actions Bot and others added 7 commits November 13, 2026 15:56
…Table Topics' with no timing rules

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atch

The first plan fixed the keyword ladder so "(Table Topics)" landed on
Table Topics Speech. The direction changed: a fallback to a built-in can
land on a role the user removed, and the closest-match step from #77
gives false positives ("(Table Topics)" became a custom Table Topics
Master). Import now takes only an exact role name and highlights the
rest on the Agenda tab for a manual pick.
…e rest

A line gets a role only when its bracket names one of the roles in the
dropdown (same words; case, spacing and punctuation ignored). Any other
line keeps its text as written, with no rules. The Agenda tab highlights
such rows, with a "Pick a role" list of the saved roles and a banner
that counts them; clicking one asks for a role instead of opening the
Live tab. The check is derived from the current roles, so rows saved
earlier as 'Table Topics' and rows whose role was later removed are
highlighted too. Completed rows and breaks never are.

matchCustomRole and detectRoleFromText had no callers left and are
removed. Add Item now starts on the first listed role when Standard
Speech is hidden, so it never saves a role the picker didn't show.

agenda-import gains IM-06 and new expectations for IM-02 and IM-05.
timer-core is reviewed: importBulkSpeakers is under its Not covered.
The real-client run was blocked: the Mac's screen was locked, so no
step ran. The branch is deployed to dev (version 9b18a973) for the
rerun. verified_at stays at the last real run.
The Agenda tab already refused to open a row whose role isn't one of the
user's. The Live tab had two other ways to load it: its agenda picker
(the panel's speaker suggestions, and the Full-Screen Timer's speaker list)
and the auto-advance after FINISH. Either loaded the row, and the effect
from e876ca8 then moved it to Standard Speech or the first listed role
without a word.

Now all three refuse the row and show "Pick a role for {name} on the
Agenda tab first". After FINISH the name field is cleared and the role
stays as it was; the running order stops at that row instead of skipping
it. The e876ca8 effect still moves a hand-picked role that leaves the
list; for a loaded agenda row whose role leaves the list it lets go of
the row (clearActiveSpeaker, new in the web TimerContext) with the same
toast, instead of re-roling it. Zoom app and web timer.

Tests: both LiveTab.test.jsx files, a Playwright case in
zoom.timer-flow.spec.js. timer-core gains TC-13; timer-sounds and
club-card-sets reviewed; agenda-import's follow-up line removed.
Passed in the real Zoom client against dev version 5b28ba86. The device
ran a club role list without Standard Speech or Ice Breaker, so TC-13 now
says how to substitute listed roles. verified_at is unchanged: only TC-13
ran.
Both specs ran green in the real Zoom client against dev a249d5d7:
agenda-import IM-01..IM-06 and timer-core TC-01..TC-08, TC-10, TC-11
(TC-09 and TC-12 are human steps). The device hides every built-in
role, so the agenda-import needs line now says to use a listed role
in its place, and to keep the device's own custom roles unless one
clashes with a line in the spec.
INDEX conflict on the timer-core row: kept #140's Covers text and #139's
verified column (b029a29, 2026-11-13). timer-core stays `stale` here:
its full run on b029a29 predates #140's LiveTab.jsx change, on which only
TC-13 ran (5de8185). agenda-import takes #139's `current`; #140 changes
none of its covered files. timer-core's #91 note now names the steps that
reran on b029a29.
@ShuhaoZQGG
ShuhaoZQGG marked this pull request as ready for review November 13, 2026 19:52
@ShuhaoZQGG
ShuhaoZQGG merged commit 905d8d1 into dev Nov 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant