Skip to content

feat(panel): hub page lists hubs with live counts and registers a channel - #305

Merged
SyniRon merged 2 commits into
developfrom
feat/293-hub-page-register
Sep 18, 2026
Merged

SyniRon merged 2 commits into
developfrom
feat/293-hub-page-register

Conversation

@SyniRon

@SyniRon SyniRon commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Closes #293. Spec: #285, "Panel: hub page" and "Panel: service layer".

What changed

  • The hub page at 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 /hubs registers an existing voice channel as a hub with the defaults (permission source category, 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.
  • Refusals name the field and write nothing: a channel that is not a voice channel of the guild, one that is already a hub, one with no category, or a base string outside 1 to 90 characters. The page re-renders with status 422 and the refusal under data-error="<field>".
  • 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 the request, call one service function and render. The refusal path calls list again to redraw the page.
  • commands.TempVCManager gains GuildChannels; commands.TempVC gains SpawnedCount. NewTempVC and HandleVoiceStateUpdate are exported so the panel's tests build a real runtime over their own fakes and feed it a join. StartTempVC stays the one production caller.
  • panel.New takes Deps{Store, Runtime, Manager, GuildID}. main.go builds the panel after StartTempVC, still before the Discord session opens. With PANEL_ADDR set and BOT_DB_DSN empty the bot logs BOT_DB_DSN not set, panel disabled and runs with no panel, as the spec has it.
  • Templates carry data-hub="<id>" and data-field="<name>" on the elements that show a hub's values and forms, and data-error="<field>" on a refusal. Those attributes are the test contract; labels and order are not.
  • Docs: the hub page and the store requirement in CLAUDE.md, README.md and .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.Fake and a Discord fake that implements commands.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-out POST /hubs redirects to sign-in and writes nothing; a failed guild read is a 5xx. In commands, SpawnedCount is per hub and follows joins and leaves.

Verified

  • CI gate locally: lint, tidy, race tests with floors against a throwaway Postgres, build.
  • Smoke test on the test guild: registered Open - Join to create as Open Voice in the panel, joined it and got Open Voice - 1, left and it was deleted, joined again and got Open Voice - 1 again, and the page showed a spawned count of 1. A second register of the same channel answered 422 with the field named.

Reviewer notes

  • The code-review pass (Standards and Spec) is applied in the second commit. Two judgement calls were kept as they are: panel.Deps as a name, and a second manager fake in the panel tests, since Go test helpers do not cross packages.
  • HandleVoiceStateUpdate is exported for the panel's tests only; its comment says so and names StartTempVC as the one production caller.

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
SyniRon enabled auto-merge (squash) September 18, 2026 17:39
@SyniRon
SyniRon merged commit e1690f1 into develop Sep 18, 2026
3 checks passed
@SyniRon
SyniRon deleted the feat/293-hub-page-register branch September 18, 2026 17:40
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
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.

Hub page: list hubs and register an existing channel

1 participant