refactor(persistence): 24 hand-rolled atomic JSON/text writers go through utils.atomic_json_write / atomic_write_text

Each copy re-implemented temp+replace by hand and lacked one or more of
fsync, symlink preservation, atomic_replace's Windows-contention retry and
EXDEV/bind-mount fallback, mode preservation, or interrupt-safe temp
cleanup. Three (gateway/session_persistence, cron/suggestions,
agent/shell_hooks) were verbatim inlines of utils._atomic_write; two
modules defined their own directory-fsync helper, now utils.fsync_directory.
plugins/google_meet/_jsonfile.write_json_atomic is deleted (callers use the
canonical helper directly).

Behavior change: every one of these writers now fsyncs the payload, keeps a
pre-existing target's mode, cleans its temp file on BaseException, and
survives Windows AV/indexer contention and cross-device renames the way
config writes already did. cron/suggestions.json is 0600 from creation
(previously chmod'ed after the replace). Skipped on purpose: cron/jobs.py
two-phase staging, gateway/status._write_json_excl (create-only lock),
kanban_transfer staging (not atomic writers); tools/skill_usage.
_write_suppressed_names lives inside a PLUGIN-COMPAT block.
This commit is contained in:
teknium1
2026-09-12 20:12:12 -07:00
committed by Teknium
parent 2be8e6147a
commit 3ef8b384a9
33 changed files with 151 additions and 300 deletions
+2 -4
View File
@@ -27,6 +27,7 @@ import warnings
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Tuple
from utils import atomic_json_write
COMPAT_REMOVAL_DATE = _dt.date(2026, 9, 14)
COMPAT_REMOVAL = COMPAT_REMOVAL_DATE.isoformat()
@@ -247,10 +248,7 @@ def _write_report_file(report: Dict[str, List[Hit]]) -> None:
"written_at": _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="seconds"),
"plugins": {k: [h.__dict__ for h in v] for k, v in report.items()},
"lines": summary_lines(report)}
p.parent.mkdir(parents=True, exist_ok=True)
tmp = p.with_suffix(".tmp")
tmp.write_text(json.dumps(payload, indent=1), encoding="utf-8")
os.replace(tmp, p)
atomic_json_write(p, payload, indent=1)
except Exception:
pass