40 KiB
Issue #244 — восстановление маркеров с мёртвой ссылкой на пространство
- Дата: 2026-08-22
- Тип: bug · приоритет P2 · ценность 8/10 · сложность/риск 5/10 и 5/10
- Issue: #244
- Ветка:
issue/244-orphan-space-references - Статус ТЗ: утверждено (SPEC-REVIEW-244-r2 green)
Канонические документы: docs/SCOPE.md, docs/TOUCH-SUPPORT.md,
docs/USER-GUIDE.md, docs/USER-GUIDE.ru.md, docs/CONFIG-COMPATIBILITY.md,
docs/TESTING.md, docs/specs/050-config-export-import.md,
docs/specs/199-optimize-geometry-preflight.md.
1. Сценарий и персона
Администратор дома годами поддерживает несколько пространств, импортирует
отдельные этажи и иногда удаляет старые. В хранилище остаётся настроенный
маркер реального или виртуального устройства, но его space ссылается на id,
которого уже нет. Если рабочая HA Area не связывает устройство с комнатой
другого пространства, маркер исчезает со всех планов. Администратор видит
настройку в diagnostics либо backup, но не может найти устройство в редакторе,
перенести его или понять причину исчезновения.
Второй вход в сценарий — карточка Lovelace с default_floor, который больше не
существует. План безопасно открывает первое пространство, но никак не сообщает,
что стартовая настройка проигнорирована.
2. Что человек увидит до и после
До: устройство настроено и хранится, но не показывается ни на одном плане;
Optimize сообщает только о геометрии. Удаление пространства может создать ещё
такие записи. Невалидный default_floor молча открывает другой этаж.
После: Optimize однозначные ссылки переносит на восстановленное
пространство, а без надёжного назначения снимает только мёртвую привязку и
возвращает сам маркер в доступный план. Preview сообщает, сколько устройств
восстановлено и какие ссылки требуют внимания. Удалить занятое пространство
нельзя до переноса его маркеров, пока существует другое пространство. Последнее
пространство можно удалить после подтверждения: маркеры сохраняют настройки и
теряют только размещение. Редактор карточки постоянно показывает, какой
default_floor отсутствует.
3. Подтверждённый диагноз
resolveExplicitMarkerPlacement()для device/entity использует(Area -> space) || marker.space || firstSpaceId. Непустой, но мёртвыйmarker.spaceблокирует fallback. Для virtual-маркераmarker.spaceимеет ещё более высокий приоритет.- Full и Static отрисовывают только устройства, у которых
d.spaceсовпадает с id существующего пространства. Поэтому значение вродеf1не рисуется вspace_f1_fc3db9d6и не попадает на первый этаж. _deleteSpace()удаляет только элементconfig.spaces. Маркеры и layout не меняются, а backend не проверяет их ссылочную целостность.optimizePlans()обходит маркеры ради прежних миграций, но не проверяетmarker.space,marker.room_id,vacuum.segment_mapиlayout[*].s.- Space import создаёт независимую копию через
space_<old-id>_<8 hex>и знает полныйid_mapимпортируемого пространства, но переписывает только приехавшие объекты. Он не удаляет корректное старое пространство и сам по себе не доказан как генератор дефекта; это возможность безопасно вылечить уже повреждённый target. resolveInitialSpace()валидируетdefault_floorпо живым space id и переходит к первому, но редактор карточки не показывает причину fallback.
4. Зафиксированные решения владельца
- Однозначная import-подпись и рабочая HA Area используются для автоматического восстановления.
- Если ни одна из них не даёт назначения, удаляется только мёртвая привязка размещения и недействительная позиция. Marker record, binding, иконка, действия, metadata, PDF и прочие настройки сохраняются. Реальное или virtual-устройство после этого использует рабочую Area либо первое пространство и доступно для ручного переноса.
- Весь marker record не удаляется: это потеряло бы пользовательские настройки, уничтожило virtual-маркер и не гарантировало бы автопоявление реального устройства без HA Area.
- Если пространств два или больше, удаление пространства блокируется, пока на
нём остаются активные маркеры; UI называет количество и просит сначала
перенести или удалить их. Исключение: последнее пространство можно удалить
после подтверждения. У всех затронутых активных и removed marker records
снимаются только
spaceиroom_id; binding, иконка, действия и остальные настройки сохраняются. Это сохраняет empty-state контракт #113. - Невалидный
default_floorполучает постоянную inline-ошибку в редакторе карточки. Дополнительный toast не показывается; безопасный fallback на первое пространство сохраняется.
5. Цели
- после Optimize ни один активный маркер не остаётся невидимым только из-за
мёртвого
marker.space, если существует хотя бы одно пространство; - не переносить устройство между этажами по догадке и не терять его настройки;
- использовать известный import
id_mapцеликом, включая ссылки на комнаты; - перестать создавать новые ссылки-сироты через штатное удаление пространства;
- сделать результат и остаточный долг видимыми до Apply;
- сохранить точный preview, атомарную запись, Undo и идемпотентность Optimize.
6. Scope
Входит
- новый reference-repair pass в явном
optimizePlans(); - exact signature-remap для пространств и комнат;
- runtime Area-remap для активных device/entity-маркеров;
- безопасное снятие нерешённой привязки активного маркера;
- согласованная обработка
marker.space,marker.room_id, layout ownership, room-label keys иvacuum.segment_map; - отдельные counters/diagnostics в Optimize preview и итоговом toast;
- восстановление target-ссылок при импорте одного пространства;
- preflight и авторитетная защита удаления пространства;
- очистка layout, принадлежащего успешно удаляемому пространству;
- inline-валидация
default_floorв редакторе карточки; - RU/EN i18n, unit/backend/browser tests, mutation и reviewed golden evidence.
Не входит
- автоматический Optimize при чтении, обычном Save или старте карточки;
- удаление marker record, binding, файлов, PDF либо metadata по косвенной связи;
- управление HA Areas или автоматическое создание комнат;
- переписывание внешнего YAML Lovelace либо автоматическое исправление
default_floor; - изменение контракта фиксированного
floorиз #210; - лечение произвольных broken marker controls, entity bindings и HA registry;
- перенос координат между несвязанными планами;
- изменение full-restore: он по-прежнему заменяет config и layout целиком;
- фоновый сбор мусора для неизвестных layout entries.
7. Термины и инварианты
existingSpaceIds — непустые id текущих config.spaces.
deadSpaceId — непустая строка в marker.space либо layout[*].s, которой нет
в existingSpaceIds. Отсутствующее поле не является мёртвой ссылкой.
active marker — marker record с removed !== true; hidden: true остаётся
активным, потому что это пользовательская видимость, а не tombstone.
effective Area device/entity-маркера разрешается тем же production roster,
что и View: явная marker.area, registry Area device/entity и специальное
правило manual room without Area должны совпадать с
resolveExplicitMarkerPlacement(). Новый pass не вводит второй расходящийся
resolver.
После любого Apply должны выполняться инварианты:
- у активного маркера нет непустого
space, которого нет вconfig.spaces; - сохранённая позиция маркера применяется только в том пространстве, которое разрешено для маркера;
- неизвестная позиция без активного владельца не удаляется по одной догадке;
- повторный pass над собственным candidate не меняет JSON и counters;
- входные
configInиlayoutInне мутируются.
8. Контракт Optimize
8.1 Поиск signature-кандидата
Для мёртвого id X signature-кандидат — существующий id строго вида
space_X_<8 lowercase hex>. Поиск разрешён только если:
Xсам является валидным space id;- длина
X <= 35, то есть_fresh()не обрезал stem и соответствие остаётся обратимым; - исходного
Xнет среди пространств; - найден ровно один кандидат.
Ноль, несколько кандидатов либо обрезанный stem не считаются доказательством.
Пример защиты: при существующем f1 маркер space=f1 не меняется; при двух
space_f1_<hex> signature-rule не выбирает ни один.
Для room_id=R внутри выбранного пространства применяется аналогичное правило
room_R_<8 lowercase hex> только при необрезанном stem и ровно одном
кандидате. Already-valid room id сохраняется.
8.2 Приоритет правил для активного маркера
Для активного marker с мёртвым marker.space правила применяются по порядку:
- Signature: записать найденный imported space id.
- Effective Area: если signature нет, но production resolver связывает маркер ровно с одной комнатой существующего пространства, записать это пространство.
- Detach: иначе удалить только поле
marker.space.
Virtual-маркер не имеет registry Area и проходит signature либо detach. Removed marker получает только доказуемый signature-remap; без него tombstone сохраняется и не входит в пользовательский счётчик возвращённых устройств.
Если пространств нет вообще, detach всё равно очищает ложную ссылку, но отчёт честно говорит, что маркер станет видимым только после создания пространства.
8.3 Позиции и вложенные ссылки
- При signature-remap и при space import новая сущность — копия той же
геометрии.
layout[marker.id].sс тем же dead id переписывается, аx/y/kсохраняются. - При Area-remap или detach старые координаты относятся к неизвестному плану. Такая marker-position удаляется целиком, чтобы production default grid либо центр комнаты дал честную позицию. Координаты между планами не трансплантируются.
- Already-valid layout entry не меняется только потому, что рядом найден другой dead id.
marker.room_idобновляется по точному room signature либо по единственной комнате effective Area. Невалидный room id без доказуемой замены удаляется; валидный id не трогается.- Значения
vacuum.segment_mapпереписываются только по exact room map. Нерешённые значения сохраняются и считаются вnestedRefsUnresolved, потому что удаление калибровки было бы потерей пользовательских данных. - Layout-only entry с dead
s, у которого нет активного marker-владельца, signature-remap получает автоматически; без signature сохраняется и учитывается вpositionsUnresolved. rl_<old-room-id>переименовывается только по exact room map. Если валидный destination key уже существует, он выигрывает, stale source удаляется и считается обслуженным, но не перезаписывает действующую позицию.
8.4 Отчёт, preview и Undo
OptimizeReport получает отдельный блок либо эквивалентные typed fields:
spaceRefsRemapped;roomRefsRemapped;positionsRemapped;markersDetached;positionsUnresolved;nestedRefsUnresolved;- отсортированный уникальный
deadSpaceIdsпосле candidate.
Эти значения не смешиваются с migrated, canonicalized или геометрическими
counters. Preview отдельной строкой сообщает однозначно восстановленные и
detached-маркеры; warning называет первые десять оставшихся dead id и число
остальных. Полный массив остаётся в report для тестов/диагностики.
Если остались только нерешённые позиции/вложенные ссылки и candidate не изменился, диалог показывает warning вместе с «изменений нет», но Apply недоступен. При положительном repair Apply сохраняет exact preview candidate через существующую plan/optimize transaction. Cancel ничего не пишет; Undo возвращает исходные dead references и позиции. Итоговый toast включает число reference-repair действий.
PLAN_MODEL_VERSION повышается на один. Как и сейчас, одна версия без
содержательного изменения не создаёт запись; repair выполняется по данным, а
не пропускается из-за уже высокого model version.
9. Контракт импорта одного пространства
build_space_merge() уже знает old_space_id, new_space_id и полный
id_map. Если old_space_id отсутствует в current target, перед объединением
он применяет этот map к оставшимся target-объектам:
marker.space == old_space_id->new_space_id;- matching
marker.room_idиvacuum.segment_map-> новые room id; layout[*].s == old_space_id->new_space_id;rl_<old-room-id>->rl_<new-room-id>.
Здесь не нужен эвристический signature search: соответствие известно из
конкретного import document. Если current target всё ещё содержит пространство
old_space_id, его ссылки не меняются. Full restore не меняется.
Import preview и apply result получают repaired_target_refs; при значении
больше нуля существующая RU/EN preview-поверхность сообщает число. Imported
room-label position имеет приоритет над stale target label при коллизии, потому
что именно imported document определяет новую независимую копию. Маркерные id
импортируемой копии остаются fresh и не перезаписывают current markers.
10. Безопасное удаление пространства
Перед delete frontend считает активные marker dependencies. Если пространств два или больше, маркер блокирует удаление, если выполняется хотя бы одно:
marker.spaceравен удаляемому id;- его
room_idпринадлежит комнате удаляемого пространства; - его marker-layout position имеет
s, равный удаляемому id.
Один маркер считается один раз. Room-label positions и layout-only entries, принадлежащие самому удаляемому пространству, не являются отдельными пользовательскими устройствами: после снятия marker blockers они удаляются вместе с пространством.
При blockers и наличии другого пространства редактор пространства сохраняет
диалог открытым, показывает
inline role=alert с количеством и инструкцией перенести/удалить устройства,
не показывает native confirm и не пишет config/layout/revisions. Delete control
остаётся доступным для активации, чтобы причина была объяснена, а не выглядит
как необъяснимо disabled.
После устранения blockers подтверждённый delete удаляет space и все layout
entries с s == spaceId; у removed tombstones снимаются только placement-поля,
которые ссылались на удалённое пространство. Marker metadata и файлы не
удаляются по inference.
Если удаляется единственное оставшееся пространство, dependencies не
блокируют подтверждение. Та же авторитетная транзакция удаляет space и весь его
layout, а у каждого затронутого активного или removed marker record снимает
ровно space и room_id. Затронутым считается маркер, чьё прямое space,
комната либо marker-layout position принадлежит удаляемому пространству.
Binding, иконка, действия и любые остальные настройки сохраняются; незатронутые
legacy-ссылки не переписываются.
Авторитетная backend-операция повторно проверяет dependencies под write lock,
сверяет обе expected revisions и сохраняет config/layout без наблюдаемого
полусостояния. Гонка возвращает стабильный conflict либо space_in_use с
counts; frontend обновляет данные и показывает локализованную причину. Удаление
последнего пустого или занятого пространства сохраняет действующий empty-state
контракт #113.
11. default_floor в редакторе карточки
После авторитетной загрузки списка пространств непустой
config.default_floor, которого нет в списке, отображается как сохранённое
невалидное значение и получает постоянную inline-ошибку рядом с полем:
- RU: «Стартовое пространство “{id}” больше не существует. Выберите другое»;
- EN:
Initial space “{id}” no longer exists. Choose another space.
Предупреждение отсутствует до завершения загрузки, при отсутствующем/пустом
значении и после выбора существующего пространства. Несвязанное редактирование
другого поля не должно молча стереть raw id. Toast, блокировка сохранения и
автоматическая запись первого пространства не добавляются. Runtime продолжает
безопасный fallback hash -> saved -> valid default -> first.
12. Данные, compatibility и миграция
Persisted schemas CONFIG_SCHEMA и LAYOUT_SCHEMA не меняются: поля остаются
optional strings/records, чтобы старые повреждённые установки продолжали
читаться и могли быть вылечены. Никакая фоновая миграция не запускается.
Старые клиенты читают repaired candidate как обычный config/layout. Новая backend-защита удаления grandfather'ит уже существующие unrelated dead refs и не блокирует обычный Save: она применяется к точной delete-команде. Space import остаётся preview-first и сохраняет текущую optimistic locking модель.
Детерминированные remap/detach — пользовательское явное обслуживание и входят в существующий Optimize Undo. Неизвестные поля, content URLs, файлы, marker controls и unattached unresolved layout сохраняются. Числовая геометрия и координатная система не меняются.
13. i18n, accessibility и безопасность текста
Все новые строки существуют в src/i18n/en.json и ru.json: reference repair,
остаточный warning, import repair, delete blocker и invalid default_floor.
Counters используют явные существительные, а не цвет как единственный сигнал.
Delete blocker и editor warning доступны текстом и role=alert; focus не
перехватывается. Длинные ids переносятся внутри диалога, выводятся как text
binding Lit и не интерпретируются как HTML. UI ограничивает summary первыми
десятью id и сообщает остаток, поэтому forged legacy data не раздувает диалог.
Touch/kiosk наследуют действующий контракт docs/TOUCH-SUPPORT.md без нового
жеста или интерактивного пути. Восстановленный маркер использует тот же
поддерживаемый View/Static render и тот же tap-контракт на desktop, телефоне и
планшете-киоске; его доступность не зависит от возможности открыть редактор.
Optimize, редактор пространства и редактор карточки остаются desktop-first
best effort на touch, поэтому новые editor controls не расширяют touch-гарантию.
14. Acceptance criteria
| AC | Критерий | Доказательство |
|---|---|---|
| AC1 | Fixture с активным marker space=f1, без рабочей Area и без пространства f1 не рисуется на исходном dev; после Optimize маркер сохраняет binding/settings, теряет только dead placement и появляется в первом пространстве одинаково в desktop View, touch/kiosk View и Static. |
buildDevices + plan-optimizer regression unit и production-bundle smoke с desktop/touch/static viewport. |
| AC2 | При единственном space_f1_<8hex> marker, его matching layout, room id, room label и vacuum segment map переводятся по exact signature без изменения координат; второй прогон changed=false и все reference counters равны нулю. |
Table-driven optimizer unit + idempotence unit. |
| AC3 | Существующий f1, два signature-кандидата, id длиннее 35 и неверный suffix не запускают signature-remap. Активный marker проходит Area либо detach, removed tombstone не переносится по догадке. |
Negative/boundary unit + signature mutation. |
| AC4 | Device/entity с effective registry Area записывает существующее пространство/комнату; manual-room-without-Area не возвращается в registry Area. Virtual marker проходит signature либо detach. | Production placement resolver unit matrix. |
| AC5 | Area-remap/detach удаляет marker-layout старого плана вместо переноса x/y; signature-remap сохраняет x/y/k. Layout-only unresolved entry сохраняется и попадает в warning. |
Optimizer unit с тремя layout-классами. |
| AC6 | Нерешённый vacuum.segment_map не удаляется, но считается в nestedRefsUnresolved; exact room map переписывает его полностью. |
Vacuum reference unit. |
| AC7 | Optimize preview и toast отдельно показывают remapped/detached counters; warning виден и при changed=false, Apply тогда отсутствует; Cancel не пишет, Apply сохраняет exact candidate, Undo возвращает исходные ссылки. |
UI unit + targeted browser/backend smoke. |
| AC8 | Space import при отсутствующем source id переписывает target marker/layout/room/vacuum refs известным id_map, отражает count в preview и apply; при существующем source id target не меняется. Full restore остаётся прежним. |
Backend import/export tests preview/revalidate/apply. |
| AC9 | При двух или более пространствах удаление пространства с N зависимыми активными markers показывает inline blocker с N, не вызывает confirm/backend write и не меняет revisions. Один marker по трём ссылкам считается один раз. | Pure dependency unit + browser smoke. |
| AC10 | После переноса/удаления blockers delete под revision guard удаляет space и весь принадлежащий ему layout без полусостояния; concurrent change даёт conflict/space_in_use; marker metadata/files не удаляются. Единственное оставшееся пространство удаляется и при dependencies: у всех затронутых active/removed markers снимаются только space/room_id, остальные настройки сохраняются, после чего действует empty-state #113. |
Backend transaction tests + smoke_orphan_space_references + smoke_optional_space_model. |
| AC11 | Редактор с отсутствующим default_floor=f1 после загрузки показывает raw id и RU/EN inline error; выбор валидного id убирает её. Runtime открывает первое пространство, toast отсутствует, несвязанное поле не стирает raw value. |
Editor unit + light/dark golden/browser smoke. |
| AC12 | Уже валидные marker/layout/room refs не меняются; входы не мутируются; model version не создаёт запись сам по себе; старый schema fixture читается. | Immutability/compatibility/idempotence units. |
| AC13 | Проход линейный по spaces + rooms + markers + layout и не входит в render/state tick; permission и optimistic-lock guards не ослаблены. | Code inspection + large synthetic unit/backend test. |
| AC14 | Пользовательская документация, оба changelog, docs screenshots, golden-impact и три bundle-копии синхронны; рабочие gates зелёные. | Docs check, reviewed docs artifact, golden report, bundle hashes, typecheck/unit/build/targeted smoke/mutation. |
15. План реализации и автотестов
Reference repair оформляется чистым typed helper рядом с plan-optimizer.ts,
принимающим cloned config/layout и optional production placement context. Контекст
строится один раз при открытии Optimize из того же roster snapshot, а не
вычисляется внутри цикла маркеров. Helper возвращает candidate и отчёт; сам
optimizePlans() объединяет его с существующими geometry passes.
Delete dependency collection также выносится в чистую функцию. Frontend даёт быстрый объяснимый blocker, backend остаётся авторитетом и использует ту же семантику через contract fixtures. Конкретное имя WS command и внутренний формат pending transaction свободны, но config/layout не должны быть видны клиентам в разных поколениях.
build_space_merge() применяет уже построенный id_map к clone current target
до финального merge. Preview, revalidate и apply обязаны возвращать одинаковый
repair count для одного target revision.
Тестовый план:
test/plan-optimizer.test.mjs: signature, Area, detach, removed, virtual, layout, nested refs, immutability и idempotence;- focused
devices/placement unit: registry Area и manual null-Area; - новый pure unit удаления и editor source/UI unit;
tests_backend/test_ha_import_export.py: target-reference repair и no-remap;tests_backend/test_ha_websocket.py: delete permission, blockers, revisions, transaction recovery и last-space;- targeted production-bundle smoke
demo/smoke_orphan_space_references.mjs: Preview/Cancel/Apply/Undo, повторный no-op, delete blocker и editor warning; - mutation entries: отключение detach, выбор первого из двух signature- кандидатов и сохранение stale layout при Area-remap;
- golden scenes: orphan Optimize preview в dark и invalid
default_flooreditor в light/dark. Semantic guard проверяет counters/raw id до PNG compare.
В implementation-цикле запускаются typecheck, unit, build, selected smokes, mutation и docs check. Полные smoke/golden/performance остаются предрелизными; golden baseline принимается только reviewed Linux pre-beta процессом.
16. Риски, производительность и security
| Риск | Мера |
|---|---|
| Ложный signature переносит маркер на чужую копию | Только необрезанный exact stem, ровно один кандидат и отсутствие исходного id; boundary/mutant. |
| Area и persisted room расходятся | Используется production effective Area; manual null-Area сохраняет приоритет. |
| Старые координаты попадают на чужой план | x/y сохраняются только для exact import/signature; Area/detach удаляет marker-position. |
| Optimize удаляет пользовательские настройки | Удаляются только placement fields/position; marker record, nested vacuum data без exact map, metadata и файлы сохраняются. |
| Delete preflight устаревает между click и write | Backend повторяет проверку под lock и ожидает обе revisions. |
| Два store write дают полусостояние | Pending/recovery transaction по образцу Optimize либо эквивалентная доказанная атомарность; backend failure tests. |
| Invalid YAML перезаписывается при редактировании другого поля | Editor сохраняет raw option до явного выбора; unit. |
| Legacy data создаёт огромный warning | Полный set в report, UI summary 10 id + remaining count, text-only rendering. |
Repair строит maps/sets один раз и проходит данные линейно. MAX_SPACES, MAX_MARKERS и MAX_LAYOUT уже ограничивают объём. Нового кода в frame render, HA state update или pointermove нет; отдельный runtime performance baseline не нужен, но large synthetic unit защищает от вложенного marker x space scan.
Backend-команды сохраняют действующую write permission и revision guards. Новых сетевых источников, service calls, HTML injection, файловых удалений и передачи данных третьим сторонам нет.
17. Release-артефакты и rollback
Изменение пользовательское. Implementation-коммит имеет User-Visible: yes и
включает:
docs/CHANGELOG.mdиdocs/CHANGELOG.ru.mdсо ссылкой на #244;docs/USER-GUIDE.mdиdocs/USER-GUIDE.ru.md: reference repair в Optimize, delete blocker и поведение fallback;docs/CONFIG-COMPATIBILITY.md: explicit migration/compatibility/import;docs/TESTING.md: unit/backend/smoke/mutation/golden coverage;- RU/EN i18n, три синхронные bundle-копии и обязательный docs screenshot
artifact после
src/**; - reviewed golden candidates из §15 и точный impact report относительно
origin/dev.
Новый performance/security baseline не нужен. Golden не принимается локально. Rollback — revert frontend/backend implementation-коммитов вместе. Уже восстановленные записи остаются валидными; исходные мёртвые значения можно вернуть существующим Optimize Undo до следующей записи либо backup. Откат backend delete-защиты не должен восстанавливать уже удалённое пространство.
18. Принятые предположения
Эти технические решения приняты предположительно и могут быть свободно изменены ревьюером без нового продуктового решения владельца:
- Exact signature ограничен stem длиной до 35: лучше отправить длинный id в безопасный detach, чем считать обрезанную строку обратимой.
- Removed tombstone не считается активным устройством и не detach'ится по догадке; доказуемый import/signature map для него всё же применяется.
- Unattached layout без signature сохраняется: standing rule запрещает удалять пользовательские данные только потому, что владелец сейчас не найден.
- Marker position при Area/fallback удаляется целиком, а не только теряет
s, чтобы legacy position не ожила позднее в случайном пространстве. - Inline delete blocker живёт в существующем редакторе пространства; отдельный modal не добавляется.
- UI показывает первые десять dead id, report хранит полный отсортированный массив.