# Cursor · bounded repair #394 **Предлагаемый handoff, не выполненная реализация.** Основание — новое Owner direction 02.11.2026 и RECOMMENDATION.md / ELIGIBILITY.json этой папки. Source snapshot по заданию: `0194902d3d57d5e35a381a61b71afcf421ff2ed6`. Работать в том же #394; сохранить PR465, расходы, прежние QA/FAIL/UNKNOWN и cancelled DEV receipt. Не merge PR73, не создавать новый реестр backlog, scheduler, service или framework. ## Сначала исключить случайный выпуск старого VENUE До следующей установки DEV, включая несвязанный release, trusted integrator должен включить в source маленький repair: - В `apps/api/src/comments/comments.service.ts` централизованно закрыть **все broad VENUE create/reply/update** до настоящего experience-gate. В `resolveCanCreate` вернуть false для legacy venue; одного скрытия composer недостаточно. Не объявлять старые rows проверенными отзывами. `create/update` и `authorizeReplay` обязаны проходить этот запрет. Не ломать author withdrawal для уже существующих данных, если они обнаружатся: не связывать это действие в UI с canCreate, использовать отдельное собственное право снятия; публичный вывод legacy venue rows также закрыть до разбора их provenance. - В `apps/web/src/components/consumer/venues/VenuePage.tsx` убрать приглашение «напишите комментарий» без игры. Честная локальная формулировка: «Отзывы после игр появятся здесь». Game Q&A не ставить за attendance-gate; отдельный небольшой slice возможен после своих обязательных проверок. - Поправить source `COMMENTS_CONTRACT.md` и handoff на новую семантику. Старые QA/CI не переименовывать в принятие новой политики. Не редактировать применённые миграции задним числом и не удалять полезные комментарные компоненты. Это немедленное ограждение — **не полное выполнение #394**. Ни наличие merge PR465, ни старое `canCreate`, ни ожидаемая alpha15 не дают разрешения установить отменённый кандидат. Установку, release number/receipt и merge выполняет trusted process, не автор. ## Реальные точки повторного использования | Путь / символ | Использовать | Что отсутствует / меняется | |---|---|---| | `apps/api/src/games/game-attendance.policy.ts: eligibleFromTransitions` | Историческая membership; withdrawal-before-start не возрождается | Не называть этот predicate проверкой присутствия | | `game-attendance.service.ts: projectRecord/dispute/requestDisputeHelp` | Своя отметка, exact versions, один dispute, help progress | UI canDispute требует known row; `dispute()` при отсутствии строки возвращает ATTENDANCE_MISSING. Нужен bounded missing/unknown request/intake, не fake present/version | | `apps/api/src/admin/attendance-disputes.{dto,service}.ts` | Assigned Cases, step-up, независимость, пять resolutions; insufficient evidence остаётся open | Help не assignment. Добавить missing-mark case route и ограниченный case outcome path; нельзя обещать это через старый `/help` | | `game-outcomes.policy.ts: assertLifecycle`, `game-outcomes.service.ts` | Серверный конец, различие held/not_held/unknown, запрет cancelled→held обычным manager | Outcome clarification/resolution не притворяется attendance correction; case-bound отдельный versioned repair | | `game-feedback.service.ts: ownEligibleParticipation/loadPeerSubjects/createReview` | Серверный recipient, same-game identity, UNIQUE, приватные receipts/replies/history | Сейчас `needsDiscrepancy` допускает own unknown/absent; peer target фильтруется по membership, не explicit present. Строгий gate для **нового peer create**, обе стороны; не просто убрать поле discrepancy у всех видов | | `game-feedback.policy.ts`, `GameFeedbackPanel.tsx` | Peer play_again/neutral/difficulties, ≤3 tags, ≤500, один reply; organization/training separate | Не вводить публичный route старого ReviewView: он содержит gameId/контекст. Сохранить старые private records/notes и их права; no automatic reclassification | | `comments.policy/service.ts` | Unicode text, version CAS, parent privacy, MutationRunner, projection и PairFence | Только game/venue; create body не содержит proof. Generic `parentCommentId` не является подходящим правом отвечать на review | | `CommentsSection.tsx`, `styles/comments.css` | Уже существующая форма/карточка, safe text, session fences, UNKNOWN, pagination, ProfileDialog | Форма сейчас inline в CommentsSection, отдельного exported Composer нет. Выделить локальный presentational composer только если нужно переиспользование. Не копировать всё дерево и не создавать глобальную библиотеку | | `apps/api/src/safety/safety.service.ts` | Настоящие block/report игрока; существующие Cases права | `targetKind` сейчас только player. Typed reviewSafety residual **не закрыт**: добавить exact public/private review + revision evidence, назначенный доступ и ограниченные решения, без general staff read | Все относительные backend пути без префикса выше находятся в `apps/api/src/games/`, frontend — `apps/web/src/components/consumer/games/`, кроме явно указанных. ## Следующий содержательный кандидат 1. Один общий server predicate для нового peer/venue experience в существующем product module, с типизированным private capability result. Сверять server end + held + lifecycle + historical membership + explicit present + disputes, обе стороны peer, самостоятельное подтверждение manager исключить. Пользовательские supplied identities/present/verified flags не доверять. Eligibility и private picker возвращать только авторизованному автору; наружу не передавать чужие attendance reasons. Нормальный `present` не требует нового обязательного ACK, если открытого спора нет. 2. Extend PG2/Cases: автор может запросить missing mark и обратиться за помощью без organizer response. Explicit absent — существующий dispute. По outcome — узкий case-scoped источник решения со своим expectedVersion, не расширение manager прав. Текущие сроки help показываются по timestamp, не по job. На 48h появляется более заметное действие; отправка требует действия автора, не фиктивного автоназначения. Reviewer independence включает конфликт интересов площадки. Если корректного исторического context нет, новый intake не подделывает его: нужны сведения/уточнение, review закрыт. 3. Venue-specific experience record/projection отдельно от PG3 и legacy comments. Приватные связи author + game + assertion versions + actual canonical venue + policy revision; публичный DTO только opaque review id, допустимые темы/текст, generic author, состояние редактирования. Не публиковать gameId/precise dates/roster/profile link; не использовать auth userId в cursor. Новая additive migration после проверки номера в текущей ветке, старую 0051 не переписывать. Canonical identity проверить сервером, сохранить played-at binding; после спорной смены площадки — case, не silent retarget. 4. UNIQUE author/target/game + одна публичная карточка author/venue; новая посещённая игра обновляет ту же карточку. Конкурентные create/alias/different idempotency key не дают дубль. Окно 14 дней от первого eligible; appeal сохраняет остаток/min 7 дней. Exact consent revision, approval revision и 48h от последнего content edit. `notBefore=max(approvedAt,lastMaterialEditAt+48h)`; visibility вычисляется по server time и holds, без scheduler. Не писать фиктивный фактический publish event заранее; approval и effective visibility различаются. 5. Content review/typed report + reasoned appeal в существующих Cases. Перед public enable доказать реальную назначенную обработку одного bounded DEV case независимым reviewer. Нет гарантированного SLA и нет новой общей роли администратора. При исправлении показать автору reason, не отправлять отзыв обратно только организатору. Без обслуживаемого независимого пути — public writer закрыт. Показывать один private peer reply; venue responses в v1 закрыты, generic comments reply нельзя использовать как обход. 6. Поздняя односторонняя correction не уничтожает критику: unpublished hold; published version получает private case. Только независимое решение о недействительности основания/нарушении снимает текст, либо сам автор отзывает. Само количество reports не hide. Existing account/public-parent fences остаются; organizer block не переносить из GAME Q&A в gate venue experience. Block может закрывать direct peer interaction, помощь остаётся доступна по существующим ограничениям. ## UI scope и wording В `GameResultsPage.tsx` / `GameFeedbackPanel.tsx` добавить отдельный вход площадки; private peer остаётся там же. Public player page: «Отзыв после игры» → собственный список совместных игр → private PG3, без публичного списка отзывов и без score. VenuePage: «Поделиться опытом» → свой game picker → gate → отдельная audience/consent форма. Вариант «Только себе» — отдельный own draft, **не** PG3 manager-only note и не сообщение персоналу площадки. Старые отзывы не prefill. Вводные: «Посещение ещё не отмечено», «Попросить уточнение», «Нужна независимая проверка», «Отправлен; ещё не опубликован». Ошибка неизвестного результата: «Не удалось подтвердить отправку. Текст сохранён» + «Проверить отправку» с исходным key. Version conflict сохраняет draft и предлагает reread; не retry новым key. Public venue copy поясняет, что имя автора и игра не показаны, но автор не должен раскрывать себя/других в тексте. Сохранить approved shell, Onest, palette, radius28, panel40/20, controls≥44, phone inputs16, native safe-area reserve76. Desktop normal ProfileDialog560, X44@24, inset40, один scrolling body; mobile existing bottom sheet. Макет `index.html` использует native dialog только для локальной демонстрации; не переносить новый modal engine. Предлагаемое узкое дополнение Guide: review-card имеет один текст и максимум один допустимый ответ, а состояние/аудитория расположены над composer. Общую comments/Q&A глубину этим не переопределять. ## Приёмка и адресные проверки Не строить T01–T20. После repair — узкие deterministic regression checks изменённого gate. После содержательного кандидата один независимый QA другой модели, затем обязательные Product CI/security и trusted DEV smoke. Не повторять неизменённую матрицу после небольшого исправления. - **Реальная auth boundary в disposable/разрешённом DEV fixture:** настоящий session/CSRF Origin/Idempotency flow существующего test harness; guest read разрешённого public review и guest write denied; signed-in nonparticipant не может писать VENUE через старый route. Из задачи не искать cookies или секреты. При недоступной среде отметить NOT_TESTED и оставить назначенный smoke, не имитировать авторизацию. - **Один held game fixture + адресные отрицательные варианты:** будущий конец, unknown/not_held/cancelled, confirmed без present, absent/open dispute, другая Game/venue, self peer и creator без participation. Подмена `present/recipient/verified` в body ничего не разрешает. Manager self-mark не даёт свой отзыв. В target peer проверить explicit present, не только membership. - **Один жизненный путь апелляции:** missing→request→silence→independent intake→correct_present; receipt+версии, никакого help=assigned. Manager correction при открытом dispute не qualify. При insufficient evidence нет допуска. Отдельно проверить outcome-gap если он входит в кандидат. Поздний absent после public review не удаляет критику сам; независимое ошибочное основание снимает её с audit. - **Одна повторная операция / гонка:** UNKNOWN reconciliation оригинального key, повтор с другим key, stale expectedVersion и смена session. Не создаётся дубль, не выдаётся чужой draft, старый receipt не возвращает права после restriction/block. Реальное отсутствие receipt не выдаётся за success. - **Одна публичная проекция:** opt-in пуст по умолчанию; no body до exact approval+48h; после редактирования approval сброшен, original published revision не подменяется. Public guest API/DOM/cursor не содержат private game/identity/PG3 данных. Unlisted source game специально включить в fixture. Negative venue text проходит; контакт/угроза возвращается/снимается по exact case. Organizer не удаляет и не отвечает от имени площадки. Block фильтрует личную выдачу без глобального удаления. Venue alias canonical не создаёт вторую карточку. После принятых gates trusted process выпускает DEV и записывает exact source SHA, release_id/receipt, фактическую дату acceptance, smoke и **UAT=NOT_TESTED**, обновляет существующую историю версий и передаёт Owner. Номер версии готовит release-агент перед реальным выпуском: не предполагать, что отменённая alpha15 уже выпущена. #394 целиком DONE не объявлять, если release только оградил старый writer или содержит лишь Q&A. ## Evidence / проверка артефактов Наблюдения основаны на узком чтении source и primary web, перечисленных в RECOMMENDATION.md; текущая Git/runtime/DB история не исследовалась. Source edits, модельные дочерние вызовы, Git/merge/deploy не выполнялись. Проверка файлов выполнена: ELIGIBILITY.json успешно разобран; в HTML 8 сценариев, встроенные исходные logo/font, нет внешних src/href или JS network calls, статические id не дублируются; JavaScript прошёл `node --check` (локальный Node v24.21.0). Это проверка файлов предложения, **не QA продукта**. Браузерный render и mobile keyboard/focus smoke оставлены trusted PMA; скриншоты и визуальный PASS не заявляются.