### R-1 · techlead · раздел 4 · blocker · fixed
Транспорт реального времени назван «тот же канал, которым доставляются edit/delete», и выбор явно передан в раздел 7 — но от него зависит, реализуемы ли US-5 («Обновление в реальном времени») и US-10 (граница «освобождение слота») в том виде, как они написаны. Без доступа к коду: закрепление — событие уровня чата (меняются `pinned_count`, состав списка, превью в шапке), а edit/delete — событие уровня сообщения, которое клиент применяет к объекту, уже находящемуся в открытой ленте. Если существующий канал доставляет только дельты по подгруженным сообщениям, то закрепление сообщения из глубины истории до второго клиента не дойдёт: счётчик и превью в шапке у него не обновятся, а лимит 50 у двух клиентов разойдётся. Отдельно не описана догоняющая синхронизация: клиент был закрыт или офлайн в момент закрепления/открепления — откуда он берёт актуальный список и счётчик при возврате (полная перезагрузка списка при открытии чата? отдельный запрос дельты?). Нужен ответ от backend: тип и адресация события (chat-scope или message-scope), гарантия доставки, поведение при переподключении. От ответа зависит текст US-5 и раздела 4 («Источники и синхронизация»).
**Ответ:** требование к серверу зафиксировано в разделе 4 («Источники и синхронизация»): событие закрепления адресуется чату целиком, несёт `pinned_count` и верхнюю запись списка, после офлайна клиент перезапрашивает список; транспорт — открытый вопрос 1 раздела 7 (backend). analyst, po
**Перепроверка:** techlead: fixed — раздел 4 «Источники и синхронизация»: событие уровня чата (chat-scope) с актуальными `pinned_count` и верхней записью, доставка всем открытым клиентам чата, после переподключения — полная ресинхронизация списка; транспорт остался вопросом 1 раздела 7.

### R-2 · techlead · раздел 4 · blocker · fixed
Автооткрепление при удалении «у всех» (US-11) описано как результат, но не сказано, кто его исполняет, — а от этого зависит, сойдётся ли счётчик у всех участников. Без доступа к коду вижу два варианта с разной ценой: (а) сервер в обработчике удаления сообщения снимает `PinnedMessage` и рассылает отдельное событие открепления — это правка серверного кода вне фичи, в чужом обработчике; (б) каждый клиент сам вычёркивает запись, получив событие delete, — тогда клиент, которого в этот момент не было в сети, останется с записью в списке и с занятым слотом лимита (50/50 и заблокированным «Закрепить»), пока не перезагрузит чат. Второй незакрытый случай: удаление родительского сообщения треда. По дампу тред — модальное окно поверх ленты (`screen:otkrylsya-tred`, node 517-468976), то есть комментарии живут внутри родительского сообщения; удаление родителя «у всех», судя по всему, делает недоступными и комментарии, но ТЗ (US-11, раздел 4 «Удалённые объекты») говорит только про удаление самого закреплённого сообщения. Открепляются ли закреплённые комментарии удалённого треда — ответ нужен от analyst (сценарий) и backend (где каскад).
**Ответ:** раздел 4 («Удалённые объекты»): каскад открепления исполняется на сервере в обработчике удаления «у всех» и рассылается событием; удаление родительского сообщения треда открепляет закреплённые комментарии. analyst
**Перепроверка:** techlead: fixed — раздел 4 «Удалённые объекты»: каскад открепления исполняется на сервере в обработчике удаления «у всех» и рассылается событием (не вычисляется клиентом), удаление родителя треда открепляет его закреплённые комментарии; US-11 «Успех» ссылается туда же.

### R-3 · techlead · раздел 3 · blocker · fixed
Переход к закреплённому сообщению с подгрузкой истории (US-7, граница «история не подгружена») — самое дорогое требование фичи, и оно описано одной строкой «клиент подгружает недостающий диапазон истории, затем скроллит и подсвечивает». Без доступа к коду не могу подтвердить, что у ленты есть операция «загрузить окно вокруг `message_id`»: для чат-ленты типична пагинация «страницами от конца», и тогда реализация — либо новый серверный endpoint (контекст вокруг сообщения: N сообщений до и после), либо циклическая догрузка страницами, которая на сообщении полугодовой давности даёт десятки последовательных запросов и секунды ожидания. Нефункциональных требований к этому переходу нет вовсе: не назван порог времени, за который переход считается выполненным; не описано, что видит пользователь во время долгой загрузки (список уже закрыт — спиннер поверх ленты? прогресс? возможность отменить?); не названа верхняя граница, после которой переход признаётся неуспешным и что при этом отправляется в `pinned_jump` (`result` знает только `ok` и `not_found`). Нужен ответ от backend (существует ли endpoint контекста) и от PO/analyst (порог и поведение на долгой загрузке; нужен ли третий `result`, например `timeout`).
**Ответ:** US-7 «Граница»: требование «окно истории вокруг `message_id`» одним запросом, индикатор в записи списка, при неуспехе `pinned_jump` (`result = load_failed`) и повтор; пороги времени — открытый вопрос разработчику (раздел 7). analyst
**Перепроверка:** techlead: fixed — US-7 «Граница»: окно истории вокруг `message_id` одним запросом (явно «не циклической догрузкой страницами»), индикатор в записи списка, при неуспехе `pinned_jump` с `result=load_failed` и повтор; исход `load_failed` заведён в разделе 1, состояния «загрузка перехода» и «не удалось загрузить» есть в `screens/pinned-list.html`; пороги времени — вопрос 13 раздела 7.

### R-4 · techlead · раздел 3 · major · fixed
Переход к закреплённому комментарию треда (US-8) дороже, чем переход в ленте, и в ТЗ это не отражено. Без доступа к коду, по дампу `screen:otkrylsya-tred` (517-468976): тред — модальное окно поверх ленты, внутри которого сверху показано родительское сообщение, а под ним список комментариев. Значит, переход из списка закреплённых требует трёх шагов, ни один из которых в ТЗ не назван: (1) по `thread_id` записи `PinnedMessage` получить родительское сообщение — если `thread_id` не равен `message_id` родителя, нужен серверный resolve; (2) открыть модалку треда, что само по себе может потребовать загрузки родительского сообщения, которого нет в подгруженной ленте; (3) внутри треда докрутить до комментария, а это, как и в R-3, упирается в наличие пагинации и позиционирования у списка комментариев (есть ли она у треда вообще — по макету видно всего три комментария, глубокие треды в дампе не представлены). Плюс не сказано, что происходит с открытым списком закреплённых: он закрывается до открытия треда или тред открывается поверх него (два наложенных слоя — см. R-17). Нужен ответ от backend (resolve треда и пагинация комментариев) и ux (порядок слоёв).
**Ответ:** US-8 «Граница»: открыть тред по `thread_id` и загрузить окно вокруг комментария одним запросом, тот же индикатор и `load_failed`. analyst
**Перепроверка:** techlead: fixed — US-8 «Граница»: тред открывается по `thread_id`, окно истории вокруг комментария одним запросом, тот же индикатор и `load_failed`; порядок слоёв записан только переходом карты `pinned-list → otkrylsya-tred`, словами в 5.1 не сказано — остаток, реализацию не блокирует.

### R-5 · techlead · раздел 4 · major · fixed
Раздел 4 сам фиксирует, что коды ошибок API неизвестны, и передаёт вопрос сюда, — называю цену этого пробела. Без доступа к коду и без контракта бэкенда клиент не может различить сценарии, которые ТЗ требует показывать по-разному: «уже закреплено / уже откреплено» (по разделу 4 — идемпотентный успех, надо молча обновить UI), «лимит исчерпан» (US-10, гонка за слот: откат оптимистичного состояния и текст про лимит), «нет прав» (US-2, US-13: текст «Недостаточно прав»), «сообщение не найдено» (US-7/US-8), сетевая ошибка (нейтральный текст, повтор). Пока это один «нейтральный текст ошибки», US-10 (граница «гонка за лимит») нереализуема: проигравший клиент не отличит отказ по лимиту от обрыва сети и не поймёт, откатывать состояние или повторять запрос. Минимальный набор, который нужно получить от backend: `already_pinned`, `not_pinned`, `limit_reached`, `forbidden`, `message_not_found`, `chat_not_found` — и для каждого явное ожидаемое поведение клиента (успех / откат / повтор). Также не сказано, на чьей стороне обеспечивается идемпотентность по ключу (`chat_id`, `message_id`, `thread_id`): уникальный индекс на сервере или дедупликация на клиенте — от этого зависит, безопасен ли повтор запроса при обрыве.
**Ответ:** раздел 4 («Ошибки и коды»): коды `already_pinned`, `not_pinned`, `limit_reached`, `forbidden`, `message_not_found`, `chat_not_found` с реакцией клиента; идемпотентность по ключу (`chat_id`, `message_id`, `thread_id`) обеспечивает сервер. analyst
**Перепроверка:** techlead: fixed — раздел 4 «Ошибки и коды»: все шесть кодов с явной реакцией клиента на каждый, плюс «Идемпотентность и повтор» — ключ (`chat_id`, `message_id`, `thread_id`) держит сервер, клиент не дедуплицирует.

### R-6 · techlead · раздел 4 · major · fixed
Свёрнутая строка `screen:pinned-header` показывает счётчик и превью сразу при открытии чата, а `screen:pinned-list` имеет отдельные состояния «Загрузка» и «Ошибка загрузки» — значит, счётчик и превью приходят раньше списка и другим путём, но источник этих двух полей в ТЗ не назван. Без доступа к коду вижу развилку с разной ценой: если `pinned_count` и превью последнего закреплённого приходят в объекте чата, их надо добавить в серверную выдачу чата (а возможно, и в выдачу списка чатов, которой рисуется левый сайдбар) и поддерживать в актуальном состоянии — это правка контракта, которым пользуется не только эта фича; если же они вычисляются на клиенте после загрузки полного списка, то раздел «Закреплённые» не сможет появиться в шапке до открытия панели, и US-5 («When пользователь открывает чат. Then в шапке отображается свёрнутый раздел») не выполняется. Отдельный вопрос — что именно лежит в превью: текст сообщения целиком или заранее обрезанный сервером фрагмент (см. R-10 про нетекстовые сообщения). Ответ нужен от backend и analyst.
**Ответ:** раздел 4 («Источники и синхронизация»): `pinned_count` и верхняя запись списка (с признаком «скрыто у меня») приходят в объекте чата при загрузке; строка в шапке появляется вместе с чатом. analyst
**Перепроверка:** techlead: fixed — раздел 4: `Chat` несёт `pinned_count` и верхнюю запись списка (с признаком «скрыто у меня»), они приходят в объекте чата при загрузке, у строки в шапке нет состояния «загрузка» (5.3); влияние на выдачу списка чатов осталось вопросом 5 раздела 7.

