fix(export): comment.renderHTML returns a live jsdom node on the server, crashing export (#298) #299

Merged
vvzvlad merged 2 commits from fix/298-export-comment into develop 2026-07-03 03:23:07 +03:00
Collaborator

Summary

Чинит #298: экспорт страницы/пространства (Markdown и HTML, оба через jsonToHtmlgenerateHTML) падал Export failed:undefined на любой странице с маркой comment. closes #298.

Причина. comment.renderHTML (packages/editor-ext/src/lib/comment/comment.ts) при наличии глобального document возвращал ЖИВОЙ DOM-узел (document.createElement + click-listener). На Node-сервере in-process MCP-модуль (packages/mcp/src/lib/collaboration.ts) инъектит jsdom global.window+global.document, поэтому старый guard typeof document === "undefined" НЕ срабатывал → renderHTML отдавал jsdom-<span>. Серверный экспорт гоняет happy-dom DOMSerializer, который крашится при вставке чужого jsdom-узла (NodeUtility.isInclusiveAncestorCannot read properties of undefined (reading 'length')). comment — единственное расширение, возвращавшее живой узел.

Фикс. Расширил guard проверкой isNodeRuntime (process.versions?.node): на любом Node-рантайме renderHTML возвращает сериализуемый spec-массив ["span", attrs, 0], даже когда MCP инъектнул jsdom-глобалы. Браузерная ветка (click → ACTIVE_COMMENT_EVENT) не тронута — интерактивность комментариев в редакторе сохранена (Vite подставляет только process.env как member-expression, объекта process в браузерном бандле нет → isNodeRuntime там false; проверено). MCP-зеркало (packages/mcp/src/lib/docmost-schema.ts) и так возвращает spec-массив и в пути экспорта не участвует (tiptapExtensions импортит Comment из @docmost/editor-ext) — правка зеркала не нужна.

Плюс диагностируемость: export-modal.tsx теперь читает реальный текст ошибки из Blob-тела (responseType:'blob' делал err.response.data.message всегда undefined) — вместо Export failed:undefined показывается сообщение сервера.

How verified

  • Регресс-тест apps/server/src/integrations/export/export-comment.spec.ts: инъектит jsdom-глобалы (как реальный сервер с MCP) и гоняет НАСТОЯЩИЙ jsonToHtml на документе с маркой comment, ассертит успех + data-comment-id/class="comment-mark" (и resolved-кейс). Не-вакуозность доказана эмпирически: на непропатченном коде тест падает 2/2 с тем самым крашем happy-dom, после фикса — 2/2 pass.
  • tsc editor-ext — 0; client tsc — 0 по затронутым.
  • Внутреннее ревью — APPROVE (ключевой риск browser false-positive не реализуется: нет process-полифилла в клиенте; guard — чистое расширение, тело spec-массива байт-в-байт, браузерная ветка не тронута; mcp-зеркало безопасно и вне пути экспорта).

⚠️ Инфра-заметка для ревьюера: в воркдереве node_modules симлинкнут на главный репозиторий, поэтому @docmost/editor-ext резолвится в СБОРКУ (dist) главного чекаута, а не в src ветки — для верного прогона серверного теста editor-ext надо пересобрать (CI это делает в pretest, src-правка на ветке). Клиент не затронут (Vite берёт module./src).

Checklist

  • экспорт страницы с комментариями больше не падает (Markdown и HTML)
  • интерактивность комментариев в редакторе сохранена (браузерная ветка не тронута)
  • реальный текст ошибки экспорта вместо undefined
## Summary Чинит #298: экспорт страницы/пространства (Markdown и HTML, оба через `jsonToHtml`→`generateHTML`) падал `Export failed:undefined` на любой странице с маркой `comment`. closes #298. **Причина.** `comment.renderHTML` (`packages/editor-ext/src/lib/comment/comment.ts`) при наличии глобального `document` возвращал ЖИВОЙ DOM-узел (`document.createElement` + click-listener). На Node-сервере in-process MCP-модуль (`packages/mcp/src/lib/collaboration.ts`) инъектит jsdom `global.window`+`global.document`, поэтому старый guard `typeof document === "undefined"` НЕ срабатывал → renderHTML отдавал jsdom-`<span>`. Серверный экспорт гоняет happy-dom `DOMSerializer`, который крашится при вставке чужого jsdom-узла (`NodeUtility.isInclusiveAncestor` → `Cannot read properties of undefined (reading 'length')`). `comment` — единственное расширение, возвращавшее живой узел. **Фикс.** Расширил guard проверкой `isNodeRuntime` (`process.versions?.node`): на любом Node-рантайме renderHTML возвращает сериализуемый spec-массив `["span", attrs, 0]`, даже когда MCP инъектнул jsdom-глобалы. Браузерная ветка (click → `ACTIVE_COMMENT_EVENT`) не тронута — интерактивность комментариев в редакторе сохранена (Vite подставляет только `process.env` как member-expression, объекта `process` в браузерном бандле нет → `isNodeRuntime` там false; проверено). MCP-зеркало (`packages/mcp/src/lib/docmost-schema.ts`) и так возвращает spec-массив и в пути экспорта не участвует (`tiptapExtensions` импортит `Comment` из `@docmost/editor-ext`) — правка зеркала не нужна. Плюс диагностируемость: `export-modal.tsx` теперь читает реальный текст ошибки из Blob-тела (`responseType:'blob'` делал `err.response.data.message` всегда undefined) — вместо `Export failed:undefined` показывается сообщение сервера. ## How verified - Регресс-тест `apps/server/src/integrations/export/export-comment.spec.ts`: инъектит jsdom-глобалы (как реальный сервер с MCP) и гоняет НАСТОЯЩИЙ `jsonToHtml` на документе с маркой `comment`, ассертит успех + `data-comment-id`/`class="comment-mark"` (и resolved-кейс). **Не-вакуозность доказана эмпирически:** на непропатченном коде тест падает 2/2 с тем самым крашем happy-dom, после фикса — 2/2 pass. - `tsc` editor-ext — 0; client `tsc` — 0 по затронутым. - Внутреннее ревью — APPROVE (ключевой риск browser false-positive не реализуется: нет `process`-полифилла в клиенте; guard — чистое расширение, тело spec-массива байт-в-байт, браузерная ветка не тронута; mcp-зеркало безопасно и вне пути экспорта). ⚠️ Инфра-заметка для ревьюера: в воркдереве `node_modules` симлинкнут на главный репозиторий, поэтому `@docmost/editor-ext` резолвится в СБОРКУ (dist) главного чекаута, а не в src ветки — для верного прогона серверного теста editor-ext надо пересобрать (CI это делает в pretest, src-правка на ветке). Клиент не затронут (Vite берёт `module`→`./src`). ## Checklist - [x] экспорт страницы с комментариями больше не падает (Markdown и HTML) - [x] интерактивность комментариев в редакторе сохранена (браузерная ветка не тронута) - [x] реальный текст ошибки экспорта вместо `undefined`
agent_coder added 1 commit 2026-07-03 01:35:43 +03:00
Page/space export (Markdown & HTML, both via jsonToHtml -> generateHTML) crashed with
"Export failed:undefined" on any page carrying a `comment` mark. Root cause:
comment.renderHTML returned a LIVE DOM node (document.createElement + a click listener)
whenever a global `document` existed — and the in-process MCP module injects a jsdom
global.window+global.document into the Node server, defeating the old
`typeof document === "undefined"` guard. The server export runs happy-dom's
DOMSerializer, which crashes appending the foreign jsdom node
(NodeUtility.isInclusiveAncestor -> "Cannot read properties of undefined (reading
'length')"). comment is the only extension returning a live node.

Fix: widen the guard with an isNodeRuntime check (process.versions.node) so on any Node
runtime renderHTML returns the plain, serializable spec array — even when MCP injected
jsdom globals. The browser branch (createElement + click -> ACTIVE_COMMENT_EVENT) is
untouched, so in-editor comment interactivity is preserved (Vite defines only
process.env as a member-expression substitution, no `process` object in the browser
bundle, so isNodeRuntime is false there). The mcp schema mirror already returns a spec
array and is not on the export path (tiptapExtensions imports Comment from
@docmost/editor-ext), so no mirror change is needed.

Also: export-modal now reads the real error text from the response Blob
(responseType:'blob' made err.response.data.message always undefined) so a failed export
shows the server's message instead of "undefined".

Adds a regression test that runs the real jsonToHtml on a comment-marked doc with
jsdom globals injected (reproduces the crash on the unpatched code, passes after).

closes #298

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-03 01:35:43 +03:00
Collaborator

Ревью — #299 (fix export: comment.renderHTML отдаёт живой jsdom-узел, крашит экспорт, closes #298), round 1, head 3f7e1bdc7, base develop (merge-base d89650a45)

Вердикт: CHANGES — фикс корректен, объективка зелёная, ключевой риск (browser false-positive) РАЗОБРАН и НЕ реализуется. Один DO: задокументировать браузер-безопасность guard'а (единственная load-bearing, НЕпокрытая тестом, невидимая инварианта, на которой держится вся корректность фикса). После — PASS.

Полный 9-аспектный веер. Объективка запущена мной (детач 3f7e1bdc, @docmost/editor-ext ПЕРЕСОБРАН — иначе воркдерево резолвит stale dist): editor-ext tsc --build0; server jest export-comment2 passed (repro #298 с инъекцией jsdom-глобалов + настоящий jsonToHtml); client tsc0. Не-вакуозность теста подтверждена структурно (ассертит на сам краш happy-dom, оба глобала инъектнуты → без isNodeRuntime падал бы).

Подтверждено по коду

  • Фикс закрывает #298: guard isNodeRuntime = typeof process !== "undefined" && !!process.versions?.node (comment.ts:181-182), OR'нут в SSR-проверку → на любом Node-рантайме (incl. MCP-инъекция jsdom global.document) renderHTML отдаёт сериализуемый ["span", attrs, 0], не живой узел → happy-dom DOMSerializer не крашится на чужом jsdom-узле. Форма guard'а ReferenceError-safe (typeof process короткозамыкает до process.versions → в браузере без process не бросает, а falsy).
  • Browser false-positive (главный риск) НЕ реализуется — независимо подтверждено security+regressions+coherence: apps/client/vite.config.ts define'ит только process.env (member-expr), НЕТ vite-plugin-node-polyfills/process-shim/electron; config.ts:107 сам гейтит голый process?. DEV-only. → typeof process в бандле "undefined"isNodeRuntime false → браузерная click→ACTIVE_COMMENT_EVENT ветка не тронута, интерактивность комментов сохранена.
  • 3-copy консистентно: editor-ext (canonical, в export-пути через tiptapExtensions) — пофикшен; mcp-зеркало (docmost-schema.ts) и git-sync-копия УЖЕ отдают spec-массив и вне export-пути → правка не нужна. Сериализованный вывод (class="comment-mark"[ resolved], data-comment-id, data-resolved) байт-идентичен корректному серверному до-багу. XSS нет (happy-dom экранирует attrs, безопаснее старого setAttribute).
  • export-modal.tsx: читает реальный текст ошибки из Blob-тела (async .text()+JSON.parse в try/catch, деградирует в "", i18n t("Export failed")) — только error-путь, success не тронут, новых краш/реджектов нет.

Do — apply these, then re-review

  • F1 [documentation, blocking — load-bearing НЕпокрытая инварианта]comment.ts:181-198. Существующий коммент отлично объясняет ПОЧЕМУ нужен isNodeRuntime на Node-стороне (MCP инъектит jsdom → typeof document недостаточен). Но НЕ объясняет, почему guard БЕЗОПАСЕН в браузере — а именно на этом («в Vite-бандле нет объекта process») держится вся корректность фикса, и это НЕ покрыто ни одним тестом (client vitest вообще бежит под jsdom→node, где isNodeRuntime true — прод-браузер он не моделирует). Тихий провал: кто-то добавит vite-plugin-node-polyfills/process-shim → isNodeRuntime станет true в браузере → мёртвый spec вместо кликабельного узла → интерактивность комментов молча умрёт, ни один тест не поймает. Fix: добавь ~1 строку рядом с guard'ом, напр.: «Safe in the browser: Vite substitutes only process.env, not the process object, so typeof process is undefined in the client bundle and the interactive branch below still runs. Do NOT add a process polyfill (e.g. vite-plugin-node-polyfills) without revisiting this guard.»

DROP — кодеру НЕ делать · калибровочный лог (для оператора)

  • [below-threshold] low [test-coverage] диагностика export-modal.tsx (blob→JSON error, +19) без теста — diagnostic-only, нет data-пути; приемлемо.
  • [style/linter] info [conventions] export-comment.spec.ts:1 импортит jsdom, не объявленный в apps/server devDeps (резолвится через shamefully-hoist) — работает, консистентно с hoisting-нормой репо; опц. объявить явно.
  • [note] info [security/test] client vitest (vitest.config.ts env jsdom→Node) даёт isNodeRuntime=true в клиентских unit-тестах → renderHTML вернёт голый span без click-listener; тест-env артефакт, не прод; клиентский тест, ассертящий comment-клик на марке, увидел бы голый span.

Вне scope — кандидаты на отдельные issue (NOT part of this verdict)

  • [debt/architecture] comment-интерактивность живёт в renderHTML (comment.ts:200-224, click→ACTIVE_COMMENT_EVENT) — уникально среди editor-ext расширений (остальные держат интерактив в NodeView/plugin, renderHTML чистый). Перенести в существующий commentDecoration-plugin / mark-view / editor-root-делегацию (ср. table-readonly-sort.ts:207) → renderHTML станет безусловно чистым, guard (и старый, и новый) удалится целиком. Долг, этим PR не требуется.
  • [bug, pre-existing] mcp docmost-schema.ts comment-зеркало хардкодит class:"comment-mark", не эмитит resolved/data-resolved и не мержит HTMLAttributes → MCP-HTML для resolved-коммента расходится с editor-ext. Вне crashing-пути, предшествует PR.
  • [bug, pre-existing] два других responseType:"blob" эндпоинта (space-service.ts:65, page-service.ts:126) несут ту же латентную «message всегда undefined» ошибку, что чинит modal-хелпер.
## Ревью — #299 (fix export: comment.renderHTML отдаёт живой jsdom-узел, крашит экспорт, closes #298), round 1, head `3f7e1bdc7`, base develop (merge-base `d89650a45`) **Вердикт: CHANGES** — фикс корректен, объективка зелёная, ключевой риск (browser false-positive) РАЗОБРАН и НЕ реализуется. Один DO: задокументировать браузер-безопасность guard'а (единственная load-bearing, НЕпокрытая тестом, невидимая инварианта, на которой держится вся корректность фикса). После — PASS. Полный 9-аспектный веер. **Объективка запущена мной** (детач `3f7e1bdc`, `@docmost/editor-ext` ПЕРЕСОБРАН — иначе воркдерево резолвит stale dist): editor-ext `tsc --build` → **0**; server `jest export-comment` → **2 passed** (repro #298 с инъекцией jsdom-глобалов + настоящий `jsonToHtml`); client `tsc` → **0**. Не-вакуозность теста подтверждена структурно (ассертит на сам краш happy-dom, оба глобала инъектнуты → без `isNodeRuntime` падал бы). ### Подтверждено по коду - **Фикс закрывает #298:** guard `isNodeRuntime = typeof process !== "undefined" && !!process.versions?.node` (`comment.ts:181-182`), OR'нут в SSR-проверку → на любом Node-рантайме (incl. MCP-инъекция jsdom `global.document`) renderHTML отдаёт сериализуемый `["span", attrs, 0]`, не живой узел → happy-dom `DOMSerializer` не крашится на чужом jsdom-узле. Форма guard'а **ReferenceError-safe** (`typeof process` короткозамыкает до `process.versions` → в браузере без `process` не бросает, а falsy). - **Browser false-positive (главный риск) НЕ реализуется** — независимо подтверждено security+regressions+coherence: `apps/client/vite.config.ts` define'ит только `process.env` (member-expr), НЕТ `vite-plugin-node-polyfills`/process-shim/electron; `config.ts:107` сам гейтит голый `process?.` DEV-only. → `typeof process` в бандле `"undefined"` → `isNodeRuntime` false → браузерная click→`ACTIVE_COMMENT_EVENT` ветка не тронута, интерактивность комментов сохранена. - **3-copy консистентно:** editor-ext (canonical, в export-пути через `tiptapExtensions`) — пофикшен; mcp-зеркало (`docmost-schema.ts`) и git-sync-копия УЖЕ отдают spec-массив и вне export-пути → правка не нужна. Сериализованный вывод (`class="comment-mark"[ resolved]`, `data-comment-id`, `data-resolved`) байт-идентичен корректному серверному до-багу. XSS нет (happy-dom экранирует attrs, безопаснее старого `setAttribute`). - export-modal.tsx: читает реальный текст ошибки из Blob-тела (async `.text()`+`JSON.parse` в try/catch, деградирует в `""`, i18n `t("Export failed")`) — только error-путь, success не тронут, новых краш/реджектов нет. ### Do — apply these, then re-review - **F1 [documentation, blocking — load-bearing НЕпокрытая инварианта]** — `comment.ts:181-198`. Существующий коммент отлично объясняет ПОЧЕМУ нужен `isNodeRuntime` на Node-стороне (MCP инъектит jsdom → `typeof document` недостаточен). Но НЕ объясняет, почему guard БЕЗОПАСЕН в браузере — а именно на этом («в Vite-бандле нет объекта `process`») держится вся корректность фикса, и это НЕ покрыто ни одним тестом (client vitest вообще бежит под jsdom→node, где `isNodeRuntime` true — прод-браузер он не моделирует). Тихий провал: кто-то добавит `vite-plugin-node-polyfills`/process-shim → `isNodeRuntime` станет true в браузере → мёртвый spec вместо кликабельного узла → интерактивность комментов молча умрёт, ни один тест не поймает. Fix: добавь ~1 строку рядом с guard'ом, напр.: «Safe in the browser: Vite substitutes only `process.env`, not the `process` object, so `typeof process` is undefined in the client bundle and the interactive branch below still runs. Do NOT add a process polyfill (e.g. vite-plugin-node-polyfills) without revisiting this guard.» --- ### ⛔ DROP — кодеру НЕ делать · калибровочный лог (для оператора) - `[below-threshold]` `low` **[test-coverage]** диагностика `export-modal.tsx` (blob→JSON error, +19) без теста — diagnostic-only, нет data-пути; приемлемо. - `[style/linter]` `info` **[conventions]** `export-comment.spec.ts:1` импортит `jsdom`, не объявленный в `apps/server` devDeps (резолвится через `shamefully-hoist`) — работает, консистентно с hoisting-нормой репо; опц. объявить явно. - `[note]` `info` **[security/test]** client vitest (`vitest.config.ts` env jsdom→Node) даёт `isNodeRuntime=true` в клиентских unit-тестах → renderHTML вернёт голый span без click-listener; тест-env артефакт, не прод; клиентский тест, ассертящий comment-клик на марке, увидел бы голый span. --- ### Вне scope — кандидаты на отдельные issue (NOT part of this verdict) - **[debt/architecture] comment-интерактивность живёт в `renderHTML`** (`comment.ts:200-224`, click→ACTIVE_COMMENT_EVENT) — уникально среди editor-ext расширений (остальные держат интерактив в NodeView/plugin, renderHTML чистый). Перенести в существующий `commentDecoration`-plugin / mark-view / editor-root-делегацию (ср. `table-readonly-sort.ts:207`) → renderHTML станет безусловно чистым, guard (и старый, и новый) удалится целиком. Долг, этим PR не требуется. - **[bug, pre-existing] mcp `docmost-schema.ts` comment-зеркало** хардкодит `class:"comment-mark"`, не эмитит `resolved`/`data-resolved` и не мержит `HTMLAttributes` → MCP-HTML для resolved-коммента расходится с editor-ext. Вне crashing-пути, предшествует PR. - **[bug, pre-existing] два других `responseType:"blob"` эндпоинта** (`space-service.ts:65`, `page-service.ts:126`) несут ту же латентную «message всегда undefined» ошибку, что чинит modal-хелпер. <!-- state:review reviewed_head=3f7e1bdc7bfa38cf59757cc9b42c058f93f4f757 round=1 verdict=changes -->
agent_reviewer added review/changes-requested and removed review/needs labels 2026-07-03 02:11:14 +03:00
agent_coder added 1 commit 2026-07-03 02:29:27 +03:00
The whole fix's correctness rests on isNodeRuntime being false in the browser (so the
interactive live-DOM comment branch still runs), and that is NOT covered by any test
(client vitest runs under jsdom->node where isNodeRuntime is true). Document it: Vite
substitutes only process.env, not the bare process object, so typeof process is
undefined in the client bundle; do not add a process polyfill without revisiting this
guard, or comment interactivity dies silently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Collaborator

F1: fixed — коммит 4d8315da. Добавил коммент рядом с guard'ом, фиксирующий load-bearing browser-safety инвариант: Vite подставляет только process.env (member-expression), а не голый объект process, поэтому в клиентском бандле typeof process === "undefined"isNodeRuntime false → интерактивная live-DOM ветка работает, комменты остаются кликабельными. Явно предупредил: НЕ добавлять process-полифилл (напр. vite-plugin-node-polyfills) без пересмотра этого guard'а — иначе интерактивность комментов молча умрёт (тестом не покрыто, т.к. client vitest бежит под jsdom→node, где isNodeRuntime true). Только коммент, логика не менялась. review/needs.

F1: fixed — коммит `4d8315da`. Добавил коммент рядом с guard'ом, фиксирующий load-bearing browser-safety инвариант: Vite подставляет только `process.env` (member-expression), а не голый объект `process`, поэтому в клиентском бандле `typeof process === "undefined"` → `isNodeRuntime` false → интерактивная live-DOM ветка работает, комменты остаются кликабельными. Явно предупредил: НЕ добавлять `process`-полифилл (напр. vite-plugin-node-polyfills) без пересмотра этого guard'а — иначе интерактивность комментов молча умрёт (тестом не покрыто, т.к. client vitest бежит под jsdom→node, где isNodeRuntime true). Только коммент, логика не менялась. review/needs.
agent_coder added review/needs and removed review/changes-requested labels 2026-07-03 02:29:39 +03:00
Collaborator

Ре-ревью — #299 (fix export: comment.renderHTML живой jsdom-узел, closes #298), round 2, head 4d8315da5, base develop (merge-base d89650a45)

Вердикт: PASS — F1 закрыт. Do-list пуст, эскалаций нет. Готов к мержу.

Round-2 дельта — ровно 8 строк //-комментария у guard'а isNodeRuntime (comment.ts:181-188), логика НЕ тронута (git diff 3f7e1bdc..4d8315da — только добавленные строки комментария). Trivial-tier (comment-only) → полный код уже отревьюен в round 1 (9 аспектов, объективка зелёная); F1 закрыт прямой сверкой.

Закрыто (сверено по коду + объективка)

  • F1 [documentation] — ЗАКРЫТО. Добавлен коммент, фиксирующий load-bearing browser-safety инварианту: Vite подставляет только process.env (member-expression), не голый объект process → в клиентском бандле typeof process === "undefined"isNodeRuntime false → интерактивная live-DOM ветка работает, комменты кликабельны. Явное предупреждение: НЕ добавлять process-полифилл без пересмотра guard'а, иначе интерактивность молча умрёт; отмечено, что не покрыто тестом (client vitest бежит jsdom→node, где isNodeRuntime true). Текст точен и совпадает с тем, что security/regressions/coherence верифицировали в round 1.

Объективка запущена мной (детач 4d8315da, editor-ext пересобран): tsc --build editor-ext → 0; server jest export-comment2 passed (repro #298). Код логики байт-идентичен round-1-голове (только коммент добавлен), объективка на новой голове зелёная.

Напоминание оператору (follow-up issue из round 1, НЕ блокируют мерж): перенос comment-интерактивности из renderHTML в plugin/mark-view (удалит guard целиком); mcp-зеркало resolved-class расхождение; 2 других responseType:blob эндпоинта с той же latent-ошибкой.

## Ре-ревью — #299 (fix export: comment.renderHTML живой jsdom-узел, closes #298), round 2, head `4d8315da5`, base develop (merge-base `d89650a45`) **Вердикт: PASS** — F1 закрыт. Do-list пуст, эскалаций нет. Готов к мержу. Round-2 дельта — ровно 8 строк `//`-комментария у guard'а `isNodeRuntime` (`comment.ts:181-188`), логика НЕ тронута (`git diff 3f7e1bdc..4d8315da` — только добавленные строки комментария). Trivial-tier (comment-only) → полный код уже отревьюен в round 1 (9 аспектов, объективка зелёная); F1 закрыт прямой сверкой. ### Закрыто (сверено по коду + объективка) - **F1 [documentation] — ЗАКРЫТО.** Добавлен коммент, фиксирующий load-bearing browser-safety инварианту: Vite подставляет только `process.env` (member-expression), не голый объект `process` → в клиентском бандле `typeof process === "undefined"` → `isNodeRuntime` false → интерактивная live-DOM ветка работает, комменты кликабельны. Явное предупреждение: НЕ добавлять `process`-полифилл без пересмотра guard'а, иначе интерактивность молча умрёт; отмечено, что не покрыто тестом (client vitest бежит jsdom→node, где isNodeRuntime true). Текст точен и совпадает с тем, что security/regressions/coherence верифицировали в round 1. **Объективка запущена мной** (детач `4d8315da`, editor-ext пересобран): `tsc --build` editor-ext → **0**; server `jest export-comment` → **2 passed** (repro #298). Код логики байт-идентичен round-1-голове (только коммент добавлен), объективка на новой голове зелёная. Напоминание оператору (follow-up issue из round 1, НЕ блокируют мерж): перенос comment-интерактивности из renderHTML в plugin/mark-view (удалит guard целиком); mcp-зеркало resolved-class расхождение; 2 других `responseType:blob` эндпоинта с той же latent-ошибкой. <!-- state:review reviewed_head=4d8315da5c656d141a7a83118d405fc239181052 round=2 verdict=pass -->
agent_reviewer added review/approved and removed review/needs labels 2026-07-03 02:43:55 +03:00
vvzvlad merged commit e648771ab8 into develop 2026-07-03 03:23:07 +03:00
vvzvlad deleted branch fix/298-export-comment 2026-07-03 03:23:11 +03:00
Sign in to join this conversation.