← back to Rejected Prompts Viewer
chore: lint, refactor, v1.2.1 (session close) — with-blocks, stderr on classify fail, non-list scan guard, corrected net comment
777dc1ef29a77e46f172f65c2ae817715fb94703 · 2026-08-19 11:43:18 -0700 · Steve Abrams
Files touched
A __pycache__/refresh.cpython-314.pycM package.jsonM refresh.py
Diff
commit 777dc1ef29a77e46f172f65c2ae817715fb94703
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 19 11:43:18 2026 -0700
chore: lint, refactor, v1.2.1 (session close) — with-blocks, stderr on classify fail, non-list scan guard, corrected net comment
---
__pycache__/refresh.cpython-314.pyc | Bin 0 -> 9059 bytes
package.json | 2 +-
refresh.py | 30 ++++++++++++++++++++++--------
3 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/__pycache__/refresh.cpython-314.pyc b/__pycache__/refresh.cpython-314.pyc
new file mode 100644
index 0000000..f7b6d08
Binary files /dev/null and b/__pycache__/refresh.cpython-314.pyc differ
diff --git a/package.json b/package.json
index e5dc254..34a658d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rejected-prompts-viewer",
- "version": "1.2.0",
+ "version": "1.2.1",
"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
old mode 100644
new mode 100755
index bb55456..01aa3a0
--- a/refresh.py
+++ b/refresh.py
@@ -4,7 +4,15 @@ Runs scan (Stage 1), then classifies ONLY candidates not already in the existing
data/rejections.json (reusing prior heretic verdicts by (file, ts) key so the slow
27B model is paid only for genuinely NEW rejections), then rebuilds data/rejections.json.
Designed to run unattended on a Mac2 schedule (launchd). $0 local."""
-import json, os, re, subprocess, sys, time, urllib.request, glob, datetime
+import datetime
+import glob
+import json
+import os
+import re
+import subprocess
+import sys
+import time
+import urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
CANDS = '/tmp/refusal_cands.json'
@@ -23,7 +31,7 @@ def classify(user, refusal):
body = json.dumps({"model": MODEL, "stream": False, "think": False,
"messages": [{"role": "system", "content": SYS},
{"role": "user", "content": f"USER REQUEST:\n{user}\n\nASSISTANT REPLY:\n{refusal}\n\nJSON:"}],
- "options": {"temperature": 0, "num_predict": 80}}).encode()
+ "options": {"temperature": 0, "num_predict": 80}}).encode() # 80 tokens sufficient for compact JSON verdict
r = urllib.request.urlopen(urllib.request.Request(OLLAMA_URL, body, {"Content-Type": "application/json"}), timeout=180)
txt = re.sub(r"<think>.*?</think>", "", json.loads(r.read())["message"]["content"], flags=re.DOTALL).strip()
mm = re.search(r"\{.*\}", txt, re.DOTALL)
@@ -33,13 +41,17 @@ def classify(user, refusal):
def main():
# Stage 1 — scan
subprocess.run([sys.executable, os.path.join(HERE, 'scan.py')], check=True)
- cands = json.load(open(CANDS))
+ with open(CANDS) as fh:
+ cands = json.load(fh)
+ if not isinstance(cands, list):
+ sys.exit(f"[refresh] unexpected scan output in {CANDS} (expected list, got {type(cands).__name__})")
# verdict cache from the existing data file (key = file + ts)
cache = {}
if os.path.exists(DATA):
- for it in json.load(open(DATA)).get('items', []):
- cache[(it.get('file'), it.get('ts'))] = it
+ with open(DATA) as fh:
+ for it in json.load(fh).get('items', []):
+ cache[(it.get('file'), it.get('ts'))] = it
# project map
basename_project = {}
@@ -58,12 +70,13 @@ def main():
else:
try:
v = classify(c['user'], c['refusal'])
- except Exception:
+ except Exception as e: # noqa: BLE001 — intentional resilience; KeyboardInterrupt/SystemExit still propagate
+ print(f"[warn] classify failed for {c.get('file')} ts={c.get('ts')}: {e}", file=sys.stderr)
v = {"refusal": False, "category": "other", "reason": "(unclassified — model unavailable)"}
conf, cat, rsn = bool(v.get('refusal')), v.get('category', 'other'), v.get('reason', '')
new += 1
items.append({'user': c['user'], 'refusal': c['refusal'], 'confirmed': conf,
- 'category': cat, 'reason': rsn, 'net': c.get('net', 'A'),
+ 'category': cat, 'reason': rsn, 'net': c.get('net', 'A'), # 'A' = Net A (keyword net); 'B' = tone-only net
'ts': c['ts'], 'file': c['file'],
'project': basename_project.get(c.get('file', ''), '—')})
items.sort(key=lambda x: (x['ts'] or ''), reverse=True)
@@ -74,7 +87,8 @@ def main():
'confirmed_count': sum(1 for i in items if i['confirmed']),
'items': items}
os.makedirs(os.path.dirname(DATA), exist_ok=True)
- json.dump(out, open(DATA, 'w'), indent=1)
+ with open(DATA, 'w') as fh:
+ json.dump(out, fh, indent=1)
print(f"[{out['generated']}] refresh: {len(items)} items ({out['confirmed_count']} confirmed), {new} newly classified, {time.time()-t0:.0f}s")
← b2b7cb8 feat: auto-update — incremental refresh.py + launchd KeepAli
·
back to Rejected Prompts Viewer
·
harden: atomic write (tmp+os.replace) in refresh.py + build- fde4c52 →