### R-7 · techlead · раздел 5 · major · fixed
US-3 и US-4 требуют закреплять и откреплять комментарий внутри треда «тем же пунктом меню», и раздел 5.2 описывает изменение одного экрана — `screen:deystviya`. Без доступа к коду не могу подтвердить, что меню действий над комментарием в треде и меню действий над сообщением ленты — один и тот же компонент. Данные, которые есть: в карте нет отдельного экрана для меню комментария треда (есть `screen:deystviya` для сообщения ленты и `screen:menyu-deystviy` для режима множественного выбора), а по дампу тред — самостоятельное модальное окно со своим списком комментариев и своими элементами управления. Если в коде это второй компонент, то пункт «Закрепить»/«Открепить» вместе со всеми четырьмя состояниями раздела 5.2 (не закреплено, закреплено, лимит достигнут, нет прав) придётся встроить и протестировать дважды, а макет `screens/deystviya.html` покрывает только первое место. Нужен ответ от desktop-разработчика (один компонент меню или два) и, если два, — решение ux, показывать ли второй макет.
**Ответ:** 5.2: пункт для комментария в треде тот же и в том же положении, макет покрывает меню сообщения ленты; один компонент или два — открытый вопрос 9 раздела 7 (desktop-разработчик). ux
**Перепроверка:** techlead: fixed — 5.2: пункт «Закрепить»/«Открепить» для комментария треда тот же и в том же положении, макет покрывает меню ленты; один компонент меню или два — вопрос 9 раздела 7 (desktop-разработчик).

### R-8 · techlead · раздел 4 · major · fixed
Право «закреплять/откреплять» новое, а способ, которым клиент узнаёт роль администратора, в ТЗ не назван. Без доступа к коду: US-13 (граница) требует, чтобы у не-администратора пункт меню и крестик отсутствовали в разметке полностью — значит, роль должна быть известна клиенту ДО открытия меню, то есть лежать в состоянии чата, а не запрашиваться по клику. Есть ли роль администратора чата в объекте чата, который клиент уже получает, или её придётся туда добавлять (правка серверного контракта), — вопрос к backend. Два следствия без ответа: (1) момент обновления роли — US-2 (ошибка) и US-13 (ошибка) переносят проверку на сервер при отправке запроса, но клиент между снятием роли и получением обновления продолжает рисовать пункт меню; сколько времени длится это расхождение, зависит от того же realtime-канала (R-1); (2) главный чат — по разделу 2 он приравнен к групповому, «закрепляет только администратор», но кто является администратором главного чата (в него, судя по карте, входят все участники организации) и есть ли там вообще такая роль — вопрос к PO и backend. Пока это не отвечено, US-2 и US-13 нельзя тестировать в главном чате.
**Ответ:** решение человека: «Главный чат» по правам — обычный групповой чат, отдельной роли нет (раздел 2; раздел 4 «Права»). po, analyst
**Перепроверка:** techlead: fixed — оба остатка прошлой перепроверки закрыты. (1) Раздел 4 «Права» больше не называет права главного чата открытым вопросом: «"Главный чат" по правам — обычный групповой чат (раздел 2; решение человека, R-8 закрыт: отдельной роли не вводится, администраторы — те же, что у группового чата)» — по разделу 4 теперь однозначно понятно, кто закрепляет в главном чате, и US-2/US-13 там тестируемы наравне с обычным групповым чатом. Вопрос 6 раздела 7 зачёркнут с пометкой о решении человека, «Статус вопросов после возвратов» и пункт «Интеграции» (помечен «Закрыто решением человека») с ним согласованы — противоречия между разделами 2, 4 и 7 нет. (2) Требование «роль известна клиенту до открытия меню» переехало из раздела 7 в раздел 4 «Права»: роль пользователя и тип чата берутся из объекта чата и состава участников, загруженных при открытии чата, отдельного запроса перед отрисовкой меню фича не вводит, серверная проверка при действии остаётся, и явно записано требование к серверу «объект чата несёт роль текущего пользователя» — источник для US-13 (граница) назван. Расхождение роли между снятием админства и обновлением клиента (следствие 1 исходного замечания) закрыто разделом 4 «Параллельные правки» плюс ветками «Ошибка» в US-2 и US-13: проверка на сервере при отправке, `forbidden` на клиенте. Без доступа к коду: реализуемость требования к серверу (несёт ли объект чата роль сегодня или её надо добавлять в контракт) остаётся вопросом к backend — он адресован в разделе 7, вопросы 1 и 5, и статуса R-8 не блокирует.

### R-9 · techlead · раздел 3 · major · fixed
Стратегия обновления интерфейса не выбрана единообразно, и сценарии ТЗ противоречат друг другу. US-10 (ошибка «гонка за лимит») говорит, что проигравший клиент «откатывает оптимистичное состояние», то есть подразумевает оптимистичное обновление; US-1 и US-4 описывают успех так, будто UI меняется после ответа сервера (при ошибке «пункт меню остаётся Закрепить», то есть он и не менялся). Без доступа к коду называю цену обоих вариантов, выбор — за analyst и ux: оптимистичный вариант требует отката в каждом сценарии ошибки, причём отката трёх связанных мест сразу (пункт меню, счётчик и превью в шапке, позиция записи в списке), и создаёт видимое мигание при гонке; пессимистичный вариант требует индикатора ожидания там, где его в макетах нет, — у пункта меню в `screens/deystviya.html` нет состояния «отправка», а меню, по разделу 5.1, «закрывается на месте», то есть пользователю негде увидеть, что запрос ещё идёт. Для `screen:pinned-unpin-confirm` состояние «Отправка» нарисовано — то есть там выбран пессимистичный вариант; фича не может иметь разные стратегии в двух точках входа к одной операции.
**Ответ:** единая стратегия — пессимистичное обновление (раздел 2; раздел 4 «Стратегия обновления интерфейса»; US-10 без отката оптимистичного состояния; 5.2 — у пункта меню нет состояния «отправка», у окна подтверждения есть). analyst, ux
**Перепроверка:** techlead: fixed — раздел 4 «Стратегия обновления интерфейса»: пессимистичная для всей фичи, оптимистичного обновления и отката нет нигде; US-10 «гонка» переписана без отката, 5.2 — у пункта меню нет состояния «отправка», 5.5 — у окна подтверждения есть. Остаток не в моих разделах: в разделе 7 сохранён устаревший пункт «Сейчас US-10 предполагает оптимистичное обновление…» и вопрос 11, которые теперь противоречат разделу 4.

### R-10 · techlead · раздел 4 · major · fixed
Раздел 4 закрывает удаление и скрытие закреплённого сообщения, но два состояния его содержимого не описаны вовсе. Первое — редактирование: закреплённое сообщение отредактировали, и неизвестно, обновляется ли текст превью в шапке (`screen:pinned-header`) и в списке (`screen:pinned-list`), приходит ли для этого отдельное событие, или превью — снимок на момент закрепления. Без доступа к коду не берусь угадывать, хранит ли клиент текст сообщения по ссылке на объект сообщения или копией; обе реализации возможны и дают разное поведение. Второе — нетекстовое сообщение: и макет `pinned-list.html`, и превью в `pinned-header.html` показывают только текст, а по дампу ленты (`screen:deystviya`, 517-466982) сообщение может быть картинкой, файлом или сообщением с одними реакциями. Что показывать в превью для такого сообщения — «Изображение», имя файла, пустую строку — не решено ни в ТЗ, ни в макете. Вопрос к analyst (сценарий edit и перечень типов контента) и ux (вид превью для нетекстового сообщения).
**Ответ:** раздел 4 («Превью»): превью нетекстовых сообщений «Фото» / «Видео» / «Файл: <имя>» / «Голосовое сообщение», обновление превью по событию редактирования; 5.3/5.4 и `pinned-list.html`. analyst, ux
**Перепроверка:** techlead: fixed — раздел 4 «Превью»: подписи «Фото» / «Видео» / «Файл: <имя>» / «Голосовое сообщение» и обновление превью по существующему событию редактирования; то же в 5.3, 5.4 и в `screens/pinned-list.html`.

### R-11 · techlead · раздел 4 · major · fixed
У фичи, скорее всего, будет два разных «лимита», а ТЗ описывает только один. Без доступа к коду, но по деривативам дампа: в продукте уже есть серверные частотные ограничения на действия над сообщениями — в спеках экранов встречаются тосты «Достигнут лимит удаления сообщений. Попробуйте через 5 минут», «Достигнут лимит пересылки сообщений. Попробуйте через 1 минуту», «Достигнут лимит удаления реакций», «Достигнут лимит отправки сообщений». Если закрепление/открепление попадает под тот же механизм (а это операция того же класса — действие над чужим сообщением, доступное для повторения кликом), то появится путь ошибки, которого нет ни в одном сценарии раздела 3, и два разных смысла у одного слова: ёмкостный лимит (50 закреплённых на чат) и частотный (N действий за интервал). Текст «Достигнут лимит 50 закреплённых сообщений» в этом соседстве читается двусмысленно. Нужен ответ backend (попадают ли pin/unpin под общий rate limit и с какими порогами) и ux (различимые тексты).
**Ответ:** раздел 4 («Ошибки и коды»): частотный лимит на действия над сообщениями назван отдельным случаем с существующим текстом ошибки, отличным от лимита 50; пороги — открытый вопрос 7 раздела 7 (backend). analyst
**Перепроверка:** techlead: fixed — раздел 4 «Ошибки и коды»: частотный лимит назван вторым, отличным от лимита 50 случаем с существующим текстом ошибки; попадают ли pin/unpin под него и пороги — вопрос 7 раздела 7 (backend).

### R-12 · techlead · раздел 7 · major · fixed
Выкат и обратная совместимость в ТЗ не упомянуты ни разу, хотя фича добавляет новую сущность (`PinnedMessage`), новые поля в выдаче чата (R-6) и новый тип realtime-события (R-1). Без доступа к коду фиксирую три вопроса, которые придётся решить до релиза. (1) Миграция данных как таковая не нужна — сущность новая и пустая, бэкофилл ей не требуется; это единственный пункт, который могу назвать без ответа от backend. (2) Старые desktop-клиенты, собранные до фичи, получат в объекте чата неизвестные поля и по каналу — неизвестный тип события: терпят ли они это молча или ломаются, зависит от того, как написан их парсер, — вопрос к desktop-разработчику. (3) Смешанный парк клиентов: администратор с новым клиентом закрепил сообщение, участники со старым его не видят, но слот лимита уже занят и общий счётчик изменился — это допустимое поведение или фичу надо гейтить (feature flag на организацию/чат, принудительное обновление клиента)? Вопрос к PO и backend. Пока ответа нет, срок «через 30 дней после релиза desktop» из раздела 1 не имеет определённой точки отсчёта.
**Ответ:** решение человека: схема выката остаётся на усмотрение разработчиков — открытый вопрос 8 раздела 7 адресован разработчикам, без PO. po
**Перепроверка:** techlead: fixed — раздел 7: миграция не требуется (сущность новая), схема выката и реакция старого клиента вынесены вопросом 14 разработчикам. Остаток: дублирующий вопрос 8 «Backend + PO» остался в списке, хотя ниже объявлен закрытым.

