diff --git a/.roo/memory-bank/activeContext.md b/.roo/memory-bank/activeContext.md
index 52fe91b..f4d3443 100644
--- a/.roo/memory-bank/activeContext.md
+++ b/.roo/memory-bank/activeContext.md
@@ -2,12 +2,62 @@
## Task State
-- task_id: TASK-HIDE-TRACK-3-FIX
+- task_id: TASK-PROFILE-ULID-BUG
- status: success
-- parent_task: TASK-HIDE-TRACK
-- summary: **Чекбокс `hide_start_end` + `changeHideStartEnd` + i18n ключ восстановлены в WorkoutItem.vue (потеряны в git stash при TASK-HIDE-TRACK-4).**
+- summary: **PATCH /profiles/{id} возвращает 500 (смена имени не персистится). Root cause: `default=str(ULID())` в `profiles.py:32` и `users.py:20` — ULID считается один раз при импорте; `DBProfileRepository.update` включает `id` в SET → `IntegrityError`. Фикс: `default_factory=lambda: str(ULID())` + defensive `del new_dict["id"]` + новый API-тест (PATCH → 200 + персистентность имени).**
- next task: —
+## TASK-PROFILE-ULID-BUG: план делегирования
+
+**Scope:** `../backend/` (3 файла фикса + 1 новый тест).
+
+**Изменения:**
+
+1. `../backend/app/modules/users/domains/profiles.py:32` — `default=str(ULID())` → `default_factory=lambda: str(ULID())` (обязательный фикс).
+2. `../backend/app/modules/users/domains/users.py:20` — та же замена (оборона в глубине).
+3. `../backend/app/modules/users/repositories/profile.py` (`DBProfileRepository.update`, ~106-108) — после `del new_dict["created_at"]` добавить `new_dict.pop("id", None)` (PK не должен участвовать в UPDATE).
+4. Новый тест `../backend/tests/test_api_profile.py` (паттерн `test_api_general.py`, фикстуры `client` + `signin_data` из `conftest.py`):
+ - signup → получить `profile_id` (из `data["profile"]["id"]`) + token;
+ - `PATCH /api/v0/profiles/{profile_id}` с `{"profile": {"first_name": "New", "surname": "Family"}}` → 200;
+ - `GET /api/v0/profiles/{profile_id}` → `profile.first_name == "New"` и `surname == "Family"`;
+ - второй PATCH (ещё раз) → 200 + персистится (ловит регрессию «один ULID на процесс»).
+
+**Acceptance criteria:**
+
+1. `./venv/bin/python -m pytest tests/test_api_profile.py -v` → passed.
+2. `./venv/bin/python -m pytest tests/ -v` → green (зафиксировать diff, если есть пре-экзистинг failing).
+3. `./venv/bin/python -m mypy app/modules/users/ --explicit-package-bases --no-error-summary` → 0 errors.
+4. `ruff check` (по `ruff.toml`) → 0 errors.
+
+**Не в scope (follow-up):** обновление `../integration/tests/workout-upload-flow.spec.ts` (добавить assert персистентного имени, убрать comment KNOWN BACKEND BUG) и `../integration/README.md` — отдельная мини-задача после success.
+
+## ✅ CODER SUCCESS REPORT — TASK-PROFILE-ULID-BUG
+
+- `../backend/app/modules/users/domains/profiles.py:32` — `default=str(ULID())` → `default_factory=lambda: str(ULID())` (ULID теперь вычисляется на каждый `Profile()`, а не один раз при импорте).
+- `../backend/app/modules/users/domains/users.py:20` — та же замена (оборона в глубине).
+- `../backend/app/modules/users/repositories/profile.py` (`DBProfileRepository.update`) — добавлено `new_dict.pop("id", None)` после `del new_dict["created_at"]` (PK не участвует в UPDATE — defensive).
+- `../backend/tests/test_api_profile.py` — 2 API-теста: (1) PATCH с `{"profile": {"first_name": "New", "surname": "Family"}}` → 200 + GET → персистентность имени/фамилии; (2) повторные PATCH → 200 + GET `first_name == "Second"` (ловит регрессию «один ULID на процесс»).
+- Verified: `./venv/bin/python -m pytest tests/test_api_profile.py -v` → **2 passed**.
+- Verified: `./venv/bin/python -m pytest tests/ -v` → **56 passed, 10 errors** — все 10 errors **пре-экзистинг** (проверено через `git stash`: `NotImplementedError: No support for ALTER of constraints in SQLite dialect` в fixture-setup `test_db_profile.py`/`test_db_workout.py`/`test_db_workout_photos.py` — миграции на SQLite, не связаны с фиксом; до/после идентично).
+- Verified: `./venv/bin/python -m mypy app/modules/users/ --explicit-package-bases --no-error-summary` → **0 errors**.
+- Verified: `./venv/bin/python -m ruff check app/modules/users/ tests/test_api_profile.py` → **All checks passed** (полный `ruff check` по репо: единственная находка F401 в `migration/versions/2025-09-25-0000-...-hide-start-end.py:8` — пре-экзистинг, коммит `ff5f468`, вне scope).
+- Follow-up (отдельная задача): update `../integration/tests/workout-upload-flow.spec.ts` (assert персистентного имени вместо KNOWN BACKEND BUG comment) + `../integration/README.md`.
+
+## ✅ CODER SUCCESS REPORT — TASK-TEST-WORKOUT-UPLOAD
+
+- `../integration/tests/workout-upload-flow.spec.ts` создан: 5 последовательных тестов (signup UI+профиль, upload .fit + resolve id, make public→GET public=200, hide_start_end→authed GET body true, add photo→img в `.workout-photos-grid`).
+- Ключевое решение: **стабильный owner** (`itest-workout-owner@example.com`, idempotent signup/signin) для Части 2 — dedup по content-hash ГЛОБАЛЕН, а `GET /api/v0/workouts` scoped по `created_by`, поэтому уникальный юзер на повторном прогоне всегда падал бы в dedup-ветку с пустым своим списком. Part 1 (профиль) — уникальный юзер через UI (genuine signup).
+- Детальный URL `/workouts/workouts/:id` (сверено с `router/index.ts`: вложенный `workouts` → `workouts/:id`); навигация `domcontentloaded` (Yandex map держит соединения, `networkidle` не стабилизируется).
+- `../integration/README.md` обновлён (секция + notes про dedup и про backend-баг имени).
+- Verified: `npx playwright test tests/workout-upload-flow.spec.ts` → **5 passed**, стабильно ×3 (dedup-путь) + прогон на свежей DB (redirect-путь). `yarn build` (vue-tsc --noEmit) exit 0.
+- Cleanup: диагностический `../integration/tests/_diag.spec.ts` удалён; `npx playwright test --list` → 16 tests в 5 файлах, `_diag` отсутствует.
+
+## ⚠️ Discovered (NOT fixed, out-of-scope test-only)
+
+- **Backend-баг сменy имени:** `PATCH /api/v0/profiles/{id}` (ветка `profile`) возвращает 500. Причина: `../backend/app/modules/users/domains/profiles.py:32` — `id: str = field(default=str(ULID()), ...)` вычисляет ULID ОДИН раз при импорте (не `default_factory`), ORM-UPDATE переписывает PK → `IntegrityError: duplicate key (profiles_pkey)`. SPA показывает toast синхронно (до чтения ответа), поэтому имя НЕ персистится. Spec асертит SPA-toast (реальное UI-поведение), персистентное имя задокументировано, но не asserted.
+- **Стек должен быть свежим:** колонка `hide_start_end` появляется только после `alembic upgrade head`. Перед прогоном спе — свежий стек / `docker compose up -d --force-recreate backend`.
+- **`yarn lint` сломан системно** — `tsutils.iterateComments is not a function` (пре-экзистинг, падает и на нетронутые `.ts`, дефект env eslint/typescript-estree).
+
## ✅ CODER SUCCESS REPORT — TASK-HIDE-TRACK-3-FIX
- `src/pages/workouts/components/WorkoutItem.vue`:
diff --git a/.roo/memory-bank/progress.md b/.roo/memory-bank/progress.md
index d3ab0eb..4001bb3 100644
--- a/.roo/memory-bank/progress.md
+++ b/.roo/memory-bank/progress.md
@@ -1,5 +1,23 @@
# Progress — Strava Frontend
+## 2026-09-25 — TASK-PROFILE-ULID-BUG: фикс PATCH /profiles 500 (смена имени не персистится)
+
+- `../backend/app/modules/users/domains/profiles.py:32` — `default=str(ULID())` → `default_factory=lambda: str(ULID())`.
+- `../backend/app/modules/users/domains/users.py:20` — та же замена.
+- `../backend/app/modules/users/repositories/profile.py` — `DBProfileRepository.update`: `new_dict.pop("id", None)` (PK вне UPDATE).
+- `../backend/tests/test_api_profile.py` — 2 теста (персистентность имени + повторные PATCH).
+- Verified: `pytest tests/test_api_profile.py -v` → 2 passed; `pytest tests/ -v` → 56 passed, 10 errors (все 10 пре-экзистинг: SQLite alembic ALTER-constraints в db-fixture setup, подтверждено git stash); `mypy app/modules/users/ --explicit-package-bases --no-error-summary` → 0; `ruff check app/modules/users/ tests/test_api_profile.py` → 0.
+- Фикс закрывает known issue из TASK-TEST-WORKOUT-UPLOAD (backend-баг имени).
+
+## 2026-09-25 — TASK-TEST-WORKOUT-UPLOAD: интеграционный тест загрузки тренировки (Playwright)
+
+- `../integration/tests/workout-upload-flow.spec.ts` — 5 последовательных тестов полного user-journey: (1) signup через UI + профиль (аватар-загрузка toast «Фото успешно загружено!», смена имени toast «Вы успешно изменили имя!»), (2) загрузка `.fit` на `/workouts/upload` + resolve id (redirect ИЛИ dedup-фолбэк через owner's `GET /api/v0/workouts`), (3) «Сделать публичной» → `GET /api/v0/public/workouts/{id}`=200, (4) «Скрыть начало и конец трека» → authed detail `workout.hide_start_end===true`, (5) «Добавить фото» → `` в `.workout-photos-grid`.
+- Ключевое: **стабильный owner**-юзер для Части 2 (глобальный content-hash dedup vs scoped `GET /workouts`); unique-юзер через UI для Части 1. Detail URL `/workouts/workouts/:id`, nav `domcontentloaded`.
+- `../integration/README.md` — секция про спек + notes (workout-dedup, backend-баг имени).
+- Cleanup: `../integration/tests/_diag.spec.ts` (диагностический) удалён.
+- Verified: `npx playwright test tests/workout-upload-flow.spec.ts` → **5 passed** (×3 dedup-путь + 1 свежая DB redirect-путь); `yarn build` exit 0; `npx playwright test --list` → 16 tests / 5 files (без `_diag`).
+- Known issues (не фикс, out-of-scope): backend `PATCH /profiles` (имя) → 500 из-за `Profile.id = field(default=str(ULID()))` (не `default_factory`) → имя не персистится, SPA toast ложный; `yarn lint` системно сломан (`tsutils.iterateComments`); перед прогоном спе нужен свежий стек / force-recreate backend (миграция `hide_start_end`).
+
## 2026-09-25 — TASK-HIDE-TRACK-3-FIX: восстановление чекбокса hide_start_end в WorkoutItem.vue
- `src/pages/workouts/components/WorkoutItem.vue` — восстановлены (потеряны в git stash при TASK-HIDE-TRACK-4):
@@ -248,4 +266,3 @@
- `../backend/tests/test_api_workout.py`: добавлен `test_workout_hide_start_end` (PATCH hide_start_end False→True→False + GET-верификация)
- Результат: `python -m pytest tests/test_api_workout.py -v` → **4 passed (5.13s)**
-
diff --git a/src/pages/workouts/components/WorkoutItem.vue b/src/pages/workouts/components/WorkoutItem.vue
index d70180f..3ed1a3c 100644
--- a/src/pages/workouts/components/WorkoutItem.vue
+++ b/src/pages/workouts/components/WorkoutItem.vue
@@ -177,7 +177,9 @@
/>