Repository navigation
Dev - #150
Merged
Merged
Dev#150
Conversation
…icker The mode menu's "My Background…" opened a modal that only wrapped OwnBackgroundPicker, which the Card Images modal already shows. Remove the item, the wrapper modal and its LiveTab wiring. The "Show my own background" toggle stays in the menu.
…emoval No step changes: no step opened the mode menu's "My Background…" item. Note under "Not covered" that the own-background picker now lives only in Card Images.
…enu-item fix(overlay): drop the My Background menu item; Card Images has the picker
A soft chime when the card changes (one note at green, two at yellow, three at red) in the Zoom app and the web timer, and music while Take a Break counts down in the Zoom app. Pro only, behind the new `sounds` release flag, and off until the user turns it on. Sound plays on the timer's own computer; the Sounds modal says the speaker hears it only through Zoom's Share sound. The chime and the placeholder "Music box" track are synthesized with Web Audio; file tracks can be added under public/music/ via BREAK_TRACKS. The player is mounted above the tabs so sound carries on when the Live tab is switched away or the web panel is minimized. Tests: shared engine and component unit tests, web and Zoom e2e that count real oscillators, and a new timer-sounds Zoom-client spec (timer-core reviewed: no step changes, pointer under Not covered).
feat(sounds): card-change chime and break music for Pro (#105)
Hiding the Sounds button whenever a user could not use it meant free users never learned the feature existed, so nothing told them what Pro adds. The button and modal now always render; anyone who cannot use them sees them locked (grey text, lock icons, a tooltip saying why). Pro users are locked too while the `sounds` release flag is off, with "coming soon" instead of an upgrade pitch. This ships a visible, locked Sounds button to production on merge, before the flag is turned on.
feat(sounds): show Sounds locked instead of hiding it
fix(wrangler): change FLAGS_FORCE from "1" to "0"
A Pro plan on dev (a subscription, a grant, or a club on the device) saw "Sounds are coming soon" while the pro flag was off, with no Pro chip to say why. useEntitlement's isPro now needs the pro flag as well as the plan, in both apps, so while pro is dark every Pro feature reads a Pro plan as free: Sounds shows the Pro lock, Card images says images stay in this browser. "Coming soon" is left for pro on and sounds off. `known` waits for the flags too, so a Pro user never sees a lock flash. The server still keys on `entitled`, and clubs on the club record, so a paying user's sync, backups and club archive carry on. The flag is retired before Pro has real customers, so nobody pays and sees free. timer-sounds spec: covers useEntitlement, TS-08 now needs pro on and sounds off (PostHog targeting), new TS-09 for a Pro plan under FLAGS_FORCE "0". Not rerun in the real client.
fix(pro): make the pro flag the source of truth for Pro in the UI
An admin or editor presses "Share with my club" on one of their own custom card sets in Card Images. The images go to R2 under club-card/<clubId>/ and the set to club-cards:<clubId>, and the club's ver moves, so every club device downloads it into IndexedDB and lists it in the picker. A device that never chose cards of its own opens on the club's set; one that did keeps its own, with the club's one tap away (toastmaster_card_source). A lapsed or left club drops the set and the device's own choice shows again. Admins can remove the set from /club/admin. The images are private: they live outside club/<clubId>/, which /api/club-assets serves to anyone. Uploads are checked against their SHA-256, sniffed as JPEG/PNG/WebP, capped at 2 MB each and 20 MB per club. Publishing prunes images no set names, sparing uploads under 15 minutes old. Phase 1 of docs/plans/10-pro-is-the-club-plan.md (local plan, not tracked). New Zoom-client spec: e2e/zoom-client/club-card-sets.md (draft, not yet run).
Claude used to finish the code and stop, then ask whether Zoom was open. The repo's own instructions made that the right finish: CLAUDE.md let an unrun spec end as `status: stale`, the zoom-app-testing skill triggered only when the user asked to test and wanted a yes first, and preflight told the user to start the meeting. - CLAUDE.md: a definition of done that includes running the covering Zoom-client specs, standing approvals for the steps that needs (dev deploy, starting Zoom, flag flips with restore, branch/PR work), and a pointer to the factory. - zoom-up.sh launches Zoom and starts the test meeting (PMI from the gitignored meeting.local, or Zoom's Start Meeting shortcut). Preflight and the skill now treat "Zoom not running" as Claude's step. - Stop hook (scripts/factory/stop-gate.mjs): on a factory/ branch, the first stop is refused while covered files changed and no newer run.md names the spec. CI, headless stations and a second stop pass. - scripts/lib/zoom-specs.mjs: the spec front-matter reader, shared by check-zoom-specs.mjs and the hooks. vitest now runs scripts/.
Each step of the pipeline is now a repo skill with one input (an issue number), one artifact and one label change, runnable in a session, headless (`claude -p`) or from a trigger: factory (the router), factory-issue, factory-plan, factory-build (oracle → implement → verify in fresh subagents), factory-zoom and factory-report. - scripts/factory/state.mjs is the one reader of the label state machine: where an issue is, what runs next, and the WIP queue. Human gates (factory:ready, plan:approved) count only when a config.json actor added them; the repo is public. - Oracle tests: factory-oracle records them in .factory/oracles/<n>.json. A PreToolUse hook refuses edits to them, and check-oracle.mjs fails a PR that changed them after the oracle commit. - scripts/factory/gh.sh drops the shell's work-account GITHUB_TOKEN locally. - .factory/README.md documents labels, conventions and artifacts.
There was no CI at all, so an agent's work was checked only by the agent. ci.yml runs unit tests, the spec check, the oracle check, the build and the Playwright suite on every PR into dev or master and every push to them. The PR template is the completion report's shape (PR #128's sections plus Evidence, Not verified and Look here first).
factory-plan.yml plans the oldest promoted issue when a factory actor adds factory:ready or a factory PR closes (WIP allowing). factory-build.yml runs oracle → implement → verify when a factory actor adds plan:approved. Both re-check the gate with state.mjs, so a label from anyone else does nothing. Inert until the repo variable FACTORY_ENABLED is true, the Claude GitHub App is installed (its token, unlike GITHUB_TOKEN, makes pushes trigger CI) and CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY is set. The Zoom station stays on the Mac (.factory/schedules.md).
factory-ideate reads PostHog (usage, errors, surveys), recent issues and comments, and drafts 3–5 evidence-backed issues labelled idea; promoting stays human. A "Factory-ready issue" form asks for the same sections the plan station needs. .factory/schedules.md holds the prompts for the two runs set up by hand: the Mac's Zoom night shift and weekly ideation.
…he role in parentheses
ubuntu-latest moved to 26.04, which Playwright 1.58 cannot install browsers for, so the first CI run failed at "Install Playwright browser" after unit tests, specs, the oracle check and the build had passed.
First real run of zoom-up.sh: Zoom launched and Start Meeting (⌃⌘V) worked in 23 s, but what opened was the pre-join video preview, titled "<Host>'s Zoom Meeting", and the substring check took it for the meeting. zoom-up.sh and preflight now require the window named exactly "Zoom Meeting" (what zoom-front.sh raises); zoom-up.sh presses Return on the preview's default Start button. Gotchas: the preview, and Zoom popovers needing click.sh --activate.
chore: lights-on factory (phases 1–5)
The 02:00 night shift rarely found the Mac awake. The local scheduled task factory-zoom-daily now runs at 17:00, starts only between 16:55 and 17:30, and does nothing unless a PR is labelled needs:zoom-test.
chore(factory): run the Zoom station daily at 17:00
… in step with master master is the default branch, so issue-label events run master's copy of the factory workflows and claude-code-action skips any workflow that differs from it. The skip reported success, so #90's build looked green without running. Each workflow now checks that its station moved the issue on, and says why when it didn't.
The Custom-set card-switch tests opened the app, stored four images in IndexedDB, then reloaded. The web timer page is lazy-loaded, so on a slow CI runner it was still starting while the test stored the images; its start-up sweep saw images no set listed yet and deleted them, and after the reload the set fell back to the built-in card. Seed from robots.txt on the same origin instead, where no app code runs, then open the app once.
test(e2e): seed custom card sets before the app starts
ci(factory): fail when the Claude step is skipped; keep the workflows in step with master
Live tab: refuse an agenda row that needs a role instead of timing it under the default (#91 follow-up)
…e release timer-core TC-01..08, 10, 11, 13 and club-card-sets CC-01..05, 09 passed in the real Zoom client on dev 3ce801a6. CC-06 ran only as a guest (401), CC-07/CC-08 and TC-09/TC-12 not run (human or unapproved KV edits).
test(zoom-client): rerun timer-core and club-card-sets on dev before release
… before stopping The #80 plan run (actions run 43532812887) ended cleanly after 14 turns with nothing written: no branch, PR, comment or label. The likely cause is a subagent launched in the background: a headless run can end the moment the station ends its turn to wait for it. - .factory/README.md: new convention, subagents run in the foreground. - factory-plan: research subagent in the foreground; step 9 checks state.mjs before stopping. - factory-build: sub-stations one at a time in the foreground; don't end the turn before needs:zoom-test unless factory:stuck. - factory-verify: review subagent in the foreground.
…agents Factory: run station subagents in the foreground; check state before stopping
Once the pro flag is on, a free device gets a 'New: Pro for your club' window after a speech ends (PeriodicPrompts timing, shown once), handing off to the Upgrade dialog, and the footer's Upgrade button carries a red dot until any Upgrade dialog has been opened or 28 days after the window showed. Pro devices see neither. New zoom-client spec pro-launch.
…ce released A chime when the card changes, and music during breaks, listed only while the sounds flag is on, so nobody is sold a feature they would find locked.
…can be bought Sounds stay visible and locked. The lock says "Hold tight! Sounds are coming soon." whenever the sounds or pro flag is off, and "Sounds are a Pro feature" only once buying Pro would unlock them. Same rule in the web app. A dismissible banner above the tabs in the Zoom app says a new look and Pro are coming. It shows only while the pro flag is known and off, so it goes away by itself at launch.
The pre-launch banner becomes the first entry in ANNOUNCEMENTS (packages/shared/announcements.js). To announce something, add an entry with a new id: lead, text, tone, surfaces, optional dates, an optional flag condition and an optional link. The Zoom app shows the first entry that applies; a close is remembered per announcement on that device. The banner itself is presentational and shared (packages/ui), so the web app can render it later. Events become announcement_shown, announcement_dismissed and announcement_link_opened, each with announcement_id.
A run now ends with zoom-down.sh: end the test meeting and quit the client, then say in chat that the Mac is free. With Zoom left open the user could not tell whether the run was over. Not done when the run paused because the user took the Mac.
…er only in the last 60 days Before the launch (pro off) the footer keeps a grey Pro button with a lock. Pointing at it or clicking it lists what Pro brings: sounds, club card sets on every club timer, voting for Best Table Topics and Best Evaluator, and more. Once pro is on it is the real Pro / Upgrade button again. The club renewal reminder now shows for a club that will not renew only in its last 60 days, and only with a known end date. A failed payment is still warned about at once.
feat(pro): pre-launch — announcement banner, locked Pro button, "coming soon" Sounds; renewal reminder only in the last 60 days
chore(zoom-testing): quit Zoom at the end of a run
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
toastmaster-timer-dev | e28d733 | Nov 14 2026, 09:51 PM |
Every club feature now reads one gate, clubFeaturesOn (packages/shared/club.js): the club is active AND the pro flag is known and on. Until now they checked only club.entitled, so a club device kept its badge, club card set, club timings, archive strip / History / share, report branding and renewal line while pro was off. While pro is off a club device behaves as a lapsed one: nothing is deleted, it stays in its club, and everything is back once pro is on. Speeches are not sent to the club's archive meanwhile. useClub and the renewal banner re-render when the flags arrive; the club card sync re-checks on a flag change too. Both apps.
…-pro feat(pro): club features only while Pro is released
The release PR (dev → master, #150) failed it: across the release range, #91's commits legitimately updated tests in #90's and #107's oracles, and the check counts every later commit. The factory README already scopes it to a factory/ PR, where an oracle is frozen for its own issue's build; each feature PR into dev still runs it there.
Resolves the spec-index conflict with dev (#147, #151): every row from both sides, pro-launch kept after timer-sounds and in the run order. Footer.jsx merged cleanly: with pro on, the Upgrade / Pro button with the launch dot; with pro off, dev's locked 'Pro (coming soon)' button. pro-launch is marked stale (the Sounds line and this merge came after its last run) and points to pro-coming-soon for the pro-off footer.
…pgrade after the dev merge
…s-only ci: run the oracle check on factory/ PRs only
feat(pro): one-time Pro launch announcement in the Zoom app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #
Why
What changes
Security
Production impact
Testing
npm test:npm run test:e2e:npm run test:specs:Evidence (real Zoom client)
Not verified
Look here first