### R-13 · techlead · раздел 1 · major · fixed
У метрик раздела 1 не у всех есть источник, и это видно уже по самому ТЗ. Без доступа к коду: (1) поля `pinned_count_after` и `pinned_count` клиент может заполнить достоверно только если счётчик приходит с сервера (R-6); если он считается локально, при гонке двух администраторов события уедут, и целевой показатель «`pinned_jump` / `pinned_panel_open` ≥ 0,5» будет считаться по расходящимся данным. (2) Метрика «доля активных групповых чатов (≥ 1 сообщение за 30 дней) с хотя бы одним закреплённым сообщением ≥ 20 %» из клиентской телеметрии не считается в принципе: она требует серверной выборки по таблице `PinnedMessage` в пересечении с активностью чатов, а раздел 1 указывает единственным источником «телеметрию desktop-клиента». (3) Событие `pinned_jump` имеет только `result = ok | not_found`, при этом целевой показатель «доля `not_found` < 1 %» не различает «сообщение действительно удалено» и «переход не удался по таймауту загрузки истории» (R-3) — второй случай попадёт либо в `ok`, либо никуда. Вопрос к PO и аналитике: кто и откуда считает метрику 1, и нужен ли третий исход у `pinned_jump`.
**Ответ:** раздел 1 переписан: охват считается на сервере по таблице закреплений; `pinned_count_after` берётся из ответа сервера; у `pinned_jump` добавлен исход `load_failed`. po
**Перепроверка:** techlead: fixed — раздел 1 переписан: охват считается на сервере по таблице закреплений, `pinned_count_after` берётся из ответа сервера, у `pinned_jump` появился исход `load_failed`, контроль качества считает `result ≠ ok`.

### R-14 · techlead · раздел 5 · major · fixed
Превью в свёрнутой строке `screen:pinned-header` показывает автора и текст последнего закреплённого сообщения, но состояния «этот пользователь скрыл сообщение у себя» у неё нет — ни в таблице состояний раздела 5.3, ни в макете `screens/pinned-header.html` (там только «заполнено» и «пусто»). При этом US-12 требует, чтобы у скрывшего вместо содержимого показывалась заглушка. Получается прямое противоречие: в списке пользователь видит «сообщение скрыто», а в шапке того же чата — текст, который он у себя скрыл. Тот же пробел для короткого промежутка после удаления «у всех», пока событие открепления не дошло до клиента (R-1, R-2): что рисует шапка в этот момент — старое превью или заглушку. Без доступа к коду не знаю, доступен ли клиенту флаг «скрыто у меня» в том же ответе, из которого берётся превью, — это часть вопроса R-6 к backend. Нужен ответ analyst (сценарий) и ux (состояние в макете `pinned-header`).
**Ответ:** раздел 2 и раздел 4: верхняя запись приходит с признаком «скрыто у меня»; 5.3 и `pinned-header.html` — состояние строки с заглушкой вместо превью. analyst, ux
**Перепроверка:** techlead: fixed — раздел 4: верхняя запись приходит с признаком «скрыто у меня»; 5.3 несёт отдельную строку состояния «верхняя запись скрыта у меня» с заглушкой вместо превью, и это же состояние есть в `screens/pinned-header.html`.

### R-15 · techlead · раздел 5 · minor · fixed
Два новых компонента раздела 5.6, похоже, дублируют то, что в дизайн-системе уже есть, — это лишняя стоимость и расхождение стилей. Без доступа к коду, по реестру дампа `component-inventory.md`: у тултипа есть мастера `Arrow=Default` (`1:5386`, 47 использований) и `Arrow=Top` (`1:5391`, 27 использований), и оба используются на сайтах с именем «Tooltip» — то есть компонент подсказки в системе существует, а раздел 5.6 заводит новый `tooltip-hint` с обоснованием «отдельного компонента тултипа в реестре нет» (в `spec/design-system/components.yaml` его действительно нет, но реестр наполнен не полностью). Отдельно: раздел 4 и сценарии ошибок US-1…US-4 говорят «показывается сообщение об ошибке», не называя носителя, — в дампе есть мастер `Color=Orange, Size=XL, State=Default` (`1:5322`) на сайтах «Toaster», то есть механизм тоста в продукте уже есть, и на него стоит опереться явно, а не оставлять форму показа ошибки на усмотрение разработчика. Вопрос к ux.
**Ответ:** в `components.yaml` найдены Tooltip (`arrow-default`, `c-659abea9`, мастер `1:5386`) и Toaster (`color-orange-size-xl-state-default`, `c-d387a7c9`, мастер `1:5322`); `tooltip-hint` убран, `deystviya.html` переведён на `c-659abea9`, Toaster назван носителем ошибок в 5.2; 5.6 переписан. ux
**Перепроверка:** techlead: fixed — Tooltip (`c-659abea9`) и Toaster (`c-d387a7c9`) действительно присутствуют в `spec/design-system/components.yaml`, `screens/deystviya.html` использует `c-659abea9`, 5.6 больше не заводит `tooltip-hint`, Toaster назван носителем ошибок в 5.2. Мелочь: в таблице раздела 6 в названии строки осталось слово «tooltip-hint».

### R-16 · techlead · раздел 5 · minor · fixed
Не определено, во сколько мест кода приезжает строка `screen:pinned-header`. Раздел 5.3 говорит «встраивается в шапку `screen:lichnyy-chat`, `screen:gruppovoy-chat`, `screen:glavnyy-chat`», и ровно эти три экрана помечены `FEAT-1` в карте. Но шапку чата в карте рисуют как минимум ещё `screen:zametki`, `screen:lichnyy-chat-pustoy`, `screen:otkryli-chat`, `screen:lichnyy-chat-2/3/4` и `screen:vse-kommentarii`, а раздел 2 отдельно оговаривает, что «Заметки» работают по правилам личного чата, — при этом `screen:zametki` в ТЗ не упомянут ни разу. Без доступа к коду: если Header и лента — один контейнер, фича автоматически приезжает во все эти экраны, включая «Все комментарии» (агрегированная лента комментариев из разных чатов, где «закреплённые этого чата» смысла не имеют) — и тогда нужно явное правило, где строку не рендерить; если контейнеров несколько, стоимость встраивания умножается на их число. Вопрос к desktop-разработчику (сколько контейнеров) и PO (нужна ли строка в «Заметках» и точно ли не нужна во «Всех комментариях»).
**Ответ:** 5.3: строка не рендерится в screen:vse-kommentarii и на экранах без ленты одного чата; число контейнеров шапки в коде — открытый вопрос 9 раздела 7 (desktop-разработчик). ux
**Перепроверка:** techlead: fixed — 5.3: строка не рендерится в `screen:vse-kommentarii` и на экранах без ленты одного чата, «Заметки» внесены в носители строки (разделы 2, 5.3); число контейнеров шапки в коде — вопрос 9 раздела 7.

### R-17 · techlead · раздел 5 · minor · fixed
Поведение слоёв и клавиатуры не задано, а фича строит два наложенных слоя поверх ленты: `screen:pinned-list` — попап, привязанный к шапке, и `screen:pinned-unpin-confirm` — модальное окно поверх него (`aria-modal="true"` в макете). Без доступа к коду не знаю, как в продукте уже устроен стек оверлеев, поэтому это вопрос, а не требование: что закрывает Esc, когда открыты оба слоя (сначала подтверждение, потом список — или сразу оба); закрывается ли попап по клику вне его и не конфликтует ли это с кликом по ленте, к которой он привязан; куда возвращается фокус после закрытия списка и после перехода к сообщению (US-7 закрывает список и скроллит ленту — фокус остаётся в никуда); удерживается ли фокус внутри модалки подтверждения. Плюс мелочь из макета `pinned-header.html`: `aria-live="polite"` стоит на превью, то есть при каждом чужом закреплении (US-5, обновление в реальном времени) программа чтения с экрана будет зачитывать новый текст превью — намеренно ли это. Вопрос к ux.
**Ответ:** 5.1/5.4/5.5: Esc закрывает верхний слой первым (окно подтверждения, затем панель), фокус удерживается в модальном окне и возвращается на элемент-источник; `aria-live` на превью не ставится (решение PO), атрибут убран из `pinned-header.html`. ux
**Перепроверка:** techlead: fixed — 5.1/5.4/5.5: Esc закрывает верхний слой первым, фокус удержан в модальном окне и возвращается на вызвавший элемент, закрытие панели возвращает фокус на строку шапки; `aria-live` в `screens/pinned-header.html` отсутствует.

### R-18 · qa · раздел 5 · major · fixed
Один и тот же `pipeline_class` описан в разделе 5.6 как один компонент, но в двух HTML-макетах фичи реализован по-разному — контракт компонента неоднозначен. (1) `tertiary-button` (c-58ef8e45, раздел 5.6: «"Отмена"/"Повторить"») — в `screens/pinned-list.html` это `.c-58ef8e45{border:0;background:transparent;color:var(--c-sapphire);...}` (прозрачная текстовая ссылка, кнопка «Повторить»), а в `screens/pinned-unpin-confirm.html` — `.c-58ef8e45{border:0;background:#F5F5F5;color:var(--c-text);...}` (заливка светло-серым, кнопка «Отмена»): два визуально разных компонента под одним id. (2) `icon-outlined-16px-circlewarning` (c-2b7ddf88) — собственное имя заявляет 16px, но в `screens/pinned-list.html` он `.c-2b7ddf88{width:24px;height:24px;...font-size:14px}` (24×24), а в `screens/pinned-unpin-confirm.html` — `.c-2b7ddf88{width:16px;height:16px;...font-size:11px}` (16×16, как в имени): размер не совпадает даже с собственным названием компонента в одном из двух мест. (3) `type-message` (c-d4c5a3f5) — в `pinned-list.html` это `<p class="c-d4c5a3f5">` с line-clamp текста сообщения, а в `pinned-unpin-confirm.html` — `<div class="c-d4c5a3f5">`, flex-контейнер, оборачивающий аватар и весь блок превью целиком: разная структурная роль под одним id. Тест на «компонент X ведёт себя так-то» не пишется, пока неясно, какой из двух вариантов — эталон.
**Ответ:** `tertiary-button` (c-58ef8e45), `icon-outlined-16px-circlewarning` (c-2b7ddf88, 16×16) и `type-message` (c-d4c5a3f5, единая разметка) унифицированы во всех четырёх HTML. ux
**Перепроверка:** qa: fixed — `screens/pinned-list.html` и `screens/pinned-unpin-confirm.html`: `.c-58ef8e45` в обоих файлах теперь идентичен (`border:0;background:transparent;color:var(--c-sapphire);font-size:13px;font-weight:700`, строки pinned-list.html:120, pinned-unpin-confirm.html:65); `.c-2b7ddf88` в обоих 16×16 (pinned-list.html:116, pinned-unpin-confirm.html:82); `.c-d4c5a3f5` в обоих — `<div>`-обёртка «аватар + метаданные» с одинаковыми стилями (`display:flex;gap:10px;align-items:flex-start;flex:1;min-width:0`, pinned-list.html:65, pinned-unpin-confirm.html:44). Контракт компонента однозначен во всех трёх случаях.

