# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации
Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней)
переехало в [`bug_closed.md`](bug_closed.md): прежде чем заводить новый баг,
грепни там по симптому.
Приоритеты работ — в [`TASKS_OPEN.md`](TASKS_OPEN.md) (закрытые задачи с
протоколами — [`TASKS_CLOSED.md`](TASKS_CLOSED.md)), а не здесь. Правило
проекта: механику сверять с `../SDLPoP/src/` ДО кодинга.
**Ревизия списка: 2026-08-07 — прогон ВСЕХ комнат уровней 1 и 2**
(пользователь). Крупных багов нет. Поэтому в [`bug_closed.md`](bug_closed.md)
уехали разом: чек-листы ручной перепроверки фиксов (2026-08-03, обе волны),
таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений того же прогона —
все они закрыты этим проходом. С прогона пришли **три новые записи**, все по
уровню 2 (сырые формулировки — [`bugs_level2.md`](bugs_level2.md)).
| ID | что | тип | статус |
|----|-----|-----|--------|
| ~~BUG-CHEAT-FIGHT-1~~ | `+`/`−` в бою с вынутым мечом → Кид теряет управление | Major (чит) | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-cheat-fight-1) |
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводимый: на повторе не поднялся; на пререлиз |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
| ~~T-2~~ | Кид перерисовывается в покое | оптимизация | **ЗАКРЫТ 2026-08-08** — [DRAW-COST шаг 1](TASKS_OPEN.md#draw-cost) |
| ~~BUG-GATE-PASS-1~~ | проход сквозь закрытую решётку шва | Major | **ЗАКРЫТ 2026-08-09** — [bug_closed.md](bug_closed.md#bug-gate-pass-1), смоук уровня 1 пройден |
| ~~BUG-GUARD-IX-1~~ | «зависание» в бою со стражем: затёрт IX главного цикла | Blocker | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-guard-ix-1) |
| ~~BUG-GATE-SEAM-ROW1~~ | решётка в шве не анимируется, если ворота не в ряду 0 | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-gate-seam-row1) |
| ~~BUG-LOOSE-BUTTON-1~~ | упавшая плита не нажимает кнопку под собой | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-loose-button-1) |
| ~~BUG-GATE-FF-1~~ | ворота «открыты навсегда» непроходимы (0xFF перегружен) | Major | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-gate-ff-1) |
| ~~BUG-TORCH-CHOMP-1~~ | чомпер стирает пламя соседнего факела | Minor | **ЗАКРЫТ 2026-08-10** — [bug_closed.md](bug_closed.md#bug-torch-chomp-1) |
| [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт |
---
# Уровень 2 — баги с приёмки
Приёмка уровня 2 закрыта ([L2-PASS](TASKS_CLOSED.md#l2-pass)): smoke
2026-08-05 + обход всех комнат 2026-08-07. Карта содержимого уровня (что где
стоит по `res2002.bin`, какие кнопки какие ворота открывают) — там же, по ней
видно, «механика не сработала» это или «так и задумано».
**Закрыто с этой волны:** [BUG-LOOSE-3](bug_closed.md) — чёрный бар под
упавшей плитой-потолком; [BUG-GUARD-DEAF-1](bug_closed.md) — страж не
оборачивался на вернувшегося Кида;
[BUG-GUARD-SPLASH-1](bug_closed.md#bug-guard-splash-1) — «брызги» при
попадании по стражу; [BUG-SWORD-GHOST-1](bug_closed.md#bug-sword-ghost-1) —
меч, спрятанный посреди боя после перехода комнаты (корень — мнимая стена за
краем комнаты в `get_tile`; кэш соседей расширен до полных комнат);
[BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1) — цвет стража и его
полосы HP теперь берётся из `guards_color` уровня (2026-08-07).
Ниже — то, что осталось открытым после прогона 2026-08-07.
## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
> **Статус 2026-08-05, вечер.** Пользователь **повторить не смог**, а замер
> (ниже) показал, что чистый пробег по убранным пикам убивает штатно. То
> есть в смертельности бага, похоже, нет вовсе: наблюдался частный случай —
> пики, УЖЕ выдвинутые полностью (h = 1), для бегущего безвредны и в
> оригинале. Запись оставлена открытой ровно из-за визуального расхождения
> со скриншотом SDLPoP (у нас острия торчат, у него убраны) — см. конец.
>
> **Как получить состояние нарочно:** подойти к пикам вплотную (выдвинутся),
> отступить на полшага, НЕ выходя из зоны срабатывания, и пробежать по ним.
> Пользователь пробовал и это, и попиксельную подгонку X читом `[`/`]` —
> не поднялось. Вывод для будущего разбора: состояние **не чисто
> позиционное**, одной шириной габарита его не объяснить; следующий
> подозреваемый — момент, в который `process_trobs` застаёт модификатор
> относительно кадра Кида.
**Наблюдение (пользователь, 2026-08-05).** Уровень 2, комната 6, пики (1,3):
| действие | что происходит |
|----------|----------------|
| длинный прыжок с ряда 0 на пики | **смерть — правильно** (путь `fell_on_spikes`) |
| пробег по ряду 1 прямо по пикам | **урона нет** |
| после уборки пик | **на экране остаются белые остатки остриёв** (в оригинале чисто) |
| прыжок на месте, стоя на пиках | урона нет |
| просто стоять на выдвинутых пиках | можно сколько угодно |
**Замер (MAME, чтение `room_modif` комнаты 6).** Пока Кид стоит на тайле,
модификатор пики (индекс 13) = **0x8E**, то есть «пики ПОЛНОСТЬЮ вышли и
идёт обратный отсчёт». Дальше вся арифметика сходится с оригиналом:
```
is_spike_harmful (seg007:1178): 0/-1 → 0; <0 → 1; 1..4 → 2; >=5 → 0
check_spiked (seg006:0658): убивает при h>=2 на кадрах бега 7..14
и при h!=0 на кадрах приземления 43/26
```
То есть **при h = 1 (пики уже вышли) бегущий не гибнет и в оригинале** —
смертельно только окно ВЫДВИЖЕНИЯ (модификатор 1..4, h = 2). Наши
`animate_spike`, `start_anim_spike`, `is_spike_harmful`, `check_spiked`
сверены с seg006/seg007 построчно и совпадают дословно.
**ВТОРОЕ НАБЛЮДЕНИЕ (то же место, сравнение с оригиналом).** Кид уронил
плиту-потолок и спрыгнул вниз; пики выдвинулись и «спрятались не все — часть
артефактов осталась». Скриншоты рядом:
[наш](bugscreens/l2-r6-spikes-ours.png) и
[SDLPoP](bugscreens/l2-r6-spikes-sdlpop.png) в той же позе. У нас из-под
щебня торчат белые острия, у оригинала пик не видно ВООБЩЕ.
**ГИПОТЕЗА «ТРИГГЕР СРАБАТЫВАЕТ РАНО» ПРОВЕРЕНА И ОПРОВЕРГНУТА (замер
2026-08-05, MAME, watchpoint на `room_modif[13]` комнаты 6).** Чистый
пробег по УБРАННЫМ пикам убивает штатно:
```
запись modif=1 : кадр 11 (беговой), x=112, curr_col=2 ← пики пошли вверх
запись modif=2 : кадр 12 (беговой), x=117, curr_col=3 ← Кид уже НА тайле, h=2
запись modif=3 : кадр 177 (frame_177_spiked) ← напоролся
```
То есть `check_spike_below`, `check_spiked`, `is_spike_harmful` и тайминг
выдвижения работают правильно, и «раннего» триггера нет.
**Настоящий корень — пики ЗАЛИПАЮТ выдвинутыми.** Пока габарит Кида
накрывает колонку пики, `check_spike_below` каждый кадр зовёт
`start_anim_spike`, а тот при отрицательном модификаторе переставляет его
обратно в 0x8F — отсчёт до уборки не доходит. А выдвинутые пики (h = 1)
для бегущего БЕЗВРЕДНЫ по правилам оригинала. Отсюда обе жалобы: пробег
по уже вышедшим пикам не убивает, и они же остаются торчать на экране.
**Что осталось выяснить (и это единственный открытый вопрос).** Код
`start_anim_spike` у нас с оригиналом совпадает дословно, значит оригинал
тоже удерживал бы пики, стой Кид там же. На скриншоте SDLPoP в похожей
позе пики УБРАНЫ — то есть его Кид стоит чуть левее и его габарит колонку
пики уже не задевает. Разница в 2–3 пикселя посадки, а у нас такие
расхождения по X уже ловились (см. заметку в BUG-GRAB-1: после касания
площадки SDLPoP уводит Кида на 134, мы — на 141).
**Как закрывать:** инструментировать SDLPoP (печать `char_x_left/right`,
`left/right_checked_col` и модификатора пики каждый кадр), проиграть ту же
сцену — падение плиты-потолка в комнате 6 и остановку на щебне — и сверить
с нашей трассой ПОЗИЦИЮ КИДА после приземления. Если позиции совпадут, а
диапазоны колонок разойдутся — виноват габарит (`kid_fp` против
`set_char_collision` текущего кадра); если разойдутся позиции — это отдельный
баг посадки, а пики — его следствие.
---
# Уровень 3 — не баги, а неначатые задачи
Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал четыре наблюдения. Два
были настоящими багами и **закрыты в тот же день** (разбор — в
[`bug_closed.md`](bug_closed.md): **BUG-JUMPWALL-1** и **BUG-SEAM-WEDGE-1**).
Оставшиеся два — не баги, а неначатые задачи:
| наблюдение | что это на самом деле |
|------------|------------------------|
| к.22: чомпер (2,6) не анимируется и **вообще не рисуется** | [L3-CHOMP](TASKS_OPEN.md#l3-chomp) — механики чомперов НЕТ. В таблице тайлов `pop_bg.c:211` строка `12 chomper` рисует только основание, правую грань и низ; самих челюстей (спрайт из `chtab`, `draw_tile_anim` seg008) нет вовсе. Так и должно выглядеть до порта |
| к.10: скелет не оживает | [L3-SKEL](TASKS_OPEN.md#l3-skel) — `check_skel` (seg002:0E1F) не портирован. **И комната другая:** тайл `skeleton(21)`, который оживает, лежит в **к.1 (1,5)** — это `skeleton_room=1, skeleton_column=5, skeleton_row=1`. Ещё два скелета уровня (к.17 (2,7), к.19 (2,2)) — просто декорация, они не оживают никогда. Плюс условие: скелет встаёт, только когда **дверь уровня уже открыта** и Кид стоит в колонке 2 или 3 |
Приёмки уровня 3 (полного обхода комнат) ещё не было — она осмысленна только
после L3-CHOMP/L3-SKEL.
---
# Оптимизация отрисовки (записано 2026-07-29)
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к
перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый
кадр), а у нас каждая такая пометка превращается в реальный heal (копию из
ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем
безусловно» корректен, но дорог.
## T-1. Пики: перерисовывать по причине, а не безусловно
**Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для
каждой живой пики в комнате (порт `redraw_21h`, который `animate_spike`
вызывает вне всяких `if`). Это корректно, но лишнее для пик, до которых
Киду дела нет.
**Надо:** перерисовывать тайл пики, только если
1. **сменился её видимый кадр** (шаг выдвижения/уборки), ЛИБО
2. **её кто-то стёр** — а стереть у нас может только heal, то есть тайл
попал в прямоугольник `kid_heal` этого кадра.
Это и есть модель оригинала, просто выраженная флагами: `redraw_at_char`
(seg003:0576) каждый кадр помечает `set_redraw_fore` тайлы персонажа, причём
**объединение текущего и предыдущего** прямоугольника
(`MIN(char_top_row, prev_char_top_row)` и т.д.), а `animate_spike` помечает
свой тайл. Итог = {тайл сменил кадр} ∪ {тайлы Кида}.
**Как:** слой Кида и так считает `cL..cR`/`rT..rB` в `pop_fore_over_kid` —
пусть публикует их (плюс предыдущие, как в оригинале), а цикл trob'ов
сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений
прямоугольников (см. память `manual_hints_over_auto_detect`).
**Приоритет:** отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается.
## T-2. Idle-skip — **ЗАКРЫТ 2026-08-08**
Сделан как шаг 1 задачи [DRAW-COST](TASKS_OPEN.md#draw-cost) (коммит
`a25ce58`), и шире, чем формулировался здесь: пропускается не только Кид, а
ЛЮБОЙ персонаж, у которого с прошлой отрисовки этой страницы дабл-буфера не
изменился ни один вход отрисовки, — включая труп стража и ждущего стража.
Условие «обе страницы уже получили это состояние», которого требовала эта
запись, выполнено само собой: снимок входов хранится ПО СТРАНИЦАМ.
Замер: комната 1.3 с трупом стража, Кид стоит — 210 % -> 116 % кадрового
периода, ноль вызовов `pop_heal_fast` за кадр. Контракт — шапка
`pop_cdraw.h`, разбор и что делать дальше — `TASKS_OPEN.md#draw-cost`.
Связь с T-1 сработала как и предсказано: пока Кида не перерисовываем, heal'а
нет, стирать пики нечем. Но T-1 остаётся открытым — трогать пики
безусловно мы всё ещё продолжаем, когда Кид рядом ДВИЖЕТСЯ.
---
## BUG-CHOMP-JUMP-1. Прыжок с места вплотную к чомперу: кадр с отступом назад — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
> **Статус 2026-08-08.** Наблюдение пользователя на приёмке L3-CHOMP: Кид
> стоит вплотную СЛЕВА от чомпера лицом вправо, прыжок с места — в анимации
> проскакивает кадр, где он «чуть отступил назад», и только потом идёт
> прыжок. В оригинале прыжок идёт прямо с места. **На повторе в тот же
> заход не воспроизвёлся** — отложено до пререлиза.
**Что уже установлено (чтобы не разбирать заново).**
1. **Это НЕ анимация.** `seq_3_standing_jump` (SDLPoP `seqtbl.c:382`)
состоит только из положительных смещений:
`act(run_jump), f16, f17, dx(2) f18…f22, dx(7) f23, dx(9) f24, dx(5) dy(-6) f25`.
Ни одного отрицательного `dx` — отката в последовательности нет вовсе.
Значит отступ даёт `bumped()`, то есть физика посчитала въезд в
препятствие и выровняла `Char.x` назад.
2. **`is_obstacle` для чомпера у нас совпадает с оригиналом** (seg004:037E):
препятствие только при `modif == 2`, причём именно `== 2`, БЕЗ маски
`0x7F` — то есть окровавленный чомпер (`0x82`) в ванили не бампит вовсе.
Проверено, расхождения нет.
3. **Прямая трасса прыжка отката НЕ показала.** Ввод «UP и RIGHT
одновременно» (mame bridge, `key UP 24` + `key RIGHT 24`) даёт чистое
движение вперёд: `x` 148 -> 155, кадр становится 178 (перемололо). Ни
одного кадра с уменьшением `x`.
**Главная гипотеза — ПОРЯДОК НАЖАТИЙ.** Если ↑ приходит на кадр раньше →,
то `control_standing` уходит не в `standing_jump()`, а в `up_pressed()` —
вертикальный прыжок с зацепом, а он **выравнивает `Char.x`**. Отсюда и
«отступил, потом прыгнул». Проверять надо `check_jump_up` /
`jump_up_or_grab` / `grab_up_no_floor_behind` (seg005:0836 и далее), а не
прыжок с места. Внимание на `can_climb_up` (seg005:0828): там у чомпера
ЕСТЬ спецкейс (`seq_73_climb_up_to_closed_gate` при взгляде ВПРАВО) — он у
нас портирован (`pop_map.c`, ветка `TILE_MIRROR || TILE_CHOMPER`), но именно
вокруг него и стоит копать.
**Как ловить.** Брейкпоинт на резидентном `_kid_tick` с
`{printf "f=%d x=%d act=%d col=%d",b@Kid+0,b@Kid+1,b@Kid+6,b@Kid+4; g}` —
одна строка на кадр, адрес `_Kid` из `.sprinter-cc-roomtest/roomtest.map`
(после КАЖДОЙ пересборки другой). Плюс стоп-кадр `1` в момент отступа и
чтение `Kid` из памяти. Метод — memory `z80_profiling_method`.
---
## Заметки (отладка)
- Тестовые клавиши осторожного шага: **J** = шаг влево, **L** = шаг вправо
(эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в
сторону = разворот (как в оригинале safe_step), движение со второго.
- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle),
**S** — выдать меч, **Shift+L** — следующий уровень, **`[`/`]`** — сдвиг
Кида на пиксель по X.
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит
Kid в стартовую позицию УРОВНЯ (`pop_start_level`, порт do_startpos).
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](bug_closed.md#bug-cheat-fight-1).
- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство
данных уровня (разбор — «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md));
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.
---
## DIED-ON-BUTTON. `died_on_button` (seg007:776) не портирован
Обнаружено при разборе [BUG-LOOSE-BUTTON-1](bug_closed.md#bug-loose-button-1).
`check_press` (seg006:1707) разбирает ЛЮБОГО мёртвого `Char`, не только Кида:
```c
if (curr_tile2 == tiles_15_opener || curr_tile2 == tiles_6_closer) {
if (Char.alive < 0) trigger_button(1, 0, -1); /* жив */
else died_on_button(); /* мёртв */
}
```
`died_on_button` на **opener** делает тайл обычным полом и форсирует
`button_type = tiles_14_debris` — ворота открываются насовсем; на любой другой
кнопке ставит `tiles_5_stuck` (заклинена, `link_timer == 0x1F`, связь мертва).
У Кида эффект живёт до перезапуска уровня (смерть → `is_restart_level` →
`play_level` заново зовёт `load_level()`, тайлы перечитываются), а вот когда на
кнопке умирает СТРАЖ — перезапуска нет, и кнопка заклинена до конца уровня.
Что нужно: сам порт `died_on_button`, константа `TILE_STUCK = 5` и тайл
заклиненной кнопки в атласе фона (сейчас его там нет).
---
## TORCH-ANIM-RIGHT. Под пламенем могут застыть не только челюсти чомпера
Открыто 2026-08-10 при закрытии
[BUG-TORCH-CHOMP-2](bug_closed.md#bug-torch-chomp-2).
Пламя факела запекается в фон и рисуется в ячейке ПРАВОГО СОСЕДА. Оригинал
после каждого кадра факела метит этого соседа (`set_redraw_anim_right`,
seg007:0101) и перерисовывает весь его слой `anim` поверх огня; мы метим
только когда сосед — чомпер. Слой `draw_tile_anim` (seg008:0644) рисует
также **пики, зелье и меч** — если такой тайл окажется справа от факела и
будет в статике, пламя накроет и его.
На уровнях 1–4 такого соседства не встретилось, поэтому расширять пометку
(она стоит перерисовки тайла каждый кадр на каждый факел) заранее не стали.
**Как проверять:** поставить факел слева от пики/меча/зелья в тестовой
комнате и дождаться статики; либо пройти уровни 5+ и смотреть на клетку
справа от каждого факела.
**Как чинить, если встретится:** там же, в ветке факела `pop_process_trobs`,
добавить в `trob_rcode`-проверку нужные коды и соответствующий вид пометки
(`POP_RD_SPIKE` / `POP_RD_FLOOR`), а зелье — переставить в порядке обхода
так, чтобы оно рисовалось ПОСЛЕ факела.
---
## FORE-DUP. Передний слой тайла рисуется ДВАЖДЫ при перекрытии объектов
Найдено пользователем 2026-08-10 (вопросом, а не по симптому — картинка
верная, страдают только такты).
**У нас.** Fore-проход зовёт КАЖДЫЙ рисующий по своему прямоугольнику:
`pop_char_fore` для слотов Кида и соперника, `pop_mirror_draw` для отражения.
Окно клипа (`pop_fore_set_clip`) у каждого своё. Если футпринты двух
объектов накрывают один тайл, его передний кусок рисуется два раза.
Когда случается:
- **Кид и отражение** — ВСЕГДА (стоят на одном тайле зеркала); этот случай
создан фиксом fore-прохода над отражением 2026-08-10;
- **Кид и соперник в одном тайле** — то есть весь ближний бой;
- Кид и падающий кусок плиты.
**Картинку не портит:** куски переднего слоя идут прозрачным блитом-копией,
операция идемпотентная. Портило бы при XOR/mono с накоплением — таких в
fore-слое нет.
**Такты тратит,** и в самом дорогом месте: fore-проход исторически самая
тяжёлая часть кадра (memory `pop_fore_layer_cost` — было 78 % кадра, окно
клипа и кэш кладки дали 3.2×).
**Как устроено в оригинале — дубль НЕВОЗМОЖЕН по построению.**
`redraw_at_char` (seg003:0427) ничего не рисует, а только помечает тайлы
своего прямоугольника:
```c
for (tile_row = x_top_row; tile_row <= char_bottom_row; ++tile_row)
for (tile_col = x_col_left; tile_col <= x_col_right; ++tile_col)
set_redraw_fore(get_tilepos(tile_col, tile_row), 1);
```
а `set_redraw_fore` (seg007:0550) — это `redraw_frames_fore[tilepos] = frames`,
ПРИСВАИВАНИЕ. Два персонажа на одном тайле оставят там ту же единицу, и
единственный обход тайлов нарисует передний кусок один раз.
**Что делать.** Механизм пометок у нас уже есть и прямо назван портом этой
архитектуры — `pop_redraw.h`. Fore-слой остался единственным местом на
прямом вызове. Приведение к оригиналу: `pop_char_fore`/`pop_mirror_draw`
вместо прохода ставят пометки, а один проход в конце кадра их разбирает.
**СНАЧАЛА ЗАМЕРИТЬ, потом чинить.** Это самый горячий путь, и окно клипа
(вместо перебора тайлов) в своё время дало 3.2× — переход на пометки может
часть этого вернуть назад. Замер: брейкпоинт на листьях fore-слоя со
счётчиком, сцена «бой в комнате 3 уровня 1» и «Кид на тайле зеркала,
ур. 4»; сравнить число нарисованных кусков с числом уникальных тайлов.
Если дубль мал — оставить как есть и закрыть запись.