Open
agent_coder
wants to merge 2 commits from
feat/251-intentional-clear into develop
pull from: feat/251-intentional-clear
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:test/244-part-b
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/184-autonomous-agent-runs
vvzvlad:feat/221-image-captions
vvzvlad:feat/git-sync
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:fix/244-dataloss-bugs
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:develop
vvzvlad:feature/offline-sync
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
No Reviewers
Labels
Clear labels
bug
documentation
duplicate
enhancement
epic
feature
good first issue
help wanted
idea
invalid
needs-human
question
refactor
review/approved
review/changes-requested
review/needs
security
status/blocked
status/done
status/in-progress
status/ready
test
wontfix
Something isn't working
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Large multi-phase effort spanning many changes
New functionality request
Good for newcomers
Extra attention is needed
Idea / proposal for discussion
This doesn't seem right
эскалация: нужно решение человека
Further information is requested
Code cleanup / refactoring
в последнем ревью нет открытых blocking-находок
последнее ревью оставило открытые blocking-находки
head не ревьюился (head != reviewed_head)
Security / hardening issue
ждёт зависимость blocked_by
закрыто и проверено
в активной работе (мягкая заявка)
специфицировано, не заблокировано, ждёт исполнителя
Test coverage / test infrastructure
This will not be worked on
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: vvzvlad/gitmost#253
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "feat/251-intentional-clear"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Проводит сигнал намеренной очистки (
intentionalClear) от редактора до store, чтобы намеренная очистка страницы (select-all + Delete) персистилась, а store-side empty-guard из #248 по-прежнему блокировал случайное затирание пустым. Closes #251.Определение намеренной очистки: ЛОКАЛЬНАЯ user-транзакция, сводящая непустой doc к пустому single-paragraph (docChanged, не remote y-sync через isChangeOrigin, было непусто → стало isEmptyParagraphDoc). Это путь select-all+Delete/Backspace и команд типа clearContent. Remote/merge-пустота не квалифицируется.
Транспорт (вариант b — hocuspocus stateless): клиент на очищающей транзакции шлёт
{type:'intentional-clear'}черезprovider.sendStateless; серверPersistenceExtension.onStatelessставит короткоживущий single-use pending-флаг по documentName (TTL 60s > maxDebounce 45s).onStoreDocumentна ветке empty-over-non-empty пускает запись только еслиconsumeIntentionalClearвернул живой флаг, иначе блокирует (#248). Не спуфится: документ берётся из соединения (не из payload), read-only не армит, флаг читается ТОЛЬКО на guard-ветке, single-use + TTL, любая непустая запись сбрасывает флаг. Redis multi-master: redis-sync проводит stateless через стандартный пайплайн на узле-владельце документа (set/consume co-located).How verified
jest src/collaboration: 68/68 (вкл. все #248 guard-тесты + новые #251).vitest src/features/editor: 140 pass (+1 pre-existing expected-fail, unrelated).onStateless({connection,documentName,payload:JSON.stringify({type:'intentional-clear'})})(точный клиентский wire-месседж) → debouncedonStoreDocumentс пустым Y.Doc → пустой контент записан; + single-use (2-я пустая блокируется), read-only reject, «непустая запись сбрасывает флаг», и неизменные #248 guard-тесты. client-тест гоняет реальныйeditor.chain().selectAll().deleteSelection().run()→ проверяетsendStateless({type:'intentional-clear'}); негативы (печать/не-очистка/уже-пусто) ничего не шлют.Review checklist
⚠️ Зависимость от #248
Store-side empty-guard (#248) ещё НЕ в develop (живёт в PR #248). Эта ветка ВКЛючает блок guard как фундамент (идентичен #248, помечен комментарием). При мерже #248 в develop — ребейз чистый, дублирующий блок схлопывается. Ревьюить #251 имеет смысл после/вместе с #248.
🤖 Generated with Claude Code
Внутренний architect-lead цикл сошёлся на
cce539e8(мой review-субагент: APPROVE). Раунд: первичная реализация (3fdb1e05) → ревью нашло WARNING (consumeIntentionalClear звался ВНУТРИ retry-цикла транзакции → транзиентный сбой БД съедал флаг → намеренная очистка терялась на повторе) → фикс: consume вынесен ДО цикла (как consumeContributors/consumeAgentTouched), ветка empty-over-non-empty читает булево allowIntentionalClear (стабильно по попыткам); +retry-тест (fails-before/passes-after); +док про межузловой fail-safe. Переревью: hoist корректен, non-empty по-прежнему сбрасывает флаг (single-use), неспуфабельность не тронута, #248-guard держится, (c) преждевременное гашение практически недостижимо. server tsc чисто; jest src/collaboration 69 pass. Голова →cce539e8, review/needs. (Зависимость от #248 — guard включён как фундамент, см. описание PR.)F1 [warning]
apps/client/src/features/editor/extensions/intentional-clear.ts:81— веткаif (isChangeOrigin(transaction)) return;не покрыта тестом, а это ЦЕНТРАЛЬНАЯ защита фичи: именно она не даёт удалённой/мердж-индуцированной пустоте (другой клиент, плохой merge, опустевшая трансклюзия) проэмитить intentional-clear и пробить server-guard #248. Это ровно та realistic data-loss ситуация, ради которой guard #248 и существует (зафиксировано в док-комментарии расширения, строки 53-58). Все 4 теста в intentional-clear.test.ts используют ЛОКАЛЬНЫЕ транзакции (select-all+delete, insertContent), где isChangeOrigin всегда false → TRUE-путь раннего return не исполняется. Если чек регрессирует (инверсия/отвал импорта), негативные тесты остаются зелёными, а защита #248 для самого реалистичного сценария потери данных молча отключается.Fix: добавить в intentional-clear.test.ts тест, опустошающий непустой документ транзакцией с change-origin (remote y-sync), и проверяющий, что sendStateless НЕ вызван. Прогнать реальным путём: подключить Collaboration/y-prosemirror и применить remote-апдейт, опустошающий док, либо задиспатчить транзакцию с выставленным ySyncPluginKey meta так, чтобы isChangeOrigin(tr)===true, и expect(sendStateless).not.toHaveBeenCalled().
F2 [suggestion]
CHANGELOG.md(секция [Unreleased]) — PR вводит пользовательски-значимое изменение поведения сохранения: серверный empty-guard блокирует затирание непустого содержимого моментально-пустым live Y.Doc (защита от тихой потери страницы), а сигнал intentional-clear пропускает ровно один намеренный clear. Это класс изменений collab/persistence, которые проект последовательно фиксирует в CHANGELOG с номером задачи (ср. (#206), (#198) в [Unreleased]).git diff --merge-base origin/develop pr-253 -- CHANGELOG.mdпуст — ни #248, ни #251 нет, хотя develop их не содержит и они появляются именно здесь.Fix: добавить в [Unreleased] запись (под ### Fixed для защиты от потери данных и/или ### Added для намеренной очистки): непустую страницу больше нельзя случайно затереть пустым live-документом; намеренная очистка (select-all+Delete) теперь корректно сохраняется через сигнал intentional-clear; ссылки (#248, #251).
Ревью
cce539e8e— раунд 1 (ПОЛНЫЕ 8 аспектов, отдельный субагент на каждый). Вердикт: CHANGES.Реализует вариант B из эскалации #248 (issue #251): сигнал намеренной очистки editor→store через stateless-сообщение + per-document одноразовый флаг с TTL.
Раскладка: security (сигнал нельзя нацелить на чужой док, нужен write-доступ, fail-safe) / stability (guard #248 цел, гонки решены, сигнал не теряется на ретраях) / regressions (без сигнала пустое-поверх-непустого по-прежнему блокируется) / conventions / architecture (per-edit сигнал — правильный примитив против per-connection контекста) — LGTM.
Открыто: F1 (warning, test-coverage — ветка isChangeOrigin без теста, защита от пробивания guard удалённой пустотой), F2 (suggestion, docs — нет записи в CHANGELOG про #248/#251).
⛔ DROP (кодеру НЕ делать · калибровка):
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.