claude-home/major-domo/release-2026.3.20.md
Cal Corum cd57645dd0
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 3s
docs: sync KB — 2026-03-20.md,release-2026.3.20.md
2026-03-20 22:00:43 -05:00

5.8 KiB

title description type domain tags
Major Domo v2 Release — 2026.3.20 Performance release: parallelized API calls, caching improvements, CI overhaul to tag-triggered releases, async hotfix, and chart path fix. reference major-domo
discord
major-domo
deployment
release-notes
docker
ci

Major Domo v2 Release — 2026.3.20

Date: 2026-03-20 Tags: 2026.3.10, 2026.3.11 (bugfix) Image: manticorum67/major-domo-discordapp:production Server: akamai (/root/container-data/major-domo) Deploy method: .scripts/release.sh → CI → .scripts/deploy.sh

Release Summary

Performance-focused release with 12 merged PRs covering parallelized API calls, caching improvements, CI workflow overhaul, and a production hotfix. Also retired the next-release staging branch in favor of direct-to-main merges with tag-triggered releases.

Hotfix During Release

PR #117 — ScorecardTracker async mismatch. PR #106 added await to all scorecard_tracker method calls across scorebug.py, live_scorebug_tracker.py, and cleanup_service.py, but the tracker methods themselves were still synchronous. This caused TypeError: object NoneType can't be used in 'await' expression on /scorebug and TypeError: object list can't be used in 'await' expression in the background scorebug update loop. Fixed by making all 6 public ScorecardTracker methods async and adding 5 missing awaits in cleanup_service.py.

Root cause: PR #106 was created by an issue-worker agent that modified callers without modifying the tracker class. The async tracker conversion existed only in uncommitted working tree changes that were never included in any PR.

Lesson: Issue-worker agent PRs that add await to calls must verify the called methods are actually async — not just that the callers compile.

Infrastructure Changes

CI: Tag-triggered releases (PRs #110, #113)

Replaced branch-push CI with tag-push CI. Merging to main no longer triggers a Docker build.

  • Before: Push to main or next-release → auto-build → auto-tag CalVer
  • After: Push CalVer tag (git tag 2026.3.11 && git push --tags) → build → Docker image tagged :version + :production

Also removed the pull_request trigger that was building Docker images on every PR branch push.

Release and deploy scripts (PRs #114, #115)

  • .scripts/release.sh — auto-generates next CalVer tag, shows changelog, confirms, pushes tag
  • .scripts/deploy.sh — updated to use SSH alias (ssh akamai) and :production image tag

Docker volume split (PR #86)

Split the single ./storage:/app/data volume into:

  • ./storage/major-domo-service-creds.json:/app/data/major-domo-service-creds.json:ro (credentials)
  • ./storage:/app/storage:rw (state files)

Production compose on akamai was updated manually before deploy. All 5 tracker default paths changed from data/ to storage/.

Retired next-release branch

All references to next-release removed from CLAUDE.md and CI workflow. New workflow: branch from main → PR to main → tag to release.

Performance Changes

Parallelized API calls

PR What Impact
#88 schedule_service: get_team_schedule, get_recent_games, get_upcoming_games use asyncio.gather() Up to 18 sequential HTTP requests → concurrent
#90 Team lookups in /publish-scorecard, /scorebug, /injury, trade validation 2 sequential calls → concurrent per location
#102 asyncio.gather() across multiple command files Broad latency reduction

Caching

PR What Impact
#99 Cache user team lookup in player_autocomplete with 60s TTL, reduce Discord limit to 25 Faster autocomplete on repeat use
#98 Replace Redis KEYS with SCAN for cache invalidation Non-blocking invalidation

Micro-optimizations

PR What Impact
#93 Use channel.purge() instead of per-message message.delete() loops 1 API call vs up to 100 per channel clear
#96 Replace json.dumps(value) probe with isinstance() in JSON logger Eliminates full serialization on every log call
#97 Cache inspect.signature() at decoration time in all 3 decorators Introspection cost paid once, not per-call

Cleanup

PR What
#104 Remove dead @self.tree.interaction_check decorator block and duplicate self.maintenance_mode assignment in bot.py
#103 Remove unused weeks_ahead parameter from get_upcoming_games

Test Coverage

  • 16 new tests for schedule_service (test_services_schedule.py — first coverage for this service)
  • Tests use existing GameFactory/TeamFactory from tests/factories.py
  • 2 existing scorebug tests updated for async tracker methods
  • Full suite: 967+ tests passing

Bugfix: 2026.3.11

PR #119chart_service.py CHARTS_FILE path still pointed to data/charts.json after PR #86 moved state files to storage/. The /charts autocomplete returned no results because the file was at /app/storage/charts.json but the code read from /app/data/charts.json. One-line path fix.

Root cause: PR #86 updated the 5 tracker classes but missed ChartService, which uses a class-level Path constant instead of the __init__ pattern used by the trackers.

Lesson: When moving file paths across volumes, grep for the old path across the entire codebase — not just the files being modified.

Deployment Notes

  • Production compose updated on akamai before deploy (volume split for PR #86)
  • Image tag changed from :latest to :production
  • Deployed three times total:
    1. 2026.3.10 — initial release (broken /scorebug and scorebug tracker)
    2. 2026.3.10 — re-tagged after hotfix #117 (async mismatch)
    3. 2026.3.11 — chart path fix (#119)
  • Final deploy confirmed healthy — all background tasks started, gateway connected