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
+2 -2
View File
@@ -74,7 +74,7 @@ export default async function v2Routes(fastify: FastifyInstance, options: { dbSe
// Replan if running low
if (active.plan.length < 5) {
const refill = await director.replan(userId, active.sessionId, active.plan, [next.trackId], active.seedTrackId ?? undefined);
active.plan.push(...refill);
active.plan = refill;
}
await setActivePlan(userId, active);
@@ -111,7 +111,7 @@ export default async function v2Routes(fastify: FastifyInstance, options: { dbSe
if (active) {
const playedTrackIds = [trackId];
const refill = await director.replan(userId, active.sessionId, active.plan, playedTrackIds, active.seedTrackId ?? undefined);
active.plan.push(...refill);
active.plan = refill;
await setActivePlan(userId, active);
}
-76
View File
@@ -1,76 +0,0 @@
import { FastifyInstance } from 'fastify';
import { DbService } from '../services/db.service.js';
export default async function vibeRoutes(fastify: FastifyInstance, options: { dbService: DbService }) {
const { dbService } = options;
// Start a new vibe session
fastify.post('/start', async (request, reply) => {
const { seedTrackId } = request.body as { seedTrackId: string };
if (!seedTrackId) {
return reply.code(400).send({ error: 'seedTrackId is required' });
}
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const batchId = await dbService.createVibeSession(userId, seedTrackId);
return reply.send({ batchId });
});
// Get the next chunk of tracks for the active session
fastify.get('/next', async (request, reply) => {
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const activeSession = await dbService.getActiveVibeSession(userId);
if (!activeSession) {
return reply.code(404).send({ error: 'No active vibe session found' });
}
const tracks = await dbService.getNextVibeChunk(activeSession.batchId);
// Update the session timestamp to keep it alive
await dbService.updateVibeSession(activeSession.batchId);
return tracks;
});
// Start/return a chunk seeded by a genre (id or name) — no active session required.
fastify.get('/from-genre', async (request, reply) => {
const { genre } = request.query as { genre?: string };
if (!genre) {
return reply.code(400).send({ error: 'genre query param is required' });
}
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const tracks = await dbService.getVibeChunkFromGenre(genre, userId);
return tracks;
});
// Current ACTIVE batch metadata for the user, or 404.
fastify.get('/current', async (request, reply) => {
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const session = await dbService.getCurrentVibeSession(userId);
if (!session) {
return reply.code(404).send({ error: 'No active vibe session found' });
}
return reply.send(session);
});
// Heartbeat: keep the active batch alive by bumping last_interaction_at.
fastify.post('/heartbeat', async (request, reply) => {
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const updated = await dbService.heartbeatVibeSession(userId);
if (!updated) {
return reply.code(404).send({ error: 'No active vibe session found' });
}
return reply.send({ status: 'ok' });
});
// End the vibe session
fastify.post('/end', async (request, reply) => {
const userId = (request.headers['x-user-id'] as string) || '00000000-0000-0000-0000-000000000000';
const activeSession = await dbService.getActiveVibeSession(userId);
if (activeSession) {
await dbService.endVibeSession(activeSession.batchId);
}
return reply.send({ status: 'session_ended' });
});
}