fix vibe engine audit findings: pg.Pool, plan replan, dead exclusions, legacy engine removal

Backend:
- app.ts: switch shared pg.Client to pg.Pool with per-transaction clients (#205)
- v2.routes.ts: replace plan instead of appending on replan, fixing self-duplication (#206)
- session-director: populate recentExclusions, per-candidate ranking, batch repetition checks (#209/#211/#213/#215 + minor)
- db.service.ts: claim-fusion watermark, legacy recommendation_batch engine removed (#216/#219/#232)
- app.ts: drop test enqueue-job endpoint (#234)

Frontend:
- AudioEngine/Vibe/usePlaybackStore: dedupe completed feedback, gate feedback to vibe sessions, End Vibe stops playback, Keep toast, shuffle played-set (#207/#236/#237/#238/#239/#240)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kami
2026-07-17 13:22:06 +04:00
parent 9eb25311c8
commit c41316ee99
10 changed files with 465 additions and 922 deletions
+20 -7
View File
@@ -9,6 +9,8 @@ import { TrackRow } from '../components/TrackRow';
import { PageContainer } from '../components/PageContainer';
import type { Track } from '../types';
import { VibeTimeline } from '../components/VibeTimeline';
import { suppressAutoFeedback } from '../components/AudioEngine';
import { toast } from '../store/useToastStore';
const INITIAL_BATCH_SIZE = 5;
const PREFETCH_THRESHOLD = 3;
@@ -19,7 +21,7 @@ function bestEffort(p: Promise<unknown>): void {
}
export default function Vibe() {
const { currentTrack, queue, setQueue, playTrack, next: playNext } = usePlaybackStore();
const { currentTrack, queue, setQueue, playTrack, next: playNext, pause, setCurrentTrack } = usePlaybackStore();
const {
activeSessionId,
buffer,
@@ -117,23 +119,34 @@ export default function Vibe() {
if (idx > 0) {
setBuffer(buffer.slice(idx));
}
}, [activeSessionId, currentTrack]);
}, [activeSessionId, currentTrack, buffer, setBuffer]);
const handleKeep = useCallback(() => {
if (currentTrack) bestEffort(vibeService.feedback(currentTrack.id, 'promoted'));
if (currentTrack) {
bestEffort(vibeService.feedback(currentTrack.id, 'promoted'));
toast.success(`Kept "${currentTrack.title}"`);
}
}, [currentTrack]);
const handleDislike = useCallback(() => {
if (currentTrack) bestEffort(vibeService.feedback(currentTrack.id, 'disliked'));
if (currentTrack) {
bestEffort(vibeService.feedback(currentTrack.id, 'disliked'));
// AudioEngine would otherwise also record a 'skipped' on the track
// change caused by playNext() below — suppress that duplicate.
suppressAutoFeedback(currentTrack.id);
}
playNext();
}, [currentTrack, playNext]);
const handleEnd = useCallback(() => {
// V2 plan expires via Redis TTL (2h). No explicit end endpoint.
pause();
setQueue([]);
setCurrentTrack(null);
reset();
setEmpty(false);
setError(null);
}, [reset]);
}, [reset, pause, setQueue, setCurrentTrack]);
const upcoming = currentTrack
? (() => {
@@ -208,12 +221,12 @@ export default function Vibe() {
<div className="space-y-2">
<h2 className="text-sm font-semibold text-muted">Or pick a seed track</h2>
<ul className="max-h-72 space-y-1 overflow-y-auto">
{libraryTracks.map((track) => (
{libraryTracks.map((track, index) => (
<li key={track.id}>
<TrackRow
track={track}
queue={libraryTracks}
index={libraryTracks.findIndex((t) => t.id === track.id)}
index={index}
showActions={false}
/>
</li>