Skip to content

refactor(bot): break Cluster C autoplay cycles (#871 PR 3) - #888

Merged
LucasSantana-Dev merged 17 commits into
release/v2.12.0from
refactor/bot-circular-deps-pr3-autoplay
May 16, 2026
Merged

LucasSantana-Dev merged 17 commits into
release/v2.12.0from
refactor/bot-circular-deps-pr3-autoplay

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented May 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

PR 3 of #871 (bot circular-deps refactor). 10 → 4 cycles in packages/bot/src (60% reduction, from the post-PR-2 baseline).

Implemented via /three-man-team (Architect: Opus / Builder: Sonnet / Reviewer: Sonnet):

  • Phase 1: Extracted 5 pure utility functions (normalizeText, stripFeaturing, normalizeTrackKey, extractSpotifyTrackId, extractYouTubeVideoId) into the new dependency-free packages/bot/src/utils/music/autoplay/scoringUtils.ts. Five autoplay siblings (candidateCollector, candidateScorer, spotifyRecommender, diversitySelector, lastFmSeeder) now import from there instead of round-tripping through queueManipulation. Backward-compatible re-export retained on queueManipulation for unrelated consumers.
  • Phase 2: Inverted the replenisher ↔ trackHandlers cycle via dependency injection. New SkipStateProvider type in autoplay/skipStateProvider.ts. replenishQueue takes an optional skipStateProvider callback (backwards-compatible — defaults to no-op). trackHandlers injects getRecentSkipCount at the call sites.
  • Phase 3: Lastfm export hygiene — moved lastFmSeeds to import directly from lastFmApi.ts instead of through lastfm/index.ts, breaking the cycle at its source. Added autoplay/lastfmExports.ts barrel.
  • Bonus: autoplayAudit ↔ candidateCollector cycle broken by reordering the ScoredTrack type import.

Validation

  • npx madge --circular --extensions ts packages/bot/src: 4 cycles (down from 10)
  • npx tsc --noEmit: clean (modulo pre-existing prom-client module-resolution error, unrelated)
  • Jest in affected dirs (scoringUtils|replenisher|trackHandlers|candidateCollector|candidateScorer|diversitySelector|lastFm|autoplayAudit): 2846/2846 passing

Remaining cycles (follow-up work)

  1. Cycle 1 — types/CustomClient ↔ models/Command ↔ types/CommandData: type-only, erased by tsc, intentionally deferred (documented in ADR + plan).
  2. Cycle 2 — queueManipulation → candidateCollector → autoplayAudit → diversitySelector → queueManipulation: newly-visible after Phase 1; back-edge is diversitySelector → queueManipulation for markAsAutoplayTrack. Needs another extraction.
  3. Cycle 3 — candidateCollector ↔ spotifyRecommender: residual function-level mutual dep.
  4. Cycle 4 — queueManipulation ↔ replenisher: residual; replenisher still imports enrichWithAudioFeatures/getTrackAudioFeatures/interleaveByArtist/buildVcContributionWeights from queueManipulation.

A follow-up issue will be filed for cycles 2–4.

Refs

Test plan

  • madge cycle count drops 10 → 4
  • tsc clean
  • Jest green in affected suites
  • CI green

Summary by CodeRabbit

  • Refactor
    • Reorganized autoplay module structure to improve code organization and utility sharing across scoring components.
    • Consolidated text normalization and media ID extraction utilities into a centralized scoring module.
    • Updated internal module imports to use new utility entry points.
    • Adjusted queue replenishment logic to use callback-based skip state injection.

Review Change Stack

Extract utility functions to neutral scoringUtils module to break queueManipulation hub cycles.
Update candidateCollector to import normalizeTrackKey from scoringUtils instead of queueManipulation.
Update candidateScorer to import normalizeTrackKey and normalizeText from scoringUtils.
Update spotifyRecommender to import utilities from scoringUtils and candidateCollector.
Update diversitySelector to import extractYouTubeVideoId from scoringUtils instead of local duplicate.
Update lastFmSeeder to import normalizeTrackKey from scoringUtils instead of queueManipulation.
Move normalizeText and stripFeaturing definitions before normalizeTrackKey to ensure proper function ordering.
…y from scoringUtils

The refactored code now imports normalizeTrackKey from ./scoringUtils instead of
../queueManipulation. Update the jest mock to target the correct module.
… scoringUtils

The refactored code now imports normalizeTrackKey from ./scoringUtils instead of
../queueManipulation. Update the jest mock to target the correct module.
candidateScorer.ts now imports normalizeText and normalizeTrackKey from
./scoringUtils. Add jest mocks for these functions to allow tests to run.
…malizeText mock

- Change calculateRecommendationScore mock from ../queueManipulation to ./candidateScorer
- Add normalizeText mock to scoringUtils mock
The code now imports calculateRecommendationScore from candidateScorer, which
itself uses normalizeText from scoringUtils.
The mock normalizeText function now properly implements NFKC normalization,
lowercase conversion, and alphanumeric filtering to match the actual
scoringUtils.normalizeText behavior.
The original tests didn't mock normalizeText since it was imported from
queueManipulation as a real function. After refactoring, normalizeText is
imported from scoringUtils and should also use the real implementation.
Remove the overly-specific mocks that were causing test failures.
@vercel

vercel Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment May 16, 2026 4:33am

Request Review

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LucasSantana-Dev has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@LucasSantana-Dev
LucasSantana-Dev merged commit 22b793d into release/v2.12.0 May 16, 2026
22 of 25 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the refactor/bot-circular-deps-pr3-autoplay branch May 16, 2026 04:34
@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 545ac6c8-90f7-4d4c-9736-dd7bf2b3b3d9

📥 Commits

Reviewing files that changed from the base of the PR and between 0d33d10 and e1ace22.

⛔ Files ignored due to path filters (3)
  • landing-check.png is excluded by !**/*.png
  • landing-desktop.png is excluded by !**/*.png
  • landing-mobile.png is excluded by !**/*.png
📒 Files selected for processing (17)
  • packages/bot/src/handlers/player/trackHandlers.ts
  • packages/bot/src/lastfm/index.ts
  • packages/bot/src/utils/music/autoplay/autoplayAudit.ts
  • packages/bot/src/utils/music/autoplay/candidateCollector.spec.ts
  • packages/bot/src/utils/music/autoplay/candidateCollector.ts
  • packages/bot/src/utils/music/autoplay/candidateScorer.spec.ts
  • packages/bot/src/utils/music/autoplay/candidateScorer.ts
  • packages/bot/src/utils/music/autoplay/diversitySelector.ts
  • packages/bot/src/utils/music/autoplay/lastFmExports.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeder.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeds.spec.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeds.ts
  • packages/bot/src/utils/music/autoplay/replenisher.ts
  • packages/bot/src/utils/music/autoplay/scoringUtils.ts
  • packages/bot/src/utils/music/autoplay/skipStateProvider.ts
  • packages/bot/src/utils/music/autoplay/spotifyRecommender.ts

📝 Walkthrough

Walkthrough

The PR refactors the music autoplay scoring pipeline by extracting shared utilities into a new scoringUtils module, introducing a SkipStateProvider type for dependency injection, creating a lastFmExports decoupling barrel, and updating import sources across scoring, seeding, and recommendation modules. Most changes are implementation logic refactoring and test formatting without altering observable behavior.

Changes

Autoplay Pipeline Refactoring

Layer / File(s) Summary
Scoring Utilities & Skip State Provider
packages/bot/src/utils/music/autoplay/scoringUtils.ts, packages/bot/src/utils/music/autoplay/skipStateProvider.ts
New scoringUtils module exports text normalization (normalizeText), author cleanup (stripFeaturing), deduplication key generation (normalizeTrackKey), and media ID extraction (extractSpotifyTrackId, extractYouTubeVideoId). New skipStateProvider module exports callback type for skip-state injection to avoid circular dependencies.
Skip State Injection in Replenishment
packages/bot/src/utils/music/autoplay/replenisher.ts, packages/bot/src/handlers/player/trackHandlers.ts
replenisher.ts signature updated to accept optional skipStateProvider callback, threaded into _replenishQueue for session mood detection. trackHandlers.ts passes guild skip-count provider into all replenishQueue calls during autoplay start, retry, and finish flows.
Module Organization & Decoupling
packages/bot/src/lastfm/index.ts, packages/bot/src/utils/music/autoplay/lastFmExports.ts, packages/bot/src/utils/music/autoplay/lastFmSeeds.ts, packages/bot/src/utils/music/autoplay/autoplayAudit.ts, packages/bot/src/utils/music/autoplay/diversitySelector.ts
New lastFmExports barrel re-exports consumeLastFmSeedSlice and consumeBlendedSeedSlice; lastfm/index.ts redirects re-exports through barrel; import sources updated across modules to use new utilities and export paths; extractYouTubeVideoId migrated from diversitySelector to scoringUtils.
Candidate Collection & Scoring Integration
packages/bot/src/utils/music/autoplay/candidateCollector.ts, packages/bot/src/utils/music/autoplay/candidateScorer.ts, packages/bot/src/utils/music/autoplay/candidateCollector.spec.ts, packages/bot/src/utils/music/autoplay/candidateScorer.spec.ts
candidateCollector and candidateScorer updated to import scoring utilities from scoringUtils; refactored into multi-line explicit forms for upsertScoredCandidate, scoring context, and conditional branches without changing control flow. Test suites reformatted to mock new utility locations and expand fixtures into multi-line literals while preserving assertions.
LastFM Seeding & Spotify Recommender Updates
packages/bot/src/utils/music/autoplay/lastFmSeeder.ts, packages/bot/src/utils/music/autoplay/spotifyRecommender.ts, packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts, packages/bot/src/utils/music/autoplay/lastFmSeeds.spec.ts
lastFmSeeder and spotifyRecommender updated to import from new utility and re-export locations; candidate scoring calls reformatted to multi-line structures with unchanged score logic. Extensive test reformatting to match new mock paths and expand argument lists while keeping assertions identical.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • LucasSantana-Dev/Lucky#659: Updates autoplay code to consume ScoredTrack from extracted diversitySelector module, directly depends on this PR's type import restructuring.

  • LucasSantana-Dev/Lucky#562: Both PRs modify trackHandlers.ts to extend replenishQueue calls during autoplay; this PR adds skip-state provider injection while the retrieved PR introduced finishedTrack parameter passing.

Suggested labels

bot, enhancement, size/xl

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/bot-circular-deps-pr3-autoplay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

LucasSantana-Dev added a commit that referenced this pull request May 21, 2026
## Summary

Cut v2.13.0 of Lucky. Bumps root + 4 workspaces from `2.11.0` → `2.13.0`
(skipping the archived `2.12.0`) and promotes the CHANGELOG
`[Unreleased]` block to `[2.13.0] - 2026-05-21`.

## Headline changes since v2.11.0

**Added**
- Guild Automation Module Executor seam + AutoMessages pilot (#901)
- Sentry React SDK + Router v7 tracing/replay on frontend (#876)
- Prometheus `/metrics` on backend (#875) + bot (#873)
- Guild join/leave history tracking (#872)
- Trivy image-scan on docker-publish, Phase A audit-only (#883)
- Self-hosted developer-tooling register on landing page (#868)

**Changed**
- Backend migrated to Zod 4 API (#919) — unblocked the CVE patch + ended
the lockfile fragility loop
- 3 bot circular-deps clusters broken (#885, #886, #888)

**Fixed**
- brace-expansion DoS + ws uninit-memory CVEs patched (#921)
- nginx-alpine CVEs (#881)
- CI postinstall rate limit + madge actionlint (#878, #905)

Full list in CHANGELOG.md.

## Next steps (after this PR merges)

1. Open `release/v2.13.0 → main` PR with merge-commit method
2. Tag `v2.13.0` on the merge commit
3. Cut next `release` (homelab-style bare branch) — Lucky's bare-release
migration is still pending the user removing protection on
`release/v2.11.0`
@LucasSantana-Dev LucasSantana-Dev mentioned this pull request May 21, 2026
3 tasks
LucasSantana-Dev added a commit that referenced this pull request May 21, 2026
## Release v2.13.0

Promotes \`release/v2.13.0\` to \`main\` for the v2.13.0 cut.

**$AHEAD commits across all merged PRs since v2.11.0 ship.**

(Skipping v2.12.0 — the branch existed but its work was rolled forward
into v2.13.0 alongside this session's Zod migration + CVE patches +
standards adoption.)

## Headline changes

**Added** — Guild Automation Module Executor pilot (#901), Sentry
frontend (#876), Prometheus metrics on bot+backend (#873, #875), guild
membership history (#872), Trivy image-scan Phase A (#883), landing
redesign (#868).

**Changed** — Backend migrated to Zod 4 API (#919), 3 bot circular-deps
clusters broken (#885/#886/#888).

**Fixed** — brace-expansion + ws moderate CVEs (#921), nginx-alpine CVEs
(#881), CI postinstall rate limit (#878), madge actionlint (#905).

**Internal** — shared coverageThreshold gate (#909/#914),
Feature-removal sweep checklist + dangerfile guard (#908/#913),
monitoring network, AI-doc policy, 4 new ADRs.

Full list in [CHANGELOG.md](./CHANGELOG.md).

## Merge method

This PR should land via **merge commit** (NOT squash) to preserve the
individual PR SHAs in main's history. After merge:

1. Tag \`v2.13.0\` on the merge commit
2. Create GitHub release with notes from CHANGELOG.md
3. Fast-forward \`release/v2.13.0\` to match the new main HEAD

## Test plan

- [ ] All 30 checks green except infra (snyk plan cap)
- [ ] Verify \`gh pr view 922 --json mergeCommit\` shows the chore-bump
commit on release tip
- [ ] After merge: confirm \`origin/main\` contains the full $AHEAD
commits

This branch was successfully deployed

1 active deployment
Preview — e1ace22e Deployed May 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant