📱 Доска iOS

всеканоны и докиворкеры↗ WEB↗ Легаси
IOS-057 · Дефект · Переводчик · web

Удаление исходника уносит прогон перевода, глоссарий и сущности документа

Закрыт P0 · горит ведёт: translate эпик: IOS-006
Суть
ФОРМУЛИРОВКА OWNER-А (продуктовое требование, не техническая придирка): «люди будут выкладывать исходники на любом языке, переводить для себя, удалять мусорный непонятный исходник, который они не понимают — а окажется, что удалят и перевод, за который заплатили».

ПРОВЕРЕНО, что именно уносит каскад от Source:
· TranslationRun — КАСКАД (вместе с ним GlossaryEntry и TranslationChunk)
· DocumentEntity — КАСКАД (это то, на чём строятся граф знаний и тепловая карта)
· UserConfirmedLink — КАСКАД
· VerificationRun, VisualRun, VideoPipelineRun, EmbedConversationLogArchive — обнуляются, переживают

ХОРОШАЯ НОВОСТЬ: сам переведённый документ ВЫЖИВАЕТ — это самостоятельная запись Source без ссылки на оригинал. Деньги не сгорают, текст остаётся.
ПЛОХАЯ: пропадает глоссарий, который человек правил руками, история и стоимость прогона, и — как выяснилось сегодня — из-за исчезнувшего прогона визард ВРЁТ, что перевод не удался, хотя он выполнен.
ПОБОЧНЫЙ ЭФФЕКТ, о котором никто не предупреждает: чистка «мусорных» исходников тихо обедняет граф знаний, потому что уносит сущности документа.
Чем закрывается (приёмка)
1) Перевод и его глоссарий переживают удаление источника (мягкое удаление или отвязка вместо каскада). 2) Диалог удаления честно перечисляет, что именно уносится: N переводов, N глоссариев, сущности графа. 3) Проверка: удалить исходник с готовым переводом — перевод и глоссарий остаются доступны, граф не теряет узлы молча.
Лента
2026-08-02T16:43:10.622Z · eng-057
Корень: пользовательское удаление source идёт через POST /api/sync wipe-and-replace: tx.source.deleteMany({ notebookId }) каскадом удалял Source -> TranslationRun -> GlossaryEntry/TranslationChunk, а также Source -> DocumentEntity и UserConfirmedLink. Даже surviving source ids уже частично защищались только для DocumentEntity, но deleted source с переводом физически исчезал.

Решение: выбран soft-delete Source при наличии оплаченных/производных артефактов, потому что TranslationRun.sourceId в фактической Prisma-схеме обязательный и translation service читает run.source.content. Nullable FK потребовал бы более широкий rewrite чтения/повторного прогона. Добавлена additive-only миграция 20260802170000_source_soft_delete_artifacts: Source.sourceDeleted/sourceDeletedAt/sourceDeletedReason/sourceDeletedById + индексы, без drop/rename и без изменения FK. /api/sync перед deleteMany считает impact для tombstoned source ids; если есть TranslationRun/GlossaryEntry/DocumentEntity/UserConfirmedLink, source сохраняется как isActive=false, sourceDeleted=true, metadata.contentHash убирается, чтобы не блокировать повторный импорт, а артефакты и FK остаются. Реально hard-deleted ids возвращаются клиенту как hardDeletedSourceIds; только их клиент отправляет в /api/rag/purge. Добавлен /api/sources/delete-impact для UI confirmation; Sidebar предупреждает: «У документа есть оплаченный перевод — он сохранится отдельно», и показывает counts translation runs/glossary/entities/links.

Проверка: patch /Users/milamarty/Downloads/ENG-057.patch. Локально: node --test app/api/sync/__tests__/source-delete-preservation.contract.test.ts __tests__/migrations/20260802170000_source_soft_delete_artifacts.test.ts -> 6 pass. git apply --check /Users/milamarty/Downloads/ENG-057.patch на чистом canon OK. tsc/pnpm не запускались: в распакованном каноне отсутствует package.json и tsc не установлен. Миграцию не применял, deploy/push не делал.
2026-08-02T18:13:04.250Z · backup-opus
Посажено v4-4ef39b8 + миграция source_soft_delete_artifacts применена в серв-БД (ALTER TABLE + 2 индекса). Удаление источника теперь soft-delete c сохранением оплаченных артефактов (перевод/глоссарий/сущности), delete-impact API отвечает, UI предупреждает. Контракт-тесты в сборке зелёные. Owner: приглашение на пост-QA живьём — удали исходник с готовым переводом и убедись что перевод остался.
Воркер
не проверен translate M4 движение в панели: неизвестно
Подключиться и смотреть/перехватить руками. Колесо мыши листает; клавишами — Ctrl-b затем [, выход из прокрутки q. Отсоединиться — Ctrl-b затем d:
ssh -t poolpooly@192.168.1.74 "ssh -t milamarty@192.168.1.114 '/opt/homebrew/bin/tmux -L gui attach -t translate'"
Прочитать историю панели без подключения — листается и ищется (/ поиск, q выход), воркеру не помешает:
ssh poolpooly@192.168.1.74 "ssh milamarty@192.168.1.114 '/opt/homebrew/bin/tmux -L gui capture-pane -p -S -5000 -t translate'" | less -R
Если при входе tmux покажет «.config/tmux/tmux.conf: Permission denied» — нажми q, это косметика: каталог ~/.config на M4 принадлежит root, сессия работает. Лечится один раз с паролем на M4: sudo chown -R milamarty:staff /Users/milamarty/.config && chmod 755 /Users/milamarty/.config
Обновлён
2026-08-02T18:13:04.174Z