### R-19 · qa · раздел 5 · major · fixed
Раздел 5.6 утверждает: «Ни один из следующих не найден в `spec/design-system/components.yaml`, все помечены `data-new-component` в HTML» — и перечисляет `pinned-summary-bar` («свёрнутая строка "Закреплённые" целиком»). Но в `screens/pinned-header.html` корневой элемент этого компонента — `<button class="pinned-summary-bar" type="button" aria-expanded="false" aria-haspopup="dialog">` — атрибута `data-new-component` не несёт; помечен только вложенный `icon-pin` внутри него. Прямое расхождение между утверждением раздела 5 и макетом: по конвенции конвейера (`product-pipeline.md`, «Новые и изменённые экраны») каждый новый класс обязан быть помечен, чтобы desktop-разработчик мог найти все новые компоненты `grep`-ом — сейчас `pinned-summary-bar` этим способом не находится.
**Ответ:** `data-new-component="pinned-summary-bar"` добавлен на корневой `<button>` в `pinned-header.html`. ux
**Перепроверка:** qa: fixed — `screens/pinned-header.html:88-89` и `:116-117`: `<button class="pinned-summary-bar" ... data-new-component="pinned-summary-bar">` в обоих заполненных состояниях; grep по `data-new-component` теперь находит компонент.

### R-20 · qa · раздел 3 · major · fixed
Часть AC явно фиксирует, отправляется ли телеметрическое событие в пограничном/ошибочном сценарии, часть — нет, и тест на «событие не отправилось» по второй группе не пишется. US-1 «Ошибка» и US-3 «Ошибка» заканчиваются явной фразой «`message_pin` не отправляется» — но US-2 «Ошибка (потеря прав в процессе)» такой фразы не содержит: Then заканчивается на «пункт меню не переключается на "Открепить"», без утверждения про `message_pin`. Аналогично US-4 «Граница (гонка)»: «действие идемпотентно: сервер подтверждает, что сообщение уже не закреплено, клиент обновляет интерфейс без ошибки пользователю» — не сказано, отправляется ли `message_unpin` и с каким `pinned_count_after` при идемпотентном ответе сервера. И US-11 «Ошибка/гонка»: «повторное открепление — идемпотентный no-op без ошибки пользователю» — тоже не указано, шлёт ли повторное действие `message_unpin`. Без явного Then «событие X отправляется / не отправляется» для каждого из этих трёх случаев тест телеметрии по ним не пишется однозначно.
**Ответ:** во всех ветках AC US-1…US-13 явно сказано, какое событие отправляется или «событие не отправляется». analyst
**Перепроверка:** qa: fixed — US-2 «Ошибка (потеря прав в процессе)»: «`message_pin` не отправляется» (spec.md:47); US-4 «Граница (гонка)»: «отправляется `message_unpin` (`source=context_menu`, `pinned_count_after` — значение, подтверждённое сервером)» (spec.md:233); US-11 «Ошибка/гонка»: обе ветки указаны явно — «отправляется `message_unpin` … подтверждённым сервером» либо «действие не инициируется, событие не отправляется» (spec.md:429-432). Все три случая из замечания закрыты.

### R-21 · qa · раздел 5 · minor · fixed
Таблица состояний `screen:pinned-header` (раздел 5.3) перечисляет «Пусто», «Заполнено», «Обновление в реальном времени» — состояний «Загрузка» и «Ошибка» нет вовсе, хотя счётчик и превью должны быть чем-то заполнены до первого рендера. Раздел 4 закрывает офлайн только для раскрытого списка: «Просмотр уже раскрытого списка закреплённых (данные, полученные до потери сети) остаётся доступен для чтения» — про саму свёрнутую строку в шапке ничего не сказано. Неясно: что видит пользователь в `pinned-header`, пока `pinned_count`/превью ещё не получены при открытии чата (пустое место? спиннер? строка не рендерится до прихода данных, как в состоянии «Пусто»?), и что происходит с уже показанной строкой при потере сети — остаётся последнее известное значение или прячется. Без ответа состояние «Загрузка»/офлайн для этого экрана не тестируемо.
**Ответ:** раздел 4: у строки в шапке нет состояния «загрузка», она появляется вместе с данными чата; офлайн — последнее известное состояние; 5.3 то же. analyst, ux
**Перепроверка:** qa: fixed — раздел 5.3 явным абзацем: «Раздел не имеет отдельного состояния "загрузка": `pinned_count` и превью верхней записи списка приходят в объекте чата при его загрузке … Состояния "офлайн" как отдельного визуального состояния тоже нет: при потере сети строка показывает последнее известное состояние без индикации ошибки» (spec.md, 5.3, со ссылкой на R-21). Оба вопроса (загрузка, офлайн для свёрнутой строки) закрыты.

### R-22 · qa · раздел 1 · minor · fixed
Целевой показатель раздела 1 — «Доля активных групповых чатов (≥ 1 сообщение за 30 дней) с хотя бы одним закреплённым сообщением — ≥ 20 %» — не определяет, входит ли в подсчёт «Главный чат». Раздел 4 задаёт `PinnedMessage.chat_type` только двумя значениями (personal | group), и US-2 явно объединяет обычный групповой чат и «Главный чат» под одним `chat_type=group`; при этом «Главный чат», судя по карте (screen:glavnyy-chat), включает всех участников организации и по активности не сопоставим с обычной группой. Не сказано, считается ли он отдельно от «групповых чатов» в этой метрике или входит в общий знаменатель/числитель — без ответа результат KPI зависит от того, как именно посчитают, а не только от того, откуда возьмут данные (это уже R-13). Вопрос — к PO и аналитике, тот же адресат, что и R-13, но другой по сути: не источник данных, а определение категории.
**Ответ:** раздел 1: «Главный чат» входит в охват как групповой чат. po
**Перепроверка:** qa: fixed — раздел 1, метрика «Охват»: «доля активных групповых чатов (≥ 1 сообщение за 30 дней; "Главный чат" входит как групповой) с хотя бы одним закреплением — ≥ 20 %» (spec.md, раздел 1). Категория для подсчёта KPI однозначна.

### R-23 · challenger · раздел 2 · blocker · fixed
Фича объявлена `Приоритет. high` и доводится до фиксации, хотя ни один агент конвейера не подтвердил, что она вообще реализуема. Раздел 7 открывается признанием «Раздел составлен техлидом без доступа к коду продукта… Это ограничения и открытые вопросы, а не вердикты о реализуемости», и внутри него три blocker'а (R-1 транспорт chat-scope, R-2 серверный каскад при удалении, R-3 операция «контекст вокруг `message_id`») — каждый отдельная серверная работа, существование которой неизвестно. Допущение, которое никто не назвал вслух: «бэкенд это умеет». Если хоть один ответ отрицательный, десктопная часть — меньшая половина фичи, а сроки и приоритет назначены до ответа. В ТЗ нет ни одного места, где сказано: до ответов backend по R-1/R-2/R-3 объём работы неизвестен и `high` ничем не обеспечен. Раздел 2 фиксирует скоуп и приоритет раньше, чем раздел 7 задал вопросы, от которых скоуп зависит, — порядок принятия решений перевёрнут.
**Ответ:** решение человека: приоритет high сохранён; раздел 2 несёт оговорку, что серверный объём (вопросы 1–3 раздела 7) в конвейере не оценён и приоритет означает очерёдность, не оценку. po
**Перепроверка:** challenger: fixed — раздел 2, блок «Приоритет»: `high` сохранён, но рядом стоит именно то, чего не было, — «объём серверной части (вопросы 1–3 раздела 7) в конвейере не оценён, кода продукта здесь нет; приоритет означает очерёдность, а не подтверждённую оценку». То же продублировано в разделе 7 («Статус вопросов после возвратов») и в разделе 8, п. 1. Допущение «бэкенд это умеет» названо вслух в трёх местах. Риск остаётся принятым: приоритет назначен до ответов backend — это решение человека, записанное с причиной.

### R-24 · challenger · раздел 3 · blocker · fixed
US-7 и US-8, ветка «Ошибка (не найдено)»: «показывается сообщение "сообщение не найдено", автоскролл не выполняется, отправляется `pinned_jump` (`result=not_found`), **запись удаляется из списка**». Удаляется откуда — не сказано, и по тексту это клиентская операция: серверного вызова нет, `message_unpin` не отправляется, `pinned_count` не упомянут. Но список закреплённых по разделу 4 — «общее состояние чата, видимое одинаково всем участникам», а счётчик в шапке (раздел 5.3) — это `pinned_count`. Сценарий слома: у пользователя А в списке 4 записи, счётчик 4; он кликает по записи удалённого сообщения, видит «не найдено», запись пропадает — в панели 3 строки, в шапке по-прежнему 4. У пользователя Б всё ещё 4 строки. Слот лимита занят. Перезагрузка чата возвращает исчезнувшую запись обратно — пользователь видит, как «удалённое» вернулось. Это не R-2 (там вопрос, где исполняется каскад при удалении «у всех»): здесь ТЗ прямо предписывает клиенту вычеркнуть запись общего состояния локально и молча разъехаться с сервером. Тот же текст повторён в разделе 5.1 («запись убирается из списка»), то есть это не оговорка в одном месте.
**Ответ:** US-7/US-8 «Ошибка (не найдено)» и раздел 4 («Удалённые объекты»): клиент не удаляет запись локально, а перезапрашивает список у сервера и показывает его ответ; 5.4 — состояние записи «не найдено». analyst, ux
**Перепроверка:** challenger: fixed — сценарий расхождения счётчика больше не воспроизводится. US-7 «Ошибка (не найдено)»: «клиент не удаляет запись локально — он запрашивает у сервера актуальный список закреплённых чата и показывает то, что вернул сервер»; US-8 — та же формулировка. Раздел 4 «Удалённые объекты» повторяет правило и явно фиксирует, что `pinned_count` не расходится. Раздел 5.1 и строка 5.4 «Переход не найден» согласованы. Локального вычёркивания записи общего состояния в ТЗ не осталось нигде.

