Releases: glitchwerks/rsl-mom-bot
Release list
v1.5.1
Fixed
deploy.ymlREQUIRED_SECRETSsynced withload_secret()call sites — added the previously-missingprod-new-members-channel-idandprod-discord-bot-api-keyentries to the preflight secrets array, which had drifted from the actual call sites insrc/; a new AST-based drift regression test walkssrc/for everyload_secret()call site and asserts exact-set equality against the workflow's array (#320, #341).- Prod revision-readiness gate added to
deploy.yml, then given real timeout headroom —az containerapp updatepreviously returned as soon as the update was accepted, not once the new revision was actually serving, leavingdeploy.ymlreporting success while the old revision could still be answering traffic for 60–90s; a polling "wait for prod revision readiness" step was added first (#332, #342), then widened from a 5-minute to a 10-minute timeout after a real prod deploy took 340s+ for the app-levellatestReadyRevisionNamefield to catch up even though the revision itself was already healthy (#344, #345).
Infrastructure
- Migrated off Data Collector API shared-key log ingestion — the Container Apps environment now routes console/system logs directly to Azure Monitor via a
diagnosticSettingsresource instead of the legacy shared-key custom-log ingest path, ahead of Microsoft's 2026-09-14 retirement of that API and in line with the org's managed-identity-over-key-auth standard;deploy.yml's migration-log KQL query now unions the old and new table names so migration-job output isn't lost during the cutover (#338, #339). glitchwerks/github-actionsreusable workflow bumped 2.6.1 → 2.6.2 — picks up a fix for a relative-path resolution bug inclaude-command-routerthat broke the action for external consumers (it invokedcheck-authvia./check-auth, which resolved into the caller's checked-out tree instead of the library's) (#336).- CI action/dependency bumps —
actions/checkout4.2.2 → 7.0.1 (#337),docker/login-action4.2.0 → 4.5.1 (#335),docker/setup-buildx-action4.1.0 → 4.2.0 (#334),docker/build-push-action6.9.0 → 7.3.0 (#333).
Documentation
- Deploy-recency guardrail documented in the AAD runbook — new subsection (written in planned tense, ahead of the guardrail workflow itself landing) covering the
DISCORD_INFRA_ALERT_WEBHOOK_URLwebhook secret and the manual action needed forinfra/scripts/**changes the automated guardrail can't see; also fixed a missing "Step 9.5" checklist item in the runbook's summary checklist, and added a README "Infrastructure runbook cross-reference" section pointing at the runbook (#319, #343).
v1.5.0
📣 Highlights
v1.5.0 adds a new recurring reminder for Siege: the bot now posts a heads-up 48 hours before Siege starts, and another 24 hours before, in the same channel and with the same role ping already used for Hydra and Chimera reminders — no more relying on someone remembering to post it manually.
Added
- Bi-weekly Siege reminders — the bot now posts reminders 48 hours and 24 hours before each Siege (14-day cadence), reusing the existing Hydra/Chimera reminder channel and role. Calendar predicates and a
month_conditionCHECK widen landed first (#325, #326, #328), followed by the message templates, scheduler wiring, and seed migration (#327, #329).
Infrastructure
- Deployed SHA recorded as a GitHub Deployment on
prod-infra—infra-deploy.ymlnow captures the actually-checked-out commit SHA and records it as a GitHub Deployment on theprod-infraenvironment after a successful Bicep apply, followed by a Deployment Status post so the environment doesn't show pending indefinitely. Slice 1 of the deploy-recency guardrail (#316, #321).
Documentation
- README refreshed for current v1.4.1 state — Status/Roadmap brought current, a "What it does" feature summary added, the hand-maintained epic table replaced with pointers to CHANGELOG.md and GitHub Releases, a CI Workflows section added, and Project Structure pruned to the actual package layout (#322, #324).
v1.4.1
📣 Highlights
New members joining right after a bot restart now reliably get their welcome message — a caching edge case could previously cause it to be silently skipped.
Fixed
v1.4.0
📣 Highlights
v1.4.0 is all about making new members feel welcome and making sure nobody falls through the cracks:
- A proper welcome — new members joining the server now get an automatic welcome message in
#new-members, asking for a screenshot of their in-game player profile so officers can get them set up faster. - Officer join alerts — officers can now opt in to a DM ping whenever a new member joins, via
/notify-new-members, instead of relying on catching it in chat. - A safety net for silent joiners — if a new member doesn't post anything within 24 hours of joining, the bot now sends them a friendly heads-up DM and removes them from the server, keeping the member list accurate without requiring manual officer cleanup.
Added
- Welcome message for new members — new joiners now get an automatic welcome post in
#new-members; a follow-up pass replaced the earlier#rolesself-assignment line with an ask for a screenshot of the member's in-game player profile (#303, #307). /notify-new-membersofficer alert command — officers can subscribe to a DM ping whenever a new member joins, instead of relying on catching it live in chat (#305).- Auto-kick for silent new members — members who post nothing within 24h of joining now receive a best-effort DM heads-up before being removed from the server, keeping membership accurate without manual officer follow-up (#304).
Infrastructure
claude-pr-reviewGitHub Actions workflow removed — PR review is now handled by CodeRabbit, making the Claude-powered review workflow redundant (#298).
v1.3.0
📣 Highlights
v1.3.0 is a reliability and observability release — no new commands, but several things that make the bot more self-correcting and easier to operate:
- Startup role-mapping preflight — the bot now self-verifies its day-role mapping once per boot (reconnect-safe, and a preflight error can never crash the bot).
- App Insights service name — the service now identifies itself as
mom-botinstead ofunknown_servicein Azure Application Insights (takes effect after the next infra deploy). - Faster due-notification lookups — a new database index on
occurrence_date_utcspeeds per-member notification queries. - Security — pip upgraded past CVE-2026-6357; Dependabot now opens automatic PRs to keep GitHub Actions pins current.
Added
- Startup role preflight —
run_preflight()is now called fromMomBot.on_ready()(afterseed_day_role_map), guarded by a_preflight_doneflag so a Discord reconnect can't re-run it, and defensively wrapped so a preflight error can't crash the bot; emits therole_preflight_completelog line once per revision boot (#194, #292). - Dev-only partial-response test seam —
MOM_BOT_FORCE_PARTIAL_FOR_DISCORD_IDenv var in_handle_assign()forces a partial role-sync result for smoke Scenario 5; absent or non-matching means zero behavior change (#74, #292).
Changed
- Authorization refactor (behavior-preserving) — extracted the duplicated manage-guild check from the five
/member-notify-*handlers into a sharedrequire_manage_guilddecorator in newsrc/mom_bot/discord_authz.py; removed dead_check_officer/_OFFICERS_ONLY_MSG; renamed_LINK_YOUR_ACCOUNT_MSG→_NOT_REGISTERED_MSG(#154, #289).
Infrastructure
- Index on
member_notification_sent.occurrence_date_utc— Alembic migrationb4+ matching ORM index backing thelist_due()date filter (previously only covered by the composite UNIQUE) (#278, #291). OTEL_SERVICE_NAME=mom-botadded to the container env so App Insightscloud_RoleNameresolves tomom-botinstead ofunknown_service; needs an infra-deploy apply to take effect (#271, #291).- pip-audit hardening — pip upgraded past CVE-2026-6357 in the pip-audit job; added
.github/dependabot.yml(github-actions ecosystem, weekly) to auto-update Action SHA pins (#59, #60, #282).
Documentation
v1.2.0
📣 Highlights
v1.2.0 brings two new automation features for officers and the whole clan:
- Tank Week reminders — the bot now posts a heads-up notice the Tuesday before Tank Week starts, and replaces the normal Hydra reminder with a "final hours" Tank Week message on the ending Tuesday. No more manual pings.
- Per-member DM notifications — officers can now schedule recurring direct-message reminders for any individual member using five new slash commands:
/member-notify-add,/member-notify-list,/member-notify-get,/member-notify-update, and/member-notify-remove. Cadence options are weekly, biweekly, or monthly.
Both features are live on next deploy.
Added
- Tank Week channel reminders — calendar-conditional reminder rows for Hydra clash: a heads-up notice fires the Tuesday before Tank Week begins, and a Tank Week end-of-clash reminder replaces the standard Hydra reminder for that occurrence. "Tank Week" is defined as the Hydra clash whose ending Tuesday is the first Tuesday of the month (#268, #276).
- Per-member notification slash commands — five officer-gated Discord slash commands for managing recurring DM notifications to a targeted guild member:
/member-notify-add,/member-notify-list,/member-notify-get,/member-notify-update,/member-notify-remove. Schedule is defined by anchor date + cadence (weekly / biweekly / monthly); monthly cadence clamps to last day of month and skips to next occurrence rather than catching up. Uses Discord's nativeMemberpicker and a cadence dropdown. All commands require Manage Server permission — the codebase's first runtime authorization gate (#269, #277).
Infrastructure
- Release CI: Discord announcement moved inline — the Discord release notification is now posted directly inside
release.ymlas a finalnotifyjob.notify-discord-release.ymlis now aworkflow_dispatch-only manual remediation tool for re-posting a failed announcement; it no longer fires automatically (#275). - New DB columns on
reminders—delivery_target(NOT NULL, default'channel') andmonth_condition(nullable, CHECK-constrained totank_week_headsup/tank_week_end) added via migrations 0004/0005 (#268). - New DB tables for per-member notifications —
member_notificationandmember_notification_senttables created via migrationb3; the reminder scheduler gained a DM-delivery branch to route these notifications (#269).
v1.1.0
📣 Highlights
v1.1.0 makes mom-bot observable and hardens its infrastructure for the long run:
- Production observability — OpenTelemetry traces and logs now flow to Azure Application Insights, giving operators structured visibility into sidecar and bot activity for the first time.
- Managed-identity migrations — Postgres schema migrations now run as a dedicated UAMI Container Apps Job with Entra token auth, replacing the manual migration step and eliminating the need for password-based credentials.
- Bicep-provisioned secrets and infrastructure — Log Analytics, App Insights, non-credential Key Vault values, and environment parameterisation are now declared in Bicep and applied through the standard deploy pipeline; no more ad-hoc manual provisioning.
- Ingress rate-limiting —
/api/internal/*endpoints are now rate-limited before bearer auth, protecting the sidecar from unauthenticated flood traffic. - Dead stopgap removed — the SQLite-on-AzureFile interim plumbing (storage account, file share, volume mount) has been fully deleted; Postgres is the unambiguous production database.
- Documentation correctness sweep — runbooks, the README, the secrets inventory, and ADR/spec files updated to reflect the current production state.
Added
- OpenTelemetry / Azure Monitor observability — OTel SDK wired with Azure Monitor exporter; traces and logs from both the bot and sidecar now flow to Application Insights (#199, #267).
- Rate-limiting on
/api/internal/*— sidecar enforces a per-IP request rate limit on internal endpoints before bearer auth is checked, protecting against unauthenticated flood traffic (#209, #232). - UAMI Container Apps Job for Postgres migrations —
alembic upgrade headnow runs as a separate Container Apps Job using a User-Assigned Managed Identity and Entra token auth, replacing the manual migration step in the deploy pipeline (#256).
Changed
- README: audit for staleness — Status, Roadmap, Database/Migrations, Project Structure, References sections refreshed to reflect v1.0 ship + v1.1 in progress (#249, #250).
- Runbook + secrets-inventory: resolve stale TBDs and "Epic 1+" placeholders (#249, #251).
Fixed
- Postgres Entra token acquisition — token for Postgres auth is now obtained via
azure-identity(ManagedIdentityCredential), replacing a fragilecurl-based approach that failed in the Container App environment (#260). - ACA IP-deny vs app-auth response codes — day-role-sync runbook corrected to document the actual HTTP responses returned by ACA ingress IP-deny rules vs. application-level auth failures (#196, #270).
- OIDC federated credentials updated to repo's canonical name
rsl-mom-bot(wasmom-botpre-rename). Bothmom-bot-prandmom-bot-main-pushFICs now match GitHub's current OIDC subject claim; unblocks theBicep what-if previewworkflow on PR-triggered runs and the nextworkflow_dispatchofdeploy.yml(#248, #252).
Infrastructure
- Log Analytics + App Insights provisioned via Bicep — workspace and Application Insights instance declared in Bicep and wired to the Container App Environment (#239).
- Non-credential Key Vault values provisioned via Bicep — configuration secrets (non-credential KV entries) are now set through the Bicep deployment rather than applied manually (#121, #237).
MOM_BOT_ENV/ database-url parameterisation — environment name and derived database URL secret name now flow through Bicep parameters, removing hard-coded values (#230).- Resource group parameterised in CI —
AZURE_RESOURCE_GROUPenvironment variable drives the deploy workflow; no more hard-coded RG name (#263). - Dead AzureFile plumbing removed —
storage.bicep(storage account + file share), the CAEstorageBindingresource, and the/datavolume mount deleted. The SQLite-on-AzureFile stopgap (#92) has been fully superseded by Postgres (#240, #265).
Documentation
- UAMI Container Apps Job migration spec committed to
docs/specs/(#241, #254). - Postgres role-ownership cutover plan and runbook Step 5.5 added (#262).
- AAD runbook TBDs resolved; App Insights rows added to secrets inventory (#249, #251).
- FIC rename (
mom-bot→rsl-mom-bot) documented (#248, #252). - Runbook flip-sequence Step 0 pre-deployment sanity check inserted (#244).
- Preflight checklist corrected for stale role-name and KV-secret claims (#242).
- Stale SQLite-as-production framing removed from framework plan (#243, #245).
- Five completed plan files deleted per lifecycle policy (#246, #247).
CI
- Discord release announcement posted automatically on GitHub Release publication via
notify-discord-release.yml(#228). uv lock --checkadded to CI to fail fast onpyproject.toml↔uv.lockdivergence (#229).
Tests
v1.0.0
Added
- Day-role-sync receiver —
POST /api/internal/role-syncsidecar endpoint receives day-role webhooks from siege-web, applies or removes Discord roles via themom_bot/roles/service, and persists idempotency state so exact replays short-circuit the service call. Per-discord_idasyncio.Lockprevents concurrent stale-write races; corrupted stored JSON self-heals on the next write. Contract:glitchwerks/rsl-mom-appscontracts/sidecar-api.yaml. (#71) - Full northbound sidecar HTTP API — FastAPI app served on port 8001 at bot startup, with reusable Bearer auth (
secrets.compare_digest), structured request/response models, and HTTP-level error translation. Endpoints:GET /api/version,GET /api/health(#184);GET /api/members,GET /api/members/{discord_user_id}(#185);POST /api/notify(#190);POST /api/post-message(#191);POST /api/post-image(#192). Sidecar wired intomake_client()/ bot startup sequence. (#163, #184, #185, #190, #191, #192) - Post-condition slash commands —
/post-conditions catalogand/post-conditions meproxy the siege-web preferences API, letting Discord members view and set their post-condition priorities without leaving Discord. UX evolved through several iterations: initial Select widget (#129), live-updating embed (#137), Button + Modal + CheckboxGroup flow (#139), button-grid V1 (#147), unified/post-conditions+/post-conditions-getwith set-summary embed (#150). Includes catalog cache, Retry-After backoff, and Discorddeferfor slow responses. (#129, #133, #135, #137, #139, #147, #150) X-Acting-Discord-Usernameheader — outgoing requests to siege-web now include the acting member's Discord username for actor identification on proxied calls. (#156)
Changed
- Member-not-registered error message rewritten to direct users to contact admins rather than exposing an internal state description. (#153)
Fixed
day_numberresolution on unassign — sidecar now resolvesday_numberfrom stored role-sync state on unassign, rather than requiring the caller to supply it. (#205)- Sidecar auth: 403 on missing
Authorizationheader — previously returned 401; corrected to 403 per contract. (#188) - Sidecar per-boundary validation — sidecar now returns 422 with structured error bodies on invalid request payloads; previously surfaced as unhandled 500s. (#189)
- Day-role name seeder — corrected expected role names to
Siege - Day {n} Attacker; enrichedDAY_ROLE_NOT_FOUNDlog with expected and available role names. (#131, #132) - Snowflake columns widened to BIGINT — reminder table snowflake columns were
INTEGER, truncating large Discord IDs. (#123) - Docker / venv invocation — bot startup now invokes the venv Python directly, avoiding a
uvcache-dir permission denial on container restart. (#117, #119) psycopgmoved to runtime dependencies — was incorrectly scoped to[dev], causing import failures in production. (#115)python-multipartadded to lock —uv.lockregenerated to includepython-multipart, required by FastAPI form parsing. (#201)
Infrastructure
- PostgreSQL Flexible Server — full migration from SQLite: provisioned ACA-integrated Postgres instance (#105), Postgres-portable Alembic dialect branching (#108), AAD-token engine with managed-identity auth + startup migrations removed (#110), Postgres admin-race fixed (#111),
alembic upgrade headstep in deploy pipeline (#113). - HTTPS ingress on port 8001 — Container App configured for public HTTPS ingress with IP allowlist; siege-web-api-dev CAE egress allowlisted. (#162, #193)
scale.minReplicas = 1— prevents the Container App from scaling to zero and dropping the Discord gateway connection. (#183)- RBAC hardening —
SystemAssignedidentity dropped (#82);AZURE_CLIENT_IDwired through Bicep toManagedIdentityCredential(#86);mom-bot-ghaservice principal granted constrained RBAC Admin at RG scope for Key Vault role management (#170); redundant GHA SP role assignments removed and ABAC conditions narrowed (#175). infra-deploy.ymlworkflow —workflow_dispatch-only pipeline for Bicep applies; includesset +ewrapper to preserveaz deploymenterror output. (#164, #168)- Bicep what-if PR preview — automated what-if diff posted as PR comment on infrastructure changes. (#100)
- SQLite via AzureFile (pre-Postgres interim) — volume mount, secret reference, replica lock, and snapshot config. Superseded by the Postgres migration. (#92)
Observability
- Startup URL log —
make_client()emitsINFO mom_bot.main Configured siege-web base URL: <url>at cold start, giving operators an instant cross-environment sanity check before any Discord gateway traffic. (#211) /healthzliveness probe — Container App liveness probe switched fromexec-type (rejected by ARM) tohttpGetagainst/healthz. (#88)
Pre-1.0 history: Initial pre-1.0 development — see git log and the merged PR history for full provenance.
v1.0.0-rc.0
Added
- Day-role-sync receiver —
POST /api/internal/role-syncsidecar endpoint receives day-role webhooks from siege-web, applies or removes Discord roles via themom_bot/roles/service, and persists idempotency state so exact replays short-circuit the service call. Per-discord_idasyncio.Lockprevents concurrent stale-write races; corrupted stored JSON self-heals on the next write. Contract:glitchwerks/rsl-mom-appscontracts/sidecar-api.yaml. (#71) - Full northbound sidecar HTTP API — FastAPI app served on port 8001 at bot startup, with reusable Bearer auth (
secrets.compare_digest), structured request/response models, and HTTP-level error translation. Endpoints:GET /api/version,GET /api/health(#184);GET /api/members,GET /api/members/{discord_user_id}(#185);POST /api/notify(#190);POST /api/post-message(#191);POST /api/post-image(#192). Sidecar wired intomake_client()/ bot startup sequence. (#163, #184, #185, #190, #191, #192) - Post-condition slash commands —
/post-conditions catalogand/post-conditions meproxy the siege-web preferences API, letting Discord members view and set their post-condition priorities without leaving Discord. UX evolved through several iterations: initial Select widget (#129), live-updating embed (#137), Button + Modal + CheckboxGroup flow (#139), button-grid V1 (#147), unified/post-conditions+/post-conditions-getwith set-summary embed (#150). Includes catalog cache, Retry-After backoff, and Discorddeferfor slow responses. (#129, #133, #135, #137, #139, #147, #150) X-Acting-Discord-Usernameheader — outgoing requests to siege-web now include the acting member's Discord username for actor identification on proxied calls. (#156)
Changed
- Member-not-registered error message rewritten to direct users to contact admins rather than exposing an internal state description. (#153)
Fixed
day_numberresolution on unassign — sidecar now resolvesday_numberfrom stored role-sync state on unassign, rather than requiring the caller to supply it. (#205)- Sidecar auth: 403 on missing
Authorizationheader — previously returned 401; corrected to 403 per contract. (#188) - Sidecar per-boundary validation — sidecar now returns 422 with structured error bodies on invalid request payloads; previously surfaced as unhandled 500s. (#189)
- Day-role name seeder — corrected expected role names to
Siege - Day {n} Attacker; enrichedDAY_ROLE_NOT_FOUNDlog with expected and available role names. (#131, #132) - Snowflake columns widened to BIGINT — reminder table snowflake columns were
INTEGER, truncating large Discord IDs. (#123) - Docker / venv invocation — bot startup now invokes the venv Python directly, avoiding a
uvcache-dir permission denial on container restart. (#117, #119) psycopgmoved to runtime dependencies — was incorrectly scoped to[dev], causing import failures in production. (#115)python-multipartadded to lock —uv.lockregenerated to includepython-multipart, required by FastAPI form parsing. (#201)
Infrastructure
- PostgreSQL Flexible Server — full migration from SQLite: provisioned ACA-integrated Postgres instance (#105), Postgres-portable Alembic dialect branching (#108), AAD-token engine with managed-identity auth + startup migrations removed (#110), Postgres admin-race fixed (#111),
alembic upgrade headstep in deploy pipeline (#113). - HTTPS ingress on port 8001 — Container App configured for public HTTPS ingress with IP allowlist; siege-web-api-dev CAE egress allowlisted. (#162, #193)
scale.minReplicas = 1— prevents the Container App from scaling to zero and dropping the Discord gateway connection. (#183)- RBAC hardening —
SystemAssignedidentity dropped (#82);AZURE_CLIENT_IDwired through Bicep toManagedIdentityCredential(#86);mom-bot-ghaservice principal granted constrained RBAC Admin at RG scope for Key Vault role management (#170); redundant GHA SP role assignments removed and ABAC conditions narrowed (#175). infra-deploy.ymlworkflow —workflow_dispatch-only pipeline for Bicep applies; includesset +ewrapper to preserveaz deploymenterror output. (#164, #168)- Bicep what-if PR preview — automated what-if diff posted as PR comment on infrastructure changes. (#100)
- SQLite via AzureFile (pre-Postgres interim) — volume mount, secret reference, replica lock, and snapshot config. Superseded by the Postgres migration. (#92)
Observability
- Startup URL log —
make_client()emitsINFO mom_bot.main Configured siege-web base URL: <url>at cold start, giving operators an instant cross-environment sanity check before any Discord gateway traffic. (#211) /healthzliveness probe — Container App liveness probe switched fromexec-type (rejected by ARM) tohttpGetagainst/healthz. (#88)
Pre-1.0 history: Initial pre-1.0 development — see git log and the merged PR history for full provenance.