[object Object]

← back to Rejected Prompts Viewer

chore: lint, refactor, v1.3.1 (session close) — clearer names, with-blocks, type hints, +x; behavior/schema unchanged

58fa36f8bccaa105334420b15964bb8548f860bb · 2026-08-19 12:43:52 -0700 · Steve Abrams

Files touched

Diff

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

    chore: lint, refactor, v1.3.1 (session close) — clearer names, with-blocks, type hints, +x; behavior/schema unchanged
---
 build-data.py | 25 +++++++++++++------------
 package.json  |  2 +-
 refresh.py    | 31 ++++++++++++++++---------------
 scan.py       | 49 +++++++++++++++++++++++++------------------------
 4 files changed, 55 insertions(+), 52 deletions(-)

diff --git a/build-data.py b/build-data.py
old mode 100644
new mode 100755
index 1ba8b59..6d80ab9
--- a/build-data.py
+++ b/build-data.py
@@ -13,19 +13,20 @@ CLASSIFIED = sys.argv[1] if len(sys.argv) > 1 else '/tmp/refusal_classified.json
 PROJROOT = os.path.expanduser('~/.claude/projects')
 
 # map transcript basename -> readable project name
-basename_project = {}
-for d in glob.glob(os.path.join(PROJROOT, '*')):
-    proj = os.path.basename(d)
+basename_project: dict[str, str] = {}
+for proj_dir in glob.glob(os.path.join(PROJROOT, '*')):
+    proj_base = os.path.basename(proj_dir)
     # -Users-macstudio3-Projects-designerwallcoverings -> designerwallcoverings
-    readable = proj.split('-Projects-')[-1] if '-Projects-' in proj else proj.lstrip('-')
-    for f in glob.glob(os.path.join(d, '**', '*.jsonl'), recursive=True):
+    readable = proj_base.split('-Projects-')[-1] if '-Projects-' in proj_base else proj_base.lstrip('-')
+    for f in glob.glob(os.path.join(proj_dir, '**', '*.jsonl'), recursive=True):
         basename_project[os.path.basename(f)] = readable
 
 try:
-    rows = json.load(open(CLASSIFIED))
+    with open(CLASSIFIED) as fh:
+        rows = json.load(fh)
 except (FileNotFoundError, json.JSONDecodeError) as e:
     sys.exit(f"ERROR: cannot read {CLASSIFIED}: {e}")
-items = []
+items: list[dict] = []
 for r in rows:
     items.append({
         'user': r.get('user', ''),
@@ -50,8 +51,8 @@ out = {
     'items': items,
 }
 os.makedirs(os.path.join(HERE, 'data'), exist_ok=True)
-_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")
+out_path = os.path.join(HERE, 'data', 'rejections.json')
+with open(out_path + '.tmp', 'w') as fh:          # atomic write — never leave a truncated data file
+    json.dump(out, fh, indent=1)
+os.replace(out_path + '.tmp', out_path)
+print(f"wrote {len(items)} items ({confirmed_count} confirmed + {len(items) - confirmed_count} borderline), scanned {scanned} transcripts -> data/rejections.json")
diff --git a/package.json b/package.json
index e3048a6..b55178b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "rejected-prompts-viewer",
-  "version": "1.3.0",
+  "version": "1.3.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
index b6fd186..6312be1 100755
--- a/refresh.py
+++ b/refresh.py
@@ -27,7 +27,7 @@ SYS = ("You audit an AI coding assistant's replies. Given the USER request and t
 '{"refusal":true|false,"category":"copyright|trademark|ip-impersonation|website-tos|other","reason":"<=12 words"}')
 
 
-def classify(user, refusal):
+def classify(user: str, refusal: str) -> dict:
     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:"}],
@@ -38,7 +38,7 @@ def classify(user, refusal):
     return json.loads(mm.group(0)) if mm else {"refusal": False, "category": "other", "reason": "parse-fail"}
 
 
-def main():
+def main() -> None:
     # Stage 1 — scan
     subprocess.run([sys.executable, os.path.join(HERE, 'scan.py')], check=True)
     with open(CANDS) as fh:
@@ -46,22 +46,23 @@ def main():
     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 = {}
+    # verdict cache from the existing data file (key = (file, ts))
+    cache: dict[tuple, dict] = {}
     if os.path.exists(DATA):
         with open(DATA) as fh:
-            for it in json.load(fh).get('items', []):
-                cache[(it.get('file'), it.get('ts'))] = it
+            for cached_item in json.load(fh).get('items', []):
+                cache[(cached_item.get('file'), cached_item.get('ts'))] = cached_item
 
-    # project map
-    basename_project = {}
-    for d in glob.glob(os.path.join(PROJROOT, '*')):
-        readable = os.path.basename(d).split('-Projects-')[-1] if '-Projects-' in os.path.basename(d) else os.path.basename(d).lstrip('-')
-        for f in glob.glob(os.path.join(d, '**', '*.jsonl'), recursive=True):
+    # project dir basename -> readable project name
+    basename_project: dict[str, str] = {}
+    for proj_dir in glob.glob(os.path.join(PROJROOT, '*')):
+        proj_base = os.path.basename(proj_dir)
+        readable = proj_base.split('-Projects-')[-1] if '-Projects-' in proj_base else proj_base.lstrip('-')
+        for f in glob.glob(os.path.join(proj_dir, '**', '*.jsonl'), recursive=True):
             basename_project[os.path.basename(f)] = readable
 
-    items = []
-    new = 0
+    items: list[dict] = []
+    new_count = 0
     t0 = time.time()
     for c in cands:
         prev = cache.get((c.get('file'), c.get('ts')))
@@ -76,7 +77,7 @@ def main():
                 v = {"refusal": False, "category": "other", "reason": "(unclassified — model unavailable)"}
                 err = True  # flag so this item is NOT cached-reused — it re-classifies next run until the model answers
             conf, cat, rsn = bool(v.get('refusal')), v.get('category', 'other'), v.get('reason', '')
-            new += 1
+            new_count += 1
         items.append({'user': c['user'], 'refusal': c['refusal'], 'confirmed': conf,
                       'category': cat, 'reason': rsn, 'net': c.get('net', 'A'),  # 'A' = Net A (keyword net); 'B' = tone-only net
                       'error': err, 'ts': c['ts'], 'file': c['file'],
@@ -94,7 +95,7 @@ def main():
     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")
+    print(f"[{out['generated']}] refresh: {len(items)} items ({out['confirmed_count']} confirmed), {new_count} newly classified, {time.time() - t0:.0f}s")
 
 
 if __name__ == '__main__':
diff --git a/scan.py b/scan.py
old mode 100644
new mode 100755
index ccbf506..ae5f35b
--- a/scan.py
+++ b/scan.py
@@ -29,12 +29,13 @@ capability = re.compile(r"(from (in )?here|from the shell|from this session|for
 # META filter: this build's OWN session discusses refusals/copyright at length — exclude self-referential noise
 META = re.compile(r"(:9858|rejected-prompts|refusal_cands|refusal_classified|borderline candidate|heretic model|\bNet [AB]\b|classify\.py|build-data|scan\.py|policy declines|decline text|two-signal|capability limit)", re.IGNORECASE)
 
-seen = set()
-cands = []
+seen: set[tuple[str, str]] = set()
+cands: list[dict] = []
 for f in glob.glob(os.path.join(PROJROOT, '**', '*.jsonl'), recursive=True):
     try:
-        raw = open(f, errors='ignore').read()
-    except Exception:
+        with open(f, errors='ignore') as fh:
+            raw = fh.read()
+    except Exception:  # noqa: BLE001,S112
         continue
     # file-level exclusion: skip any transcript that is ABOUT building this tool (self-pollution)
     # specific signatures only — bare ':9858' matched token counts like ':985835' and
@@ -42,32 +43,31 @@ for f in glob.glob(os.path.join(PROJROOT, '**', '*.jsonl'), recursive=True):
     if any(sig in raw for sig in ('rejected-prompts-viewer', 'http://127.0.0.1:9858',
             'rejected.agentabrams', 'refusal_cands', 'refusal_classified', 'qwen3.8-27b-heretic')):
         continue
-    lines = raw.splitlines()
-    recent_users = []  # rolling last-USER_TURNS user turns for context (HOLE 2)
-    for l in lines:
-        if '"user"' not in l and '"assistant"' not in l:
+    recent_users: list[str] = []  # rolling last-USER_TURNS user turns for context (HOLE 2)
+    for line in raw.splitlines():
+        if '"user"' not in line and '"assistant"' not in line:
             continue
         try:
-            d = json.loads(l)
-        except Exception:
+            d = json.loads(line)
+        except Exception:  # noqa: BLE001
             continue
-        t, m = d.get('type'), d.get('message')
-        if not isinstance(m, dict):
+        msg_type, msg = d.get('type'), d.get('message')
+        if not isinstance(msg, dict):
             continue
-        if t == 'user':
-            c = m.get('content')
+        if msg_type == 'user':
+            content = msg.get('content')
             txt = None
-            if isinstance(c, str):
-                txt = c
-            elif isinstance(c, list):
-                txt = ' '.join(x.get('text', '') for x in c if isinstance(x, dict) and x.get('type') == 'text')
+            if isinstance(content, str):
+                txt = content
+            elif isinstance(content, list):
+                txt = ' '.join(x.get('text', '') for x in content if isinstance(x, dict) and x.get('type') == 'text')
             if txt and txt.strip():
                 recent_users.append(txt.strip())
-                del recent_users[:-USER_TURNS]  # keep only the last USER_TURNS
-        elif t == 'assistant':
-            for it in (m.get('content') or []):
-                if isinstance(it, dict) and it.get('type') == 'text':
-                    tx = it.get('text', '')
+                recent_users = recent_users[-USER_TURNS:]  # keep only the last USER_TURNS
+        elif msg_type == 'assistant':
+            for block in (msg.get('content') or []):
+                if isinstance(block, dict) and block.get('type') == 'text':
+                    tx = block.get('text', '')
                     if len(tx) >= MAX_ASST_LEN:
                         continue
                     netA = decline.search(tx) and reason.search(tx)
@@ -87,5 +87,6 @@ for f in glob.glob(os.path.join(PROJROOT, '**', '*.jsonl'), recursive=True):
                                   'net': 'A' if netA else 'B'})
                     break
 
-json.dump(cands, open('/tmp/refusal_cands.json', 'w'), indent=1)
+with open('/tmp/refusal_cands.json', 'w') as fh:
+    json.dump(cands, fh, indent=1)
 print(f"CANDIDATES: {len(cands)}  by-net={dict(Counter(c['net'] for c in cands))} -> /tmp/refusal_cands.json")

← ae2a0e7 chore: gitignore __pycache__/*.pyc (auto-snapshot picked up  ·  back to Rejected Prompts Viewer  ·  auto-data-snapshot: 2026-08-19T16:13:26 (1 data files) — dat 46f0a28 →