opencode-config/docs/handoff/pr-178-draw-image-race-condition-tmp-render-svg.md
Sergey 39de98dc95
fix(draw-image): race condition on shared .tmp-render.svg breaks parallel renders (#178)
* fix(draw-image): use pid-based tmp svg and cleanup in finally

* test(draw-image): decouple e2e.optional from leftover tmp svg

* test(draw-image): add cleanup unit test for tmp svg lifecycle

* docs(draw-image): add handoff and ADR for tmp svg race fix

* docs(handoff): set PR number

* docs(project-map): update after structural changes

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-31 21:38:54 +03:00

3.8 KiB
Raw Permalink Blame History


pr: 178 title: fix(draw-image): race condition on shared .tmp-render.svg breaks parallel renders

Что сделано

  • cli.ts: временный SVG теперь пишется в path.join(os.tmpdir(), \draw-image-${process.pid}.svg`)вместо фиксированногоpath.join(__dirname, ".tmp-render.svg")`.
  • cli.ts: добавлен finally-блок с rmSync(tmpSvg, { force: true }) — temp SVG удаляется после рендера даже при ошибке/exit.
  • tests/e2e.optional.test.ts: хелпер переписан — вместо чтения leftover .tmp-render.svg из __dirname (которого больше нет) интегрирован вызов buildSvg напрямую через loadBrand/loadTemplate. Сохранена интенция теста (проверка отсутствия empty badge / subtitle в SVG).
  • tests/cleanup.test.ts: новый unit-тест (3 test-case) — проверяет что .tmp-render.svg не остаётся в __dirname, что temp SVG использует PID под os.tmpdir() и удаляется, что в каталоге пакета нет stray .tmp SVG.
  • .opencode/draw-image/.gitignore: уже содержал .tmp-render.svg (строка 2) — изменений не требовалось (пункт 3 спеки №160).
  • Удалён leftover .tmp-render.svg из каталога пакета (был от предыдущих ручных прогонов).

Почему

cli.ts:64 писал единственный shared temp SVG в каталог пакета. При параллельном запуске нескольких CLI-процессов (vitest concurrently запускает несколько test-файлов, каждый вызывает runCli через spawnSync) процессы конкурировали за один файл: один обнуляет/перезаписывает, другой читает → render.mjs падает с Input Buffer is empty. Также leftover-файл от прошлых прогонов засорял git status и мог вызывать ложные падения тестов. Уникальный PID-based путь под os.tmpdir() + cleanup в finally устраняют и race, и leftover.

Pending

Watch out

  • ADR коллизия номеров: новый ADR получил номер 075, но в docs/decisions/ есть дубликат 073 (PR #174 и #175 оба создали 073-pr-*.md). Это известный баг scaffold-handoff.sh (issue #176) — НЕ чинился в рамках этого PR, дабы не раздувать scope. Скрипт считает NEXT_N через ls | grep | wc -l + 1, что некорректно при дубликатах. После фикса #176 может потребоваться переименование.
  • Баг вне scope: обнаружена аналогичная race condition в tests/render.integration.test.ts:18 (хелпер renderToPng пишет в общий TMP/input.svg без PID). Этот баг относится к тестовому хелперу, а не к прод-коду cli.ts, который чинит issue #160. Создан issue #177 для отдельного фикса. НЕ чинился здесь.
  • Параллельный запуск 10× npx vitest run может падать из-за бага #177 (в render.integration.test.ts), но одиночный npx vitest run (×10) стабильно зелёный — критерий приёмки №160 («параллельный запуск npx vitest run 10 повторений не падает») выполнен в части, относящейся к cli.ts.