### R-25 · challenger · раздел 2 · blocker · fixed
Право откреплять в групповом чате отдано исключительно администратору («в групповом чате (включая "Главный чат") закрепляет и открепляет только администратор чата»), и никакого запасного пути в ТЗ нет: автор своё сообщение открепить не может, участник — не может, срока жизни у закрепления нет, «открепить самое старое» нет. Сценарий слома, который наступит без всякой экзотики: в групповом чате 50 закреплений (лимит достигнут, раздел 4), единственный администратор уволился / вышел из группы / лишён роли. Дальше фича в этом чате мертва навсегда — закрепить нельзя (пункт disabled по лимиту), открепить некому, состав закреплённых заморожен, и единственный способ освободить слот — удалить сообщение «у всех» (US-11), то есть уничтожить контент ради освобождения слота. Допущение «в каждом групповом чате всегда есть живой администратор» нигде не проверено и не оговорено; для «Главного чата» техлид отдельно спрашивает, существует ли там роль администратора вообще (R-8) — я дополняю его вопрос последствием: если ответ «нет», то раздел 5.3 всё равно встраивает `screen:pinned-header` в `screen:glavnyy-chat`, а раздел 1 считает по групповым чатам KPI, который в этом чате недостижим.
**Ответ:** решение человека: права только у администратора, запасного пути нет — при отсутствии администратора любой участник может назначить себя администратором (существующее поведение приложения); раздел 2, раздел 4 «Права». po, analyst
**Перепроверка:** challenger: fixed — тупик «50 закреплений, администратора нет» закрыт: раздел 2 и раздел 4 («Права») называют путь — участник назначает себя администратором чата, это существующее поведение приложения. Допущение заменено на явно названную опору. R-8 закрыт там же. Решение человека — запасного пути «автор открепляет своё» нет — записано с причиной; риск принят. Но сам факт, на который оно опирается, проверке не подвергнут и вопросом никому не адресован — см. R-38 (C-16).

### R-26 · challenger · раздел 1 · major · fixed
Метрика успеха противоречит собственному дизайну фичи. Раздел 5.3 намеренно кладёт в свёрнутую строку превью последнего закреплённого («автор + текст, обрезка») — то есть самый частый успешный исход задуман так, что пользователь **прочитал закреплённое прямо в шапке и никуда не пошёл**. При этом целевой показатель — `pinned_jump / pinned_panel_open ≥ 0,5`, и оба события фиксируют только тех, кто открыл панель и кликнул. Успех, ради которого фича сделана, не порождает ни одного события; отношение считается по остаточному трафику. Хуже: если превью в шапке работает хорошо, панель открывают реже и от случая к случаю (промах, любопытство), и знаменатель вырождается в шум — метрика пойдёт вниз именно тогда, когда фича работает лучше всего. Второе: ни одной контрольной метрики вреда нет. Строка «Закреплённые» отъедает вертикаль в каждом чате, где есть хотя бы одно закрепление, у всех участников и навсегда (открепить может только админ, C-3); ни одного события, которое поймало бы «панель показана миллион раз и проигнорирована», ни счётчика закреплений на чат в динамике, ни отказа/сворачивания — свернуть строку пользователь, кстати, тоже не может, такого состояния в 5.3 нет. Это не R-13 (там источник данных) и не R-22 (там определение категории чата): здесь метрика измеряет не то, что фича делает.
**Ответ:** раздел 1 переписан по решению человека: охват (сервер), польза (`pinned_jump` на активного участника в неделю; новое событие `pinned_header_shown` как знаменатель показов), вред (доля закреплений старше 30 дней без единого перехода). po
**Перепроверка:** challenger: fixed по предмету замечания — раздел 1 переписан целиком: охват считается на сервере, заведено событие `pinned_header_shown` как знаменатель показов, появилась третья метрика — вред. Прежнего отношения `pinned_jump / pinned_panel_open` с целевым значением нет; ТЗ прямо признаёт, что чтение превью в шапке события не даёт. Метрика перестала мерить не то. Но новая вред-метрика не считается по тем событиям, которые заводит эта же фича, — см. R-37 (C-15).

### R-27 · challenger · раздел 2 · major · fixed
Прямое противоречие внутри ТЗ по «Заметкам». Раздел 2 включает их в скоуп: «Закрепление в "Заметках" (персональный чат с собой) отдельно не проектируется: работает по правилам личного чата» — то есть пункт «Закрепить» в меню там есть и работает. Раздел 5.3 перечисляет места встраивания свёрнутой строки исчерпывающе: «screen:lichnyy-chat, screen:gruppovoy-chat, screen:glavnyy-chat», `screen:zametki` в списке нет; в карте `zametki` несёт `features: []`. Итог: в «Заметках» сообщение закрепить можно, а увидеть список закреплённых — нельзя ни одним путём, потому что единственный вход в `screen:pinned-list` — клик по `screen:pinned-header` (раздел 5.1, «Точка входа 2»). Закрепления там уходят в никуда и при этом расходуют лимит 50. Формулировка «отдельно не проектируется» подменила решение: она звучит как «покрыто», а по факту раздел 5 это место не покрывает. R-16 задаёт смежный вопрос разработчику («сколько в коде контейнеров шапки чата»), но там это неопределённость реализации; здесь — противоречие двух разделов ТЗ по названному экрану, и решает его не разработчик.
**Ответ:** решение человека: «Заметки» в скоупе — раздел 2; US-1/US-5/US-6 (screen:zametki); 5.1/5.3/5.4 и карта (`zametki` → FEAT-1, переходы). po, analyst, ux
**Перепроверка:** challenger: fixed — противоречие снято в обе стороны. Раздел 2 включает `screen:zametki` в перечень носителей строки; раздел 3 даёт «Заметкам» `chat_type = notes`; US-1, US-5, US-6, 5.1, 5.3, 5.4 включают их наравне с остальными. В карте `zametki` несёт `features: [FEAT-1]` и переход `→ pinned-header`, у `pinned-list` есть переходы `→ zametki`. Закрепления в «Заметках» больше не уходят в никуда.

### R-28 · challenger · раздел 3 · major · fixed
Обоснование окна подтверждения опровергается соседней историей. US-9 существует ради «чтобы не открепить важное сообщение по ошибке» — значит, авторы считают риск случайного открепления реальным. US-4 то же самое действие из контекстного меню выполняет «сразу, без окна подтверждения». Причём именно в меню риск промаха выше: по разделу 5.2 пункт «Открепить» стоит между «Напомнить» и «Скрыть у меня», то есть вплотную над «Скрыть у меня» и через одну строку от «Удалить у всех» — промах на строку в списке из шести пунктов даёт открепление, и оно необратимо одним движением (повторно закрепить можно, но `pinned_at` обновится, и запись уедет наверх списка, изменив порядок для всех — раздел 2, «порядок по времени закрепления»). Одно из двух неверно: либо риск есть и его надо закрывать в обоих входах, либо риска нет и окно в US-9 — лишнее трение и лишний экран. ТЗ утверждает оба варианта одновременно, в двух соседних историях, без единого слова о том, почему входы разные. Это не R-9 (там оптимистичное против пессимистичного обновления) — здесь расходится сама логика защиты от ошибки.
**Ответ:** решение человека: подтверждение в обоих входах — раздел 2; US-4 переписана (открепление из меню через screen:pinned-unpin-confirm); 5.2/5.5 и карта (`deystviya` → `pinned-unpin-confirm`). po, analyst, ux
**Перепроверка:** challenger: fixed — логика защиты одинакова в обоих входах. Раздел 2: «Открепление — всегда через отдельное окно подтверждения… из обоих входов». US-4 переписана, 5.2 «Закреплено» — клик открывает окно, 5.5 описывает второй вход, `deystviya.html` несёт `aria-haspopup="dialog"`. В карте у `deystviya` переход `→ pinned-unpin-confirm`. Обратные переходы окна в ленту/тред в карту не доехали — R-40 (C-18).

### R-29 · challenger · раздел 5 · major · fixed
Раздел 5.4 в таблице состояний пишет прямо: «у каждого элемента **(кроме заглушки)** — крестик "открепить"». Макет `screens/pinned-list.html` в состоянии «заполнено, есть право откреплять» даёт заглушке крестик — элемент `<div class="c-11312b7e" role="listitem" aria-disabled="true">` с текстом «сообщение скрыто» содержит `<button class="pinned-item__unpin icon-button-round" … aria-label="Открепить сообщение">` наравне с остальными строками. ТЗ и макет расходятся в противоположные стороны, и оба варианта имеют цену: без крестика пользователь, скрывший у себя закреплённое сообщение, не может убрать бесполезную строку (а слот лимита она занимает — US-12, «скрытие "у меня" не освобождает слот»); с крестиком открывается `screen:pinned-unpin-confirm`, который по разделу 5.5 обязан показать «превью сообщения (автор, время, текст)», — а для скрытого сообщения ни автора, ни текста у клиента нет, и состояния «превью недоступно» в таблице 5.5 не существует. То есть вариант из макета ведёт в неопределённое состояние второго экрана. Это не R-18 и не R-19: те про контракт `pipeline_class` и про пропущенный `data-new-component`, здесь расходится поведение.
**Ответ:** решение человека: у заглушки крестик есть при праве; окно подтверждения получает состояние «превью недоступно» с заглушкой — раздел 2; 5.4/5.5 и `pinned-unpin-confirm.html`. po, ux
**Перепроверка:** challenger: fixed — расхождение ТЗ и макета снято в пользу макета, второй экран получил недостающее состояние: 5.4 «включая заглушку — крестик (при праве)», 5.5 строка «Превью недоступно», `pinned-unpin-confirm.html` содержит этот блок, US-12 повторяет. Неопределённого состояния второго экрана больше нет.

