feat(panel): hub page lists hubs with live counts and registers a channel - #305
Merged
Merged
Conversation
The hub page at GET / lists every hub: base string, hub channel name and category name read from the guild, enabled state, and the live spawned count from the runtime. POST /hubs registers an existing voice channel as a hub with the defaults, writes the row and applies it to the runtime in process, so a join spawns from it with no restart. A channel with no parent, a non-voice channel, a channel already a hub, or a base string outside 1 to 90 characters is refused with the field named and nothing written. The service layer in panel/hubs.go owns validation, the store calls, the guild reads through the manager seam and the runtime updates; handlers parse, call one service function and render. TempVCManager gains GuildChannels; TempVC gains SpawnedCount, and its constructor and voice state handler are exported so the panel's tests build a real runtime over their fakes and feed it a join. The panel now requires the store: with PANEL_ADDR set and BOT_DB_DSN empty the bot stops at startup. Tests drive the real mux with the store fake and a Discord fake. Panel floor raised to 75 against 77.9 measured. Refs #293
The service reads the store and the guild once per call through a snapshot, and the guild list answers its own lookups. With PANEL_ADDR set and BOT_DB_DSN empty the bot now logs one WARN and runs with no panel, as the spec has it, instead of stopping. The browser test helper takes a body, so the form post goes through one path. The list marks a gone channel or a missing category in muted text; the broken hub marking is the later ticket's. The hub page notes in CLAUDE.md move under Architecture. Refs #293
SyniRon
enabled auto-merge (squash)
September 18, 2026 17:39
SyniRon
added a commit
that referenced
this pull request
Sep 18, 2026
panel/hubs.go: replace "Choose a voice channel of the guild." panel/templates/home.html: delete the page description under the Hubs title panel/templates/home.html: revise "not in the guild" to "not in the server" panel/templates/home.html: replace the empty-list row panel/templates/home.html: replace the Voice channel hint panel/templates/home.html: revise the Base string hint to name Cavbot2
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 #293. Spec: #285, "Panel: hub page" and "Panel: service layer".
What changed
GET /lists every hub: base string, hub channel name and category name read from the guild at each load, enabled state, and the live spawned count from the runtime.POST /hubsregisters an existing voice channel as a hub with the defaults (permission sourcecategory, no moderator roles, user limit 0, bitrate 64000, enabled), writes the row, applies it to the runtime in process, and redirects to/. A join spawns from it with no restart.data-error="<field>".panel/hubs.goowns validation, the store calls, the guild reads through the manager seam and the runtime updates. Handlers parse the request, call one service function and render. The refusal path callslistagain to redraw the page.commands.TempVCManagergainsGuildChannels;commands.TempVCgainsSpawnedCount.NewTempVCandHandleVoiceStateUpdateare exported so the panel's tests build a real runtime over their own fakes and feed it a join.StartTempVCstays the one production caller.panel.NewtakesDeps{Store, Runtime, Manager, GuildID}.main.gobuilds the panel afterStartTempVC, still before the Discord session opens. WithPANEL_ADDRset andBOT_DB_DSNempty the bot logsBOT_DB_DSN not set, panel disabledand runs with no panel, as the spec has it.data-hub="<id>"anddata-field="<name>"on the elements that show a hub's values and forms, anddata-error="<field>"on a refusal. Those attributes are the test contract; labels and order are not.CLAUDE.md,README.mdand.env.example. No new environment variable. Panel coverage floor raised from 73 to 75 against 78.6% measured.Why
This is the transcription path the cutover uses: the maintainer registers each MEE6 hub in the panel while MEE6 still runs. Editing, removal and the change log are #294; create, rename, broken hub and last failure are #295.
Tests
A seam sniffer approved the ledger before any test was written. Tests drive the real mux with
httptest, the fake forum,store.Fakeand a Discord fake that implementscommands.TempVCManager; the runtime under the panel is real. Covered: the list shows the count and the names under the data attributes; a register writes a row with the defaults and a join then spawns; each refusal names its field and writes nothing (mutation-checked, each row reddens alone when its check is removed); the picker offers voice channels that are not hubs; a signed-outPOST /hubsredirects to sign-in and writes nothing; a failed guild read is a 5xx. Incommands,SpawnedCountis per hub and follows joins and leaves.Verified
Open - Join to createasOpen Voicein the panel, joined it and gotOpen Voice - 1, left and it was deleted, joined again and gotOpen Voice - 1again, and the page showed a spawned count of 1. A second register of the same channel answered 422 with the field named.Reviewer notes
panel.Depsas a name, and a second manager fake in the panel tests, since Go test helpers do not cross packages.HandleVoiceStateUpdateis exported for the panel's tests only; its comment says so and namesStartTempVCas the one production caller.