feat: lifecycle broadcasts on signup_closed / cancelled / debating / completed

Phase 3 push-wakeup mechanism without adding a new push channel.
Topic state transitions now post short messages to the same Fabric
announce channel used for the initial signup announcement. Agents
subscribed to announce + not currently busy get woken via the
existing Phase 1 inbound path; busy-discard already filters
appropriately. No SSE, no per-agent DM fanout, no plugin changes —
reuses existing infra end-to-end.

Changes:
- ticker.go: after signup_close transition, broadcasts signup_closed
  (with pro/con/judge agent IDs + debate-start time) OR cancelled
  (with reason). After debate_start transition, broadcasts debating
  with debate-end time.
- announce.go: new PostLifecycleEvent helper - same headers/auth as
  PostTopicAnnouncement, different format.
- verdict.go: after successful judge submission, broadcasts completed
  with the judge id. Best-effort + async so a slow Fabric does not
  slow the judge response.
- routes.go: instantiates the announcer once + passes to VerdictHandler.

Workflow participate-debate step 5 should be updated to expect
wakeups instead of polling - separate follow-up edit on lyn/ClawSkills.
This commit is contained in:
h z
2026-05-23 15:02:58 +01:00
parent 15bb942d9b
commit b2a0cac460
4 changed files with 111 additions and 8 deletions

View File

@@ -11,6 +11,7 @@ import (
"git.hangman-lab.top/hzhang/Dialectic.Backend/internal/auth"
"git.hangman-lab.top/hzhang/Dialectic.Backend/internal/config"
"git.hangman-lab.top/hzhang/Dialectic.Backend/internal/fabric"
"git.hangman-lab.top/hzhang/Dialectic.Backend/internal/httpapi/handlers"
"git.hangman-lab.top/hzhang/Dialectic.Backend/internal/store"
)
@@ -62,7 +63,13 @@ func Mount(cfg *config.Config, db *sqlx.DB, version string) http.Handler {
topicsH := handlers.NewTopicsHandler(topicStore)
signupsH := handlers.NewSignupsHandler(topicStore, signupStore)
argsH := handlers.NewArgumentsHandler(topicStore, campStore, roundStore, argStore)
verdictH := handlers.NewVerdictHandler(topicStore, campStore, verdictStore)
announcer := fabric.NewAnnouncer(fabric.AnnounceConfig{
GuildBaseURL: cfg.FabricGuildBaseURL,
ChannelID: cfg.FabricAnnounceChannelID,
SystemAPIKey: cfg.FabricSystemAPIKey,
BotBearerToken: cfg.FabricBotBearerToken,
})
verdictH := handlers.NewVerdictHandler(topicStore, campStore, verdictStore, announcer)
adminH := handlers.NewAdminHandler(db, cfg.AgentAPIKeyPepper, cfg.DialecticAdminAPIKey)
// Routes.