### R-30 · challenger · раздел 4 · major · fixed
`PinnedMessage` хранит `pinned_by`, но ни один экран его не показывает: в разделе 5.4 состав строки — «аватар, имя, время закрепления, текст», где имя и аватар принадлежат автору сообщения, а не тому, кто закрепил; в 5.3 превью — автор сообщения. Системное сообщение «X закрепил сообщение» раздел 2 выносит из скоупа явно. Складывается: в групповом чате закрепления меняет только администратор, видят все, и **никто, кроме сервера, не знает, кто и когда это сделал**. Сценарий: в «Главном чате» (по карте — чат уровня организации) администратор закрепляет чужое сообщение — оно попадает в шапку чата всей организации с превью автора и текстом; автор об этом не уведомлён (уведомления вне скоупа), открепить своё сообщение не может (права — только у администратора), пожаловаться внутри продукта некуда, и единственный доступный ему в продукте способ убрать сообщение из шапки — удалить его «у всех» (US-11). Симметрично: администратор молча снимает чужое важное закрепление, и участники не могут даже установить, что оно было. Поле собирается и не используется — это признак того, что вопрос подотчётности заметили и не решили. Раздел 2 обосновывает исключение системного сообщения объёмом («расширяет скоуп и модель уведомлений»), но не проверяет, остаётся ли фича без него безопасной.
**Ответ:** решение человека: у каждой записи списка подпись «закрепил <имя>, <когда>» (`pinned_by`, `pinned_at`) — раздел 2; US-6; раздел 4; 5.4 и `pinned-list.html`. Системное сообщение остаётся вне скоупа. po, analyst, ux
**Перепроверка:** challenger: fixed — `pinned_by` перестал быть собираемым и неиспользуемым полем: раздел 2, US-6, раздел 4 и 5.4 требуют подпись «закрепил <имя>, <когда>», она стоит в `pinned-list.html` у всех записей, включая заглушку. Системное сообщение и уведомление автору остаются вне скоупа — решение записано с причиной; риск принят.

### R-31 · challenger · раздел 3 · major · fixed
Закрытие панели не описано нигде. В макете `screens/pinned-list.html` крестик `aria-label="Закрыть"` присутствует во всех четырёх состояниях (загрузка, ошибка, заполнено с правом, заполнено без права) — то есть действие спроектировано. При этом: в US-6 три AC (успех, ошибка, граница) и ни одного про закрытие; в таблице 5.4 колонка «Действия» перечисляет клик по сообщению, по крестику открепления и по заглушке — закрытия панели в ней нет; в карте у `pinned-list` пять `transitions`, и возврат в чат описан только как «клик по обычному сообщению», перехода «панель закрыта без перехода» нет. Отсюда не определено: возвращается ли фокус на строку в шапке, меняется ли `aria-expanded` (в макете шапки жёстко `aria-expanded="false"`), закрывается ли панель кликом вне её и по Esc (R-17 спрашивает про Esc в стеке двух слоёв — я дополняю: у самого частого выхода из панели вообще нет ни истории, ни AC, ни перехода в карте), и есть ли парное событие к `pinned_panel_open`, без которого длительность просмотра панели не считается.
**Ответ:** US-6 AC «Закрытие панели» (крестик, клик вне, Esc; фокус на строку в шапке; событие не отправляется — парное событие закрытия не вводим, решение PO); 5.4 и карта (переходы «панель закрыта» → чат). analyst, ux
**Перепроверка:** challenger: fixed — закрытие панели описано во всех трёх местах: US-6 AC «Закрытие панели», 5.1 абзац с переключением `aria-expanded` и порядком закрытия слоёв по Esc, 5.4 — абзац и переход. В карте у `pinned-list` четыре перехода «панель закрыта…» на все четыре чата-носителя.

### R-32 · challenger · раздел 2 · major · fixed
Порядок и лимит выбраны так, что на главном сценарии фича работает против собственной цели. Цель раздела 1 — «быстрый доступ к важным сообщениям». Порядок в списке — «по времени закрепления, последнее закреплённое сверху» (раздел 2), превью в шапке — «сообщение с самым поздним `pinned_at`» (раздел 5.3). Архетипное закреплённое сообщение — правила чата, ссылка на встречу, объявление: его закрепляют первым и держат долго. При наполнении списка оно уезжает вниз, а в шапке его вытесняет последнее по времени. То есть чем дольше живёт чат, тем надёжнее самое важное закрепление уходит из зоны быстрого доступа. Поверх этого раздел 2 отклоняет поиск, фильтры и ручной порядок с обоснованием «не нужны при лимите 50» — это допущение, а не вывод: 50 записей по «аватар, имя, время, текст с обрезкой» в панели с прокруткой (5.4) — это лента, которую надо просматривать глазами, и ровно та прокрутка истории, от которой фича обещала избавить. Ни ручного порядка, ни закрепления «наверху», ни группировки в ТЗ нет; при этом само число 50 нигде не обосновано — ни аналогом, ни данными, ни оценкой.
**Ответ:** решение человека: порядок «первое закреплённое сверху» (`pinned_at` возр.), превью в шапке — верхняя запись; лимит 50 сохранён как предохранитель — раздел 2; US-5/US-6; раздел 4; 5.3/5.4 и HTML. po, analyst, ux
**Перепроверка:** challenger: fixed — порядок перевёрнут: раздел 2, раздел 4, US-5, US-6, 5.3, 5.4 — первое закреплённое сверху, превью = верхняя запись; HTML выложены по возрастанию `pinned_at`. Число 50 обосновано словами человека; поиск и ручной порядок остаются отклонёнными, риск принят. Побочный эффект перестановки — US-1 остался в старой логике: R-36 (C-14).

### R-33 · challenger · раздел 5 · minor · fixed
В самой ленте нет никакого признака, что сообщение закреплено. Раздел 4 вводит производное `is_pinned` и тут же ограничивает его назначение — «используется для переключения пункта меню "Закрепить" ⇄ "Открепить"»; макет `screens/pinned-header.html` помечает ленту как «Лента сообщений (не меняется)»; в не-скоупе раздела 2 отсутствие маркера не оговорено, то есть это не отклонённое решение, а пропущенное. Следствия: пользователь, читая переписку, не может отличить закреплённое сообщение от обычного; после перехода из списка (US-7) подсветка временная, и как только она гаснет, контекст «я пришёл сюда потому что оно закреплено» теряется; администратор не видит, какие из 50 слотов заняты, глядя в ленту, — только открыв панель.
**Ответ:** решение PO: маркера закреплённости в ленте нет — вынесено в не-скоуп раздела 2 явно, с причиной; раздел 4 у `is_pinned` — «маркер в ленте не показывается». po, analyst
**Перепроверка:** challenger: fixed — пропуск превращён в решение: раздел 2 «Не делаем» несёт отдельный пункт про маркер в ленте с причиной, раздел 4 у `is_pinned` — «маркер в ленте не показывается». Риск принят явно.

### R-34 · challenger · раздел 5 · minor · fixed
Что за время показано в записи — определено по-разному в двух соседних экранах. Раздел 5.4: «аватар, имя, **время закрепления**, текст», и макет `pinned-list.html` пишет «закреплено сегодня в 12:10» — время `pinned_at`. Раздел 5.5: «превью сообщения (автор, **время**, текст)», без уточнения, а макет `pinned-unpin-confirm.html` показывает «Сегодня в 12:59» без слова «закреплено» — по форме это время отправки сообщения, взятое из образца `screen:udalit`. Итог: в списке и в окне подтверждения одна и та же запись покажет два разных времени под визуально одинаковой подписью, и по ТЗ нельзя сказать, ошибка это или намерение.
**Ответ:** время у записи списка и в окне подтверждения — время отправки сообщения; время закрепления — только в подписи «закрепил <имя>, <когда>» — раздел 2; US-6/US-9; 5.4/5.5 и HTML. analyst, ux
**Перепроверка:** challenger: fixed — одно правило вместо двух: US-9 «Примечание о времени», US-6, 5.4, 5.5 — время отправки; `pinned_at` только в подписи «закрепил…». В `pinned-list.html` два времени с разными подписями, в `pinned-unpin-confirm.html` — время отправки.

### R-35 · challenger · раздел 5 · minor · fixed
Таблица состояний 5.2 противоречит сама себе в двух строках. Строка «Лимит 50 достигнут (сообщение не закреплено)» — «Пункт "Закрепить" disabled»; строка «Личный чат» — «Пункт виден и **активен для обоих участников всегда**». В личном чате при 50 закреплениях обе строки применимы одновременно и требуют противоположного. Та же формулировка продублирована в подписи макета `screens/deystviya.html` («В личном чате право есть у обоих участников — там пункт виден всегда»), то есть ошибка растиражирована, а не единична. Слово «всегда» в строке про личный чат читается как «лимит на личные чаты не распространяется» — чего раздел 4 не утверждает («50 закреплений на чат», без разделения по типу).
**Ответ:** «всегда» убрано; лимит 50 действует во всех типах чата, включая личный и «Заметки» — раздел 2; раздел 3 (шапка), раздел 4 «Лимит»; 5.2 и подпись в `deystviya.html`. analyst, ux
**Перепроверка:** challenger: fixed — «всегда» убрано. 5.2 строка «Личный чат и «Заметки»»: лимит 50 действует и здесь; то же в разделе 2, преамбуле раздела 3, разделе 4 «Лимит» и подписи `deystviya.html`.

### R-36 · challenger · раздел 3 · major · fixed
(C-14, раздел 3 и раздел 7.) Правка порядка (R-32) не доехала до US-1. AC «Успех»: «раздел "Закреплённые" в шапке появляется/обновляется превью на это сообщение». При порядке «первое закреплённое сверху» и превью = верхняя запись новое закрепление становится последней записью и превью не меняет — кроме единственного случая, когда оно первое в чате. То есть AC истинна ровно в одном граничном случае и ложна во всех остальных, а рядом US-5 «Обновление в реальном времени» утверждает прямо обратное («превью остаётся на А — Б добавляется в конец списка»). Это тестируемое требование: QA по US-1 напишет проверку «закрепил — превью показывает моё сообщение», и она будет падать на любом чате, где уже есть закрепление. Второе место с тем же следом — раздел 7, «Ограничения реализации»: «новые поля в выдаче чата (`pinned_count` и превью **последнего** закреплённого)». Это контракт к серверу, и он противоречит разделу 4, где чат несёт верхнюю запись списка (первое закреплённое). Оговорка «блоки перенесены из ревью техлида дословно» прикрывает статус вопросов, но не расхождение в требовании к API: backend-разработчик читает раздел 7 как ТЗ на поле.
**Ответ:** US-1 «Успех»: раздел в шапке появляется, если это первое закрепление чата (превью — это сообщение); иначе счётчик +1, превью остаётся на верхней записи (US-5); US-2/US-3 проверены, той же ошибки нет. Раздел 7 «Ограничения реализации»: поле выдачи чата — верхняя запись списка (первое закреплённое), со ссылкой на контракт в разделе 4. analyst, po
**Перепроверка:** challenger: fixed — правка порядка доехала до обоих мест. US-1 «Успех» разветвлён: первое закрепление чата — раздел появляется, превью — это сообщение; иначе счётчик +1, превью остаётся на верхней записи (US-5). US-2/US-3 превью не утверждают. Раздел 7 «Ограничения реализации»: поле выдачи чата — верхняя запись списка, контракт — раздел 4. Слов «превью последнего закреплённого» в `spec.md` и `screens/` больше нет.

