Skip to content

refactor(bot): move state-store cluster out of utils into services - #1987

Merged
LucasSantana-Dev merged 6 commits into
mainfrom
refactor/bot-state-stores-to-services
Aug 10, 2026
Merged

LucasSantana-Dev merged 6 commits into
mainfrom
refactor/bot-state-stores-to-services

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #1985 (not yet merged).

Summary

Side findings (filed separately, not fixed here)

Verification

  • npm run type:check --workspace=@lucky/bot — clean
  • npm run test:bot — 242/243 suites (same pre-existing skip, unchanged count)
  • npm run lint --workspace=@lucky/bot — 0 errors, 73 pre-existing warnings (unchanged)
  • Repo-wide grep after the move — only 2 expected self-referencing spec imports remain
  • Adversarial critic pass — surfaced bug(bot): 5 of 7 teardown blocks in tests/setup.ts silently do nothing #1986 (verified real, pre-existing, filed separately), confirmed the service.ts skip was correct

Summary by cubic

Moved music state stores from utils to services/musicManagement to centralize mutable state and simplify ownership. No behavior changes; updated imports and resolved merge conflicts after syncing with main.

  • Refactors
    • Relocated replenishSuppressionStore.ts, voteSkipStore.ts, and idleDisconnect.ts to services/musicManagement/.
    • Added services/musicManagement/idleDisconnect.spec.ts to cover scheduling, cancellation, and error paths.
    • Updated imports and jest.mock paths across commands, handlers, specs, and the dynamic import in tests/setup.ts; fixed relative paths in idleDisconnect.ts to ./watchdog and ../musicRecommendation/collaborativePlaylist; resolved conflicts in handlers/player/queueExhaustion.ts and services/musicManagement/idleDisconnect.ts.
    • Skipped TrackManagementService (no external consumers); filed separately.

Written for commit 468a5a0. Summary will update on new commits.

Review in cubic

First bounded slice of #1968: watchdog.ts holds a stateful class
(timers, private state Maps, recurring scan loop) mislabeled as a
util. Moves it to services/musicManagement/, updates the 13 import
sites, and deletes utils/misc/pathUtils.ts (confirmed zero import
sites, flagged safe-to-delete in the same audit).

The full split #1968 proposes (~20+ files into services/musicRecommendation/
and services/musicManagement/) is out of scope for this PR -- too large
for one mechanical move, per the issue's own recommendation to scope it.
This is the first unit; rest follows as separate PRs.
Second bounded slice of #1968: collaborativePlaylist.ts holds a
stateful class (CollaborativePlaylistService, module-level Map state)
mislabeled as a util, with zero internal coupling of its own. Moves
it to services/musicRecommendation/, updates 4 real import sites plus
2 jest.mock() string-literal mocks a prior grep-only pass missed.

Stacks on refactor/bot-watchdog-to-services (#1981, not yet merged) --
both touch idleDisconnect.ts and leave.ts.
Third bounded slice of #1968. Moves sessionSnapshots.ts to
services/musicRecommendation/, and sessionStartupRestore.ts +
namedSessions.ts to services/musicManagement/ -- both of the latter
depend on sessionSnapshots, now a cross-service import mirroring the
same-directory coupling that already existed. Updates 11 import
sites plus watchdog.ts/spec (from #1981) which also depends on
sessionSnapshots.

Found and filed separately while scoping this slice, not fixed here:
#1983 (restoreSessionsOnStartup is never called -- dead code) and
#1984 (utils/music/index.ts barrel has zero external consumers).

Stacks on refactor/bot-collaborative-playlist-to-services (#1982).
Fourth bounded slice of #1968. Moves replenishSuppressionStore.ts,
voteSkipStore.ts, and idleDisconnect.ts (all module-level mutable
state, zero internal coupling among themselves) to
services/musicManagement/. Updates 8 import sites plus a dynamic
import() in tests/setup.ts.

Deliberately skips service.ts (TrackManagementService) from the
original slice plan -- found zero external consumers while scoping
this move, filed separately as #1984 rather than relocating dead
code.

Found and filed separately, not fixed here: #1986 (tests/setup.ts
calls a cache-clear function that doesn't exist on
replenishSuppressionStore.ts -- pre-existing, silently swallowed by
a try/catch, unrelated to this move).

Stacks on refactor/bot-session-cluster-to-services (#1985).
@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@LucasSantana-Dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c3af182-b87f-4d84-a4fd-a9fc842f5a78

📥 Commits

Reviewing files that changed from the base of the PR and between 96d460c and 468a5a0.

📒 Files selected for processing (16)
  • packages/bot/src/functions/music/commands/voteskip.spec.ts
  • packages/bot/src/functions/music/commands/voteskip.ts
  • packages/bot/src/handlers/musicButtonHandler.spec.ts
  • packages/bot/src/handlers/musicButtonHandler.ts
  • packages/bot/src/handlers/player/queueExhaustion.ts
  • packages/bot/src/handlers/player/trackHandlers.spec.ts
  • packages/bot/src/handlers/player/trackHandlers.ts
  • packages/bot/src/handlers/webMusic/commandHandlers.spec.ts
  • packages/bot/src/handlers/webMusic/commandHandlers.ts
  • packages/bot/src/services/musicManagement/idleDisconnect.spec.ts
  • packages/bot/src/services/musicManagement/idleDisconnect.ts
  • packages/bot/src/services/musicManagement/replenishSuppressionStore.spec.ts
  • packages/bot/src/services/musicManagement/replenishSuppressionStore.ts
  • packages/bot/src/services/musicManagement/voteSkipStore.spec.ts
  • packages/bot/src/services/musicManagement/voteSkipStore.ts
  • packages/bot/tests/setup.ts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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.

@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

cubic-dev-ai[bot]
cubic-dev-ai Bot previously approved these changes Aug 10, 2026

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 15 files

Auto-approved: Pure import-path refactor moving three state stores to services/musicManagement; only import paths and formatting changed, no behavior changes. Tests pass.

Re-trigger cubic

…ores-to-services

# Conflicts:
#	packages/bot/src/handlers/player/queueExhaustion.ts
#	packages/bot/src/services/musicManagement/idleDisconnect.ts
@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Warnings
⚠️

User-facing change without a CHANGELOG.md update. Add a line under ## [Unreleased] if this should appear in release notes. (Or apply the skip-changelog label if this PR does not affect end users.)

Generated by 🚫 dangerJS against 468a5a0

idleDisconnect.ts had no spec file on main; sonarcloud's new-code
coverage gate failed on this pr (55.3%, needs 80%) because the move
surfaced 24 previously-untested lines as new code.
@github-actions github-actions Bot added size/l and removed size/m labels Aug 10, 2026

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 1 file (changes from recent commits).

Auto-approved: Pure refactor relocating three state stores; diffs show only import path updates, formatting, and a new idleDisconnect spec. No behavior or contract changes; tests and type-check pass per description.

Re-trigger cubic

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 252b8c7 into main Aug 10, 2026
45 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the refactor/bot-state-stores-to-services branch August 10, 2026 20:04
LucasSantana-Dev added a commit that referenced this pull request Aug 10, 2026
Stacked on #1987 (not yet merged).

## Summary
- Fifth bounded slice of #1968, the largest so far. Moves the entire
27-file `autoplay/` directory plus `candidateFallback.ts` (tightly
coupled to `autoplay/`, bundled so their sibling imports stay intact) to
`services/musicRecommendation/`.
- Keeps `autoplayManager.ts` as a thin facade at its original
`utils/music/` location — only its one internal import needed fixing, so
its 6 external consumers needed no changes at all.
- Also fixes `queueEditOps.ts`, `queueManipulation.ts`, and
`queueRescue.ts` (plus ~7 spec files), which reach into `autoplay/` from
`utils/music/`, and removes a now-redundant `utils/music/autoplay` entry
from `queueResolver.guard.spec.ts`'s architecture-guard target list
(already covered by the existing `services/musicRecommendation` entry
via recursion).

## Honesty about how this one went
This slice took five rounds of fix-and-recheck via typecheck/test runs,
not just grep — worth being upfront about given the size:
1. A bulk sed substring-collided `autoplayManager` with `autoplay`
(fixed by reverting 3 files back to their original path)
2. Two off-by-one relative-path depth errors (`../` instead of `../../`)
3. Three files (`queueEditOps.ts`, `queueManipulation.ts`,
`queueRescue.ts`) missed entirely on the first pass despite being
flagged by earlier research
4. Several `.spec.ts`-only `jest.mock()` paths missed (the `.ts` files
were fixed, their specs weren't)
5. One bare `require()` call inside a test body (`replenisher.spec.ts`)
that only surfaced by actually running the suite — grep for import/mock
patterns doesn't catch it

Each was caught by actually running `type:check`/`test:bot`, not assumed
fixed after the sed. Two separate adversarial critic passes ran on this
diff (one before the final green run turned something up, one after) —
both eventually landed ACCEPT.

## Verification
- [x] `npm run type:check --workspace=@lucky/bot` — clean
- [x] `npm run test:bot` — 242/243 suites (same pre-existing skip,
unchanged count)
- [x] `npm run lint --workspace=@lucky/bot` — 0 errors, 73 pre-existing
warnings (unchanged)
- [x] Repo-wide grep for old paths — zero stale references (2 apparent
hits were false positives: an unrelated same-named
`functions/music/commands/autoplay/` directory, and files now correctly
colocated with the moved `autoplay/`)
- [x] Two adversarial critic passes — ACCEPT

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Moved the autoplay engine from utils to services to align with our
services architecture without changing behavior. `autoplayManager.ts`
remains a thin facade so existing callers don’t need changes.

- **Refactors**
- Moved all `utils/music/autoplay/*` files and `candidateFallback.ts` to
`services/musicRecommendation/autoplay/` (including `audioFeatures.ts`
and `vcWeights.ts`); removed old copies.
- Updated imports in commands (`previous`, `skip`, `stop`), player
handlers, Last.fm exports, queue helpers/tests, and `jest.mock` paths.
- Routed telemetry through
`services/musicRecommendation/autoplay/recommendationSourceMapping` and
`recommendationBasis`.
- Kept `utils/music/autoplayManager.ts` re-exporting from
`services/musicRecommendation/autoplay/index`.
- Removed the redundant `utils/music/autoplay` entry from the
architecture guard.
- Updated Sonar CPD exclusion paths to the new locations to prevent
duplicate-code violations.

<sup>Written for commit 1cf31fa.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1988?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
LucasSantana-Dev added a commit that referenced this pull request Sep 10, 2026
First slice of #1991. Does not close it: four subdirectories remain.

## Why this is not a utils module

`utils/music/trackUtils/` is a stateful service tree:

- `TrackUtils` (barrel) holds a `TrackProcessor`
- `TrackProcessor` owns a `TrackCacheManager` and a cleanup timer
(`startCacheCleanup`)
- `TrackCacheManager` wraps an `LRUCache`
- the barrel exports a module-level singleton: `export const trackUtils
= new TrackUtils()`

Same pattern as every finding in #1968, which established
`services/musicManagement/` as the destination across its 7 slices
(#1981, #1982, #1985, #1987, #1988, #1989, #1990).

## Surface

Only `getTrackInfo` escapes the directory, so the external surface is
four files:

| file | reference |
|---|---|
| `functions/music/commands/queue/queueDisplay.ts` | `import {
getTrackInfo }` |
| `functions/music/commands/queue/queueStats.ts` | `import {
getTrackInfo }` |
| `functions/music/commands/queue/queueDisplay.spec.ts` |
`jest.mock('...')` string literal |
| `functions/music/commands/queue/queueStats.spec.ts` |
`jest.mock('...')` string literal |

The `jest.mock()` string literals do not move with an import rewrite.
#1991 flags these as the most common miss in the #1968 slices, and the
same trap applied *inside* the moved specs, which mock
`'../titleComparison'` and `require()` it in five more places.

## Path arithmetic

Both directories sit three levels under `src/`, so relative depth is
unchanged. Only the one sibling import needed rewriting:

```
'../titleComparison'  ->  '../../../utils/music/titleComparison'
```

That is services depending on utils, which is the correct direction. The
inversion #1991 is about is utils depending on services, which this
avoids by moving the whole tree rather than `cacheManager.ts` alone.
`titleComparison` is itself on the #1991 list and moves in a later
slice.

## Verification

- `packages/bot` full suite: 273 suites passed, 3586 tests passed, 1
skipped, matching the pre-move baseline exactly (pure move, no test
added or removed)
- `npx tsc --noEmit`: clean
- `npx madge --circular --extensions ts packages/bot/src`: `No circular
dependency found!`
- `npm run build --workspace=packages/bot`: succeeds
- repo-wide grep for `utils/music/trackUtils` across
ts/tsx/js/json/yml/md: no remaining references

## Remaining in #1991

`search/engineManager.ts`, `search/providerHealth.ts` (8+ external
consumers, the largest slice), `titleComparison/service.ts`,
`youtubeErrorHandler/analyzer.ts`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Relocates the stateful `trackUtils` service from `utils/music/` to
`services/musicManagement/`, matching the destination established in
`#1968` and continuing the `#1991` refactor. Since only the queue
commands consume `getTrackInfo`, the external change is limited to
updating their imports and the `jest.mock()` path literals in their
specs.

**Notes**
- Moved specs also updated their `jest.mock('../titleComparison')` and
`require()` calls to the new relative path.
- `titleComparison` remains in `utils` and will move in a later slice;
`search/engineManager`, `search/providerHealth`, and
`youtubeErrorHandler/analyzer` are still pending.
- Full test suite and `tsc` pass with no circular dependencies.

<sup>Written for commit 5a734c9.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/2353?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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