[object Object]

← back to Rejected Prompts Viewer

harden: atomic write (tmp+os.replace) in refresh.py + build-data.py so an unattended launchd kill can't truncate the data file; v1.2.2

fde4c529fc76462118255b891102c52438e77564 · 2026-08-19 12:00:52 -0700 · Steve Abrams

Files touched

Diff

commit fde4c529fc76462118255b891102c52438e77564
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 19 12:00:52 2026 -0700

    harden: atomic write (tmp+os.replace) in refresh.py + build-data.py so an unattended launchd kill can't truncate the data file; v1.2.2
---
 __pycache__/build-data.cpython-314.pyc | Bin 4197 -> 4430 bytes
 __pycache__/refresh.cpython-314.pyc    | Bin 9059 -> 9179 bytes
 build-data.py                          |   5 ++++-
 data/rejections.json                   |   4 ++--
 package.json                           |   2 +-
 refresh.py                             |   5 ++++-
 6 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/__pycache__/build-data.cpython-314.pyc b/__pycache__/build-data.cpython-314.pyc
index fc64563..204225d 100644
Binary files a/__pycache__/build-data.cpython-314.pyc and b/__pycache__/build-data.cpython-314.pyc differ
diff --git a/__pycache__/refresh.cpython-314.pyc b/__pycache__/refresh.cpython-314.pyc
index f7b6d08..5b13872 100644
Binary files a/__pycache__/refresh.cpython-314.pyc and b/__pycache__/refresh.cpython-314.pyc differ
diff --git a/build-data.py b/build-data.py
index 1246a92..1ba8b59 100644
--- a/build-data.py
+++ b/build-data.py
@@ -50,5 +50,8 @@ out = {
     'items': items,
 }
 os.makedirs(os.path.join(HERE, 'data'), exist_ok=True)
-json.dump(out, open(os.path.join(HERE, 'data', 'rejections.json'), 'w'), indent=1)
+_data = os.path.join(HERE, 'data', 'rejections.json')
+with open(_data + '.tmp', 'w') as _fh:          # atomic write — never leave a truncated data file
+    json.dump(out, _fh, indent=1)
+os.replace(_data + '.tmp', _data)
 print(f"wrote {len(items)} items ({confirmed_count} confirmed + {len(items)-confirmed_count} borderline), scanned {scanned} transcripts -> data/rejections.json")
diff --git a/data/rejections.json b/data/rejections.json
index b599984..960ffab 100644
--- a/data/rejections.json
+++ b/data/rejections.json
@@ -1,6 +1,6 @@
 {
- "generated": "2026-08-19T11:37:15.634026-07:00",
- "scanned_files": 5197,
+ "generated": "2026-08-19T12:00:36.768055-07:00",
+ "scanned_files": 5201,
  "total_candidates": 86,
  "confirmed_count": 15,
  "items": [
diff --git a/package.json b/package.json
index 34a658d..e0bab9d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "rejected-prompts-viewer",
-  "version": "1.2.1",
+  "version": "1.2.2",
   "private": true,
   "description": "Web viewer of prompts Claude declined on copyright/trademark/website-ToS/IP grounds, mined from Claude Code transcripts.",
   "scripts": {
diff --git a/refresh.py b/refresh.py
index 01aa3a0..505cda7 100755
--- a/refresh.py
+++ b/refresh.py
@@ -87,8 +87,11 @@ def main():
            'confirmed_count': sum(1 for i in items if i['confirmed']),
            'items': items}
     os.makedirs(os.path.dirname(DATA), exist_ok=True)
-    with open(DATA, 'w') as fh:
+    # atomic write: a launchd SIGTERM/timeout mid-dump must never truncate the viewer's only data file
+    tmp = DATA + '.tmp'
+    with open(tmp, 'w') as fh:
         json.dump(out, fh, indent=1)
+    os.replace(tmp, DATA)
     print(f"[{out['generated']}] refresh: {len(items)} items ({out['confirmed_count']} confirmed), {new} newly classified, {time.time()-t0:.0f}s")
 
 

← 777dc1e chore: lint, refactor, v1.2.1 (session close) — with-blocks,  ·  back to Rejected Prompts Viewer  ·  harden: fix contrarian HOLE 1 (bare :9858 substring over-exc 2904d3f →