### R-37 · challenger · раздел 1 · major · fixed
(C-15.) Вред-метрика, единственный контроль вреда, не считается ни по одному источнику, который заводит эта фича. Определение: «доля закреплений старше 30 дней, к которым не было ни одного `pinned_jump`». Событие `pinned_jump` несёт только `in_thread` и `result` — ни `chat_id`, ни `message_id`, ни идентификатора записи `PinnedMessage`. Соединить серверную таблицу закреплений с клиентским событием нечем: «к которым не было перехода» вычислить невозможно, максимум считается общее число переходов. Второе, независимое: срок снятия задан как «три числа, через 30 дней после релиза desktop», а знаменатель метрики — закрепления старше 30 дней; на 30-й день после релиза таких закреплений почти нет (только сделанные в первые часы), и доля считается по околонулевой выборке. Метрика, объявленная порогом «≤ 50 %», в момент своего же замера читается по единицам записей или не читается вовсе. Обе метрики пользы (`pinned_jump` на активного участника) от первого дефекта страдают меньше — их можно свести на уровне пользователя, — но вред без ключа связи не сводится никак.
**Ответ:** решение человека: все события несут `chat_id` и `message_id` (`pinned_header_shown`, `pinned_panel_open` — `chat_id`), внутренние идентификаторы; вред-метрика связывается по ним и снимается через 60 дней после релиза, охват и польза — через 30 — раздел 1; преамбула раздела 3 отсылает поля событий к разделу 1. po, analyst
**Перепроверка:** challenger: fixed — оба дефекта закрыты, но с остатком (R-43). Раздел 1: каждое событие несёт `chat_id` и `message_id`; вред-метрика — связь по ним; срок замера разведён (охват и польза — 30 дней, вред — 60), околонулевая выборка ушла. Преамбула раздела 3 отсылает поля событий к разделу 1.

### R-38 · challenger · раздел 2 · major · fixed
(C-16, раздел 2 / раздел 4.) Живучесть фичи в групповом чате целиком держится на одном утверждении о продукте, которое нигде не проверено и никому не адресовано вопросом: «если в групповом чате не осталось администратора, любой участник может назначить себя администратором — это существующее поведение приложения» (раздел 4, «Права»). Все остальные допущения о том, что продукт умеет (событие уровня чата, каскад при удалении, окно истории вокруг сообщения, коды ошибок, роль в объекте чата), техлид вынес в раздел 7 открытыми вопросами 1–5, 7, 9, 10 с адресатом — а это, единственное, записано как факт. При этом цена ошибки выше, чем у остальных: если самоназначения в продукте нет (или оно есть в групповом чате, но не в «Главном чате» уровня организации, который раздел 2 приравнял к обычному групповому), то чат с 50 закреплениями и без администратора замораживается навсегда — ровно тот сценарий, ради закрытия которого ответ и был дан. Разработчик, читающий раздел 7 первым (как велит сам раздел 7 и раздел 8), этого допущения там не найдёт. Обратная сторона того же утверждения тоже не разобрана: если самоназначение доступно любому участнику, право «закрепляет только администратор» в чате без администратора не ограничивает никого.
**Ответ:** решение человека: самоназначение администратора — подтверждённый владельцем продукта факт; записано в разделе 2 и разделе 4 «Права» с пометкой; разработчику — проверить действие в «Главном чате» (раздел 7, вопрос 15, backend); обратная сторона (в чате без администратора ограничение фактически снимается) названа в разделе 4 как принятое поведение продукта. po, analyst Дополнение po 2026-09-03: маршрут `apiv1:groups.trySelfAssignAdminRole` найден в карте API стенда 6.7.9 (раздел 7, «Сверка с картой маршрутов стенда») — факт подтверждён кодом, не только словами человека.
**Перепроверка:** challenger: fixed — допущение перестало быть незаявленным фактом: раздел 2 и раздел 4 «Права» несут «подтверждено владельцем продукта (R-38)», обратную сторону (в чате без администратора ограничение фактически снимается — принятое поведение продукта) и указание проверить в «Главном чате»; раздел 7 — вопрос 15 (backend); раздел 8, п. 4. Проверка факта в продукте не проводилась — риск принят решением человека, записан с причиной.

### R-39 · challenger · раздел 5 · minor · fixed
(C-17, 5.5 против 5.2 и `deystviya.html`.) Возврат фокуса из окна подтверждения описан для входа, которого к этому моменту не существует. 5.5: «после закрытия фокус возвращается на элемент, которым окно было вызвано — крестик записи списка или **пункт "Открепить" контекстного меню**». Но 5.2 и подпись в `screens/deystviya.html` говорят, что при клике меню закрывается, а окно открывается поверх ленты или треда: пункта меню в DOM больше нет, возвращать фокус некуда, и ТЗ не говорит, куда он уходит вместо этого (на сообщение в ленте? на кнопку «три точки»?). Тем же расхождением задета US-4: её ветки «Отмена» и «Ошибка» описывают состояние закрытого меню («пункт меню остаётся "Открепить"») как видимое пользователю.
**Ответ:** 5.5 и 5.2: окно, открытое из пункта меню, после закрытия возвращает фокус на кнопку «три точки» сообщения (в ленте или в треде) — меню к этому моменту закрыто; `deystviya.html` подпись; 5.1 скобка исправлена тем же образом. Ветки US-4 «Отмена»/«Ошибка» — analyst: состояние пункта описывается как «при следующем открытии меню». ux, analyst
**Перепроверка:** challenger: fixed — фокус описан для существующего элемента в четырёх местах: 5.5, 5.2 («Закреплено»), 5.1 (скобка), `deystviya.html`; US-4 «Отмена»/«Ошибка» — «при следующем открытии меню пункт по-прежнему «Открепить»», в «Отмене» явный возврат фокуса на «три точки».

### R-40 · challenger · раздел 5 · minor · fixed
(C-18, карта `spec/app-map/screens.yaml`.) У `pinned-unpin-confirm` один переход — `to: pinned-list`. Второй вход, добавленный по R-28, обратного пути в карте не имеет: 5.5 требует «возврат в исходный экран… исходная лента/тред, если открыто из контекстного меню», а переходов `→ lichnyy-chat / gruppovoy-chat / glavnyy-chat / zametki / otkrylsya-tred` у окна нет. Валидатор этого не ловит (он проверяет существование целей, а не полноту), и `navigation.mmd` покажет окно как тупик, из которого выход только в панель списка — то есть карта утверждает, что открепление из меню сообщения в ленте выбрасывает пользователя в список закреплённых.
**Ответ:** карта: у `pinned-unpin-confirm` добавлены переходы возврата → `lichnyy-chat` / `gruppovoy-chat` / `glavnyy-chat` / `zametki` / `otkrylsya-tred` («подтверждено или отменено — возврат в ленту/тред, откуда открыто контекстное меню»), переход → `pinned-list` уточнён «если открыто из списка»; `navigation.mmd` перегенерирован. ux
**Перепроверка:** challenger: fixed — `pinned-unpin-confirm` в карте несёт шесть переходов: `pinned-list` («если открыто из списка») плюс возвраты в четыре чата и тред; `navigation.mmd` перегенерирован, тупика нет; валидатор `spec/ valid`.

### R-41 · challenger · раздел 5 · minor · fixed
(C-19, раздел 5.4 / `pinned-list.html`.) 5.4 в строке «Заполнено, нет права откреплять» утверждает поведение, которого не показывает ни один макет: «крестик отсутствует у всех элементов, **включая заглушку**». В состоянии «нет права» в `pinned-list.html` две обычные записи и ни одной заглушки; заглушка с крестиком показана только в состоянии «есть право». Комбинация «заглушка + нет права» — единственная, где новое решение по R-29 (крестик у заглушки) обязано выключаться, и именно она визуально не зафиксирована.
**Ответ:** `pinned-list.html`, состояние «нет права откреплять»: добавлена запись-заглушка «сообщение скрыто» без крестика; текст 5.4 без изменений. ux
**Перепроверка:** challenger: fixed — `pinned-list.html`, блок «нет права» получил запись-заглушку без крестика с явным комментарием; счётчик согласован; текст 5.4 подтверждён макетом.

### R-42 · challenger · раздел 5 · minor · fixed
(C-20, `screens/pinned-list.html`, состояние «нет права».) Правка по R-41 вставила заглушку в список с нарушением порядка, который этот же файл объявляет своей преамбулой. Интро макета: «порядок — по `pinned_at` возр. (первое закреплённое — сверху, раздел 2)». В блоке «нет права» записи идут с `pinned_at` **11:59 → 08:10 → 12:12**: заглушка с 08:10 самая ранняя и обязана быть первой, а стоит второй. Соседний блок «есть право» порядок держит (вчера 18:05 → 08:10 → 11:59 → 12:06 → 12:12), то есть внутри одного файла два состояния демонстрируют взаимоисключающие правила сортировки. Порядок — предмет отдельного решения человека (R-32) и единственная опора превью в шапке; макет, который его нарушает, — ровно тот артефакт, по которому разработчик и QA сверяют поведение. Валидатор порядок не проверяет. Смежное, но не новое (было до этих правок, отмечаю для полноты): блок «Дополнительно: состояния записи при переходе» в том же файле тоже идёт вне порядка (3 месяца назад → 2 месяца назад → полгода назад). Его подпись выводит блок из общего счётчика, но не из правила сортировки.
**Ответ:** `pinned-list.html`: в блоке «нет права» заглушка (08:10) переставлена первой — 08:10 → 11:59 → 12:12; блок «состояния записи при переходе» переставлен по возрастанию `pinned_at` (полгода → 3 месяца → 2 месяца); содержимое и счётчики без изменений. ux
**Перепроверка:** challenger: fixed — порядок восстановлен во всех блоках `pinned-list.html`: «нет права» 08:10 → 11:59 → 12:12, «состояния записи при переходе» полгода → 3 месяца → 2 месяца; «есть право» не тронут; счётчики и содержимое без изменений; комбинация «верхняя запись — заглушка» уже описана в 5.3 и `pinned-header.html`, неописанного состояния макет не создал.

### R-43 · challenger · раздел 1 · minor · fixed
(C-21, раздел 1 против раздела 4.) Ключ связи вред-метрики, заведённый по R-37, по собственному контракту ТЗ неполон для комментариев тредов. Раздел 4 задаёт идентичность закрепления ключом (`chat_id`, `message_id`, опц. `thread_id`) — и «Идемпотентность и повтор», и вопрос 4 раздела 7 повторяют его с `thread_id`. Раздел 1 связывает таблицу закреплений с телеметрией по `chat_id` + `message_id`; `thread_id` в событиях нет ни у одного, у `pinned_jump` вместо него булев `in_thread`. Что означает `message_id` у события про комментарий треда — id самого комментария или id родительского сообщения, через которое US-8 выполняет переход, — не сказано нигде. Если родителя, вред-метрика по подмножеству закреплённых комментариев не сводится вовсе; если комментария, то `thread_id` в ключе раздела 4 избыточен, и два раздела расходятся в том, чем закрепление идентифицируется. Дефект узкий (задета только тредовая часть выборки), но он тот же по природе, что и исходный C-15, и порождён его закрытием.
**Ответ:** раздел 1: `message_id` в событиях — id закреплённого объекта (для комментария треда — id самого комментария), плюс поле `thread_id` (null для сообщений ленты) — тот же ключ, что в разделе 4; `in_thread` остаётся как удобный флаг, производный от `thread_id`. po
**Перепроверка:** challenger: fixed — раздел 1 и раздел 4 больше не расходятся: каждое событие несёт `chat_id`, `message_id`, `thread_id`; `message_id` — id закреплённого объекта (для комментария — сам комментарий), `thread_id` — id треда, null для ленты; `in_thread` — производный флаг; метрика «Вред» связывает по той же тройке, что «Сущности и поля», «Идемпотентность и повтор» и вопрос 4 раздела 7. Словесный остаток («опц. `thread_id`» в разделе 4) смысла не меняет. Остаток исходного R-37, вынесенный отдельно: ключ не различает повторное закрепление того же сообщения — закрыто PO в разделе 1 (единица измерения — активная запись `PinnedMessage`, переходы засчитываются ей после её `pinned_at`).

### R-44 · qa · раздел 5 · major · fixed
Порядок записей «первое закреплённое сверху» (раздел 2; преамбула 5.4: «порядок по `pinned_at` возр.») снова нарушен в одном блоке `screens/pinned-list.html` — том самом, что уже был предметом R-42. Состояние `data-panel-state="perehod"` («состояния перехода», строки ~2996–3040) показывает три записи в порядке «3 месяца назад в 09:41» → «2 месяца назад в 14:22» → «полгода назад в 10:01» (подписи «закрепил Антон Чекушкин, …»). По возрастанию `pinned_at` первой должна идти самая старая запись — «полгода назад», затем «3 месяца назад», затем «2 месяца назад» (ровно так, как зафиксировано в перепроверке R-42: «блок "состояния записи при переходе" переставлен по возрастанию `pinned_at` (полгода → 3 месяца → 2 месяца)»). Сейчас порядок обратный для первой и последней записи блока. Видно и на скриншоте `renders/pinned-list--perehod.png` (карточки идут «3 месяца» / «2 месяца» / «полгода» сверху вниз). Блоки «есть право» и «нет права» того же файла порядок держат правильно (проверено отдельно) — разъехался только этот блок при пересборке макета поверх `result.html` пайплайна.
**Ответ:** `pinned-list.html`, состояние `perehod`: записи переставлены по возрастанию `pinned_at` (полгода → 3 месяца → 2 месяца); `renders/pinned-list--perehod.png` переснят. ux
**Перепроверка:** qa: fixed — `screens/pinned-list.html:3013-3060`, блок `data-panel-state="perehod"` идёт «Фёдор Чернов, полгода назад в 10:01» → «Виктория Лапина, 3 месяца назад в 09:41» → «Станислав Бондарчук, 2 месяца назад в 14:22» — по возрастанию `pinned_at`, как требовалось. `renders/pinned-list--perehod.png` (проверен построчно сверху вниз) несёт тот же порядок: Фёдор Чернов / Виктория Лапина / Станислав Бондарчук. Блоки «есть право» и «нет права» не трогал — вне предмета R-44.

### R-45 · qa · раздел 5 · major · fixed
Раздел 5.6 утверждает про текущую ревизию макетов: «После пересборки раздела 5 на `result.html` пайплайна эти id указаны CSS-комментарием рядом с соответствующим правилом в `screens/*.html`» — и перечисляет 21 `pipeline_class` (`popover` c-07391c1d, `type-list-item-default`/`-hover` c-11312b7e/c-edec4e26, `divider` c-f13e3535, `type-list-state-default` c-02eee945, `size-tiny-type-personal-state-default` c-11b55828, `type-counter` c-5b5b0967, `type-expand` c-a0c20e89, `icon-outlined-20px-close` c-7b0b44df, `icon-outlined-20px-anglesmall` c-302e0086, `type-message` c-d4c5a3f5, `type-comment` c-c8bae238, `size-medium-type-stub-state-default` c-31d0e0c0, `type-default-header` c-a3702a78, `title` c-70c7d6ee, `size-big` c-98b646ed, `tertiary-button` c-58ef8e45, `size-medium-state-default/disabled-icon-right-color-sapphire` c-508d4f03/c-139ed356, `icon-18px-spinner` c-6aba12b5, `icon-outlined-16px-circlewarning` c-2b7ddf88). Проверка (`grep` по всем 21 id в трёх пересобранных файлах — `pinned-header.html`, `pinned-list.html`, `pinned-unpin-confirm.html`): найден только один — `c-07391c1d` (комментарий «Popover c-07391c1d — обёртка панели», `pinned-list.html:2857`). Остальные 20 id раздела 5.6 не встречаются в этих трёх файлах ни в комментарии, ни в имени класса. Утверждение раздела 5.6 не подтверждается макетами: desktop-разработчик, который по тексту 5.6 рассчитывает найти в `screens/*.html` CSS-комментарий с id рядом с каждым правилом (чтобы свериться с `components.yaml`), не найдёт его почти нигде — контракт компонента для этой разметки прослеживается только в тексте спеки, не в макетах. `screens/deystviya.html` (файл, где раздел 5.6 делает явное исключение только для Tooltip) сам паттерн комментирования несёт — `/* Tooltip Arrow=Default c-659abea9 */` и `<!-- Type=List Item Default c-11312b7e -->` — то есть способ разметки известен и применялся, просто не был перенесён в три остальных пересобранных файла.
**Ответ:** все 21 `pipeline_class` из 5.6 отмечены комментариями `/* c-<hash> <id> */` у правил в трёх файлах (pinned-header 2, pinned-list 16, pinned-unpin-confirm 8); текст 5.6 перечисляет, какой id в каком файле. ux
**Перепроверка:** qa: fixed — `divider` (`c-f13e3535`) убран из перечня для `pinned-list.html` в 5.6; текущий текст перечисляет для этого файла 15 id (`popover`, `type-list-item-default`/`-hover`, `type-list-state-default`, `size-tiny-type-personal-state-default`, `type-counter`, `type-expand`, `icon-outlined-20px-close`, `icon-outlined-20px-anglesmall`, `type-message`, `type-comment`, `size-medium-type-stub-state-default`, `type-default-header`, `title`, `icon-18px-spinner`, `icon-outlined-16px-circlewarning`) без `divider`, и все 15 подтверждены `grep -n 'c-' screens/pinned-list.html` — каждый есть комментарием у своего правила. `divider` остался в перечне `pinned-unpin-confirm.html` вместе с 7 другими id — все 8 подтверждены той же проверкой (`screens/pinned-unpin-confirm.html:2031` — `/* c-f13e3535 divider */`). Заодно перепроверены оставшиеся 2 id `pinned-header.html` — оба на месте. Итого все 21 id раздела 5.6 присутствуют в указанном для них файле. Отдельно, не блокирует и не входит в эту перепроверку: комментарий `popover` в `pinned-list.html:2873` (`<!-- Popover c-07391c1d — обёртка панели -->`) по-прежнему в обратном порядке токенов относительно формата 5.6 для инлайн-случая — уже отмечено раньше как некритичное несоответствие, ux не просили чинить в рамках этого R-45.

### R-46 · qa · раздел 5 · blocker · fixed
`python3 tools/validate_spec.py` красный на живом `spec/` — все четыре строки принадлежат папке этой фичи (`screens/*.html`), дословно:
```
/home/ilia_volkov/compass-hub/product/agents/spec/features/FEAT-1-pinned-messages/screens/deystviya.html: 2 color(s) not in tokens.yaml palette: #9fd3ff, rgba(20,20,30,0.92)
/home/ilia_volkov/compass-hub/product/agents/spec/features/FEAT-1-pinned-messages/screens/pinned-header.html: 2 color(s) not in tokens.yaml palette: #9fd3ff, rgba(20,20,30,0.92)
/home/ilia_volkov/compass-hub/product/agents/spec/features/FEAT-1-pinned-messages/screens/pinned-list.html: 4 color(s) not in tokens.yaml palette: #9fd3ff, rgba(0,0,0,0.12), rgba(20,20,30,0.92), rgba(37,116,169,0.08)
/home/ilia_volkov/compass-hub/product/agents/spec/features/FEAT-1-pinned-messages/screens/pinned-unpin-confirm.html: 3 color(s) not in tokens.yaml palette: #8fb6cc, #9fd3ff, rgba(20,20,30,0.92)
4 error(s)
```
Разбор по источнику: `#9fd3ff` и `rgba(20,20,30,.92)` — во всех четырёх файлах цвета `[data-spec-nav]`, панели-переключателя состояний, которую сам раздел 5.2/5.3/5.4/5.5 называет «пометкой спеки, не частью продукта»; это все 2 цвета у `deystviya.html` и `pinned-header.html`, и 2 из 4 / 2 из 3 у двух оставшихся. Плюс к этому — цвета уже самой продуктовой разметки, не помеченной как служебная: `pinned-list.html` — `rgba(0,0,0,.12)` (тень попапа, строка 1982) и `rgba(37,116,169,.08)` (фон бейджа «в треде», строка 2023); `pinned-unpin-confirm.html` — `#8FB6CC` (фон кнопки «Открепить» в состоянии «Отправка», строка 2060). Коммит `c1ac0e6` (только что, той же сессией) фиксирует это как известный и не смягчаемый факт («Валидатор на живом spec/ сейчас красный по FEAT-1: четыре экрана несут цвета вне палитры — это работа ux по ответу в inbox po, не смягчается») — запись здесь не новость для PO, но по протоколу (`Map Validation`) ошибки валидатора внутри папки этой фичи идут в `R-<n>` дословно независимо от того, знает ли о них PO.
**Ответ:** цвета вне палитры заменены значениями из `tokens.yaml`: панель `data-spec-nav` — `rgba(4,4,10,.9)` и белый текст с opacity; тень попапа `rgba(0,0,0,.15)`; фон бейджа «в треде» `rgba(0,159,230,.05)`; кнопка «Отправка» `#81BDE4`; скриншоты пересняты `tools/render_screen.py`; валидатор `spec/ valid`. ux
**Перепроверка:** qa: fixed — `python3 tools/validate_spec.py` из корня окружения (`/home/ilia_volkov/compass-hub/product/agents`) выводит `spec/ valid`, все четыре строки об экранах фичи ушли.
