feat: taste onboarding for new listeners (M6)
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Failing after 1m29s
ci / ts / ts (packages + web) (push) Successful in 49s
ci / api / api (push) Successful in 1m25s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 48s
ci / recommender / recommender (push) Successful in 21s

- GET /onboarding offers the library's top artists (with album covers)
  and genres and says whether to show it (not done and < 10 plays);
  PUT /me/onboarding stores the choice or a skip (migration 00009).
- The recommender seeds the taste from it while the user has fewer than
  30 scored tracks: chosen artists' tracks +1, chosen genres +0.5.
- Web: onboarding replaces the wave on the home page until done or
  skipped; artist grid with covers, genre chips, at least 3 artists.
- e2e journey and the recommender DB test cover it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 22:35:43 +03:00
1 parent ff0aafdb1e
commit fb07f962eb
14 files changed
+1137 -238

No files matched your search

+101
View File
@@ -1066,6 +1066,43 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/onboarding": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Онбординг вкуса — исполнители и жанры библиотеки для выбора
* @description `required` — показать онбординг: пользователь его не проходил и не пропускал, а своей истории почти нет.
*/
get: operations["getOnboarding"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/me/onboarding": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
/** Сохранить выбор онбординга (или пропуск — пустые списки) */
put: operations["saveOnboarding"];
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/wave/options": {
parameters: {
query?: never;
@@ -1181,6 +1218,26 @@ export interface paths {
export type webhooks = Record<string, never>;
export interface components {
schemas: {
Onboarding: {
required: boolean;
artists: {
/** Format: uuid */
id: string;
name: string;
track_count: number;
/** @description Обложка самого большого альбома исполнителя */
cover_url: string | null;
cover_colors: string[];
}[];
genres: {
name: string;
track_count: number;
}[];
};
OnboardingChoice: {
artist_ids: string[];
genres: string[];
};
UserRef: {
/** Format: uuid */
id: string;
@@ -3599,6 +3656,50 @@ export interface operations {
default: components["responses"]["Problem"];
};
};
getOnboarding: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Варианты и состояние */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Onboarding"];
};
};
default: components["responses"]["Problem"];
};
};
saveOnboarding: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["OnboardingChoice"];
};
};
responses: {
/** @description Сохранено */
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
default: components["responses"]["Problem"];
};
};
getWaveOptions: {
parameters: {
query?: never;