Weekly Officer Briefing¶
Every Sunday at 23:30 UTC, an AI-generated briefing is posted to the officer channel summarizing the week's clan activity. The briefing is composed by Anthropic's Claude API from a structured snapshot of the bot's data.
Components¶
| File | Role |
|---|---|
Briefing/WeeklyOfficerBriefingService.cs |
Background service. Runs the schedule. |
Briefing/BriefingDataCollector.cs |
Pulls structured stats from the DB and external sources. |
Briefing/BriefingContext.cs |
The shape of data fed into the prompt. |
Briefing/BriefingPrompts.cs |
The prompt templates. |
Briefing/BriefingInviteSection.cs |
Renders invite-attribution data into prompt input. |
Briefing/BriefingRedditLeadsSection.cs |
Renders Reddit leads into prompt input. |
Briefing/BriefingServiceCollectionExtensions.cs |
DI wiring. |
AI/ClaudeAiService.cs |
Anthropic API client. |
BriefingNowCommandHandler.cs |
/briefing-now slash command (BG+). |
Schedule¶
Configured via the WeeklyBriefing section:
| Key | Default |
|---|---|
RunOnDayUtc |
Sunday |
RunAtUtc |
23:30 |
OfficerChannelId |
1499843996032438272 |
DryRun |
false |
MaxOutputTokens |
1500 |
The service polls once a minute and fires when the configured day-of-week and time are met.
Pipeline¶
flowchart TB
Sched[Sunday 23:30 UTC] --> Collect[BriefingDataCollector]
Collect --> Activity[Activity stats]
Collect --> Promotions[Promotion deltas]
Collect --> Events[Event attendance]
Collect --> Invites[Invite attribution]
Collect --> Leads[Reddit leads]
Collect --> Awol[AWOL changes]
Activity --> Ctx[BriefingContext]
Promotions --> Ctx
Events --> Ctx
Invites --> Ctx
Leads --> Ctx
Awol --> Ctx
Ctx --> Prompt[BriefingPrompts.Build]
Prompt --> Claude[ClaudeAiService]
Claude --> Post[Post to OfficerChannelId]
What goes in¶
BriefingDataCollector pulls a one-week window of:
- Activity — total messages, voice hours, top participants
- Promotions — who advanced, who's now eligible
- Events — number of events, average attendance, no-shows
- AWOL — newly flagged, kicked, recovered
- Invites — new joins per attributed source
- Reddit leads — count, claimed/dismissed split
The exact shape lives in BriefingContext. To add a new section: add a property to BriefingContext, populate it in BriefingDataCollector, and reference it in BriefingPrompts.
Prompt structure¶
BriefingPrompts builds a system prompt that instructs the model to act as an officer's chief of staff producing a concise weekly summary. Each BriefingContext section is rendered into the prompt as structured data (lists, tables) so the model has clean input to cite.
The model is instructed to:
- Lead with what changed week-over-week
- Call out anomalies (sudden drop in activity, unusual recruitment patterns, etc.)
- Surface members worth recognizing (top contributors, recent promotions)
- End with action items for the officer corps
Output is capped at MaxOutputTokens (default 1500) — long enough for substantive analysis, short enough to fit in a Discord message without splitting.
Triggering manually¶
/briefing-now (BriefingNowMinRank, default BG) bypasses the schedule. Useful when:
- Testing prompt changes
- Generating an off-cycle briefing for a leadership meeting
Dry run¶
WeeklyBriefing.DryRun = true runs the full pipeline (data collection, model call, billed tokens) but doesn't post the result. The output appears in the bot logs at Information level, prefixed [BRIEFING DRYRUN] (or similar — check the service for the exact tag).
Use dry-run when iterating on the prompt to avoid spamming the officer channel.
Cost & rate limits¶
Each run is roughly one API call to Claude (~5–15K input tokens depending on the week's activity, ≤1.5K output). At Claude Sonnet pricing this is on the order of cents per run.
If briefings stop appearing, check the API key first — expired/rotated keys are the most common cause.
Common operational questions¶
Briefing didn't post Sunday night.
Claude__ApiKeyset in the environment?WeeklyBriefing.OfficerChannelIdcorrect and bot has post permissions?DryRunnot accidentallytrue?- Was the bot online at the scheduled time? Logs around the schedule time will show whether the trigger fired.
The briefing said something factually wrong.
The model is summarizing structured data from BriefingContext — if the underlying data is correct but the briefing misrepresents it, that's a prompt issue. Adjust BriefingPrompts and re-test with /briefing-now.
If the underlying data is wrong, fix the collector (BriefingDataCollector) — the model can't be more accurate than its input.
Want to add a new section.
- Add a property to
BriefingContext. - Populate it in
BriefingDataCollector. - Render it as a section in
BriefingPrompts. - Test with
/briefing-nowbefore next Sunday.