0.131.1

Fixed

  • Singleton flock снимался сразу — два инстанса на одном dataDir (A-002, ). High-дефект integrity из аудита 2026-06-24: acquireFlock брал flock(LOCK_EX|LOCK_NB), но закрывал fd через defer f.Close() при возврате → flock (привязанный к open file description) снимался немедленно; releaseFlock был no-op. На Windows acquireFlock вообще не вызывал LockFileEx. Итог: два параллельных инстанса на одной dataDir → concurrent SQLite/PostgreSQL → порча БД + конфликт порта 14888. Теперь acquireFlock возвращает (*os.File, error) без закрытия fd; AcquireLock возвращает *os.File, который tray.Run держит весь lifecycle (defer ReleaseLock(dataDir, lockFile)); ReleaseLock(dataDir, f) закрывает fd и удаляет файл. На unix — Flock(LOCK_EX|LOCK_NB) на держимом fd; на Windows — LockFileEx(LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY) (non-blocking, как LOCK_NB), ERROR_LOCK_VIOLATION → дружелюбная ошибка. Дружелюбный PID-check fast-path оставлен, но без os.Remove stale-файла (он перезаписывается атомарно через Truncate(0) под локом — иначе TOCTOU-окно для конкурента). Тесты: singleton_test.go (unix: mutual-exclusion, reusability, PID-write, holds-file, empty-dataDir, dead-PID-recovered, live-PID-rejected) + singleton_windows_test.go (Windows-зеркало).
  • Критический момент: первый прогон ocr review выявил, что Windows-версия без LOCKFILE_FAIL_IMMEDIATELY была блокирующей — повесила бы старт tray при занятом локе; добавлен флаг + различение ERROR_LOCK_VIOLATION от прочих ошибок.