diff --git a/projects/gh-unnotifier/src/python/ghunnotif/__main__.py b/projects/gh-unnotifier/src/python/ghunnotif/__main__.py
index b4624ec..2cc1e2a 100644
--- a/projects/gh-unnotifier/src/python/ghunnotif/__main__.py
+++ b/projects/gh-unnotifier/src/python/ghunnotif/__main__.py
@@ -6,7 +6,7 @@ import tomllib
 from typing import Optional
 from datetime import datetime, timedelta, timezone
 from time import sleep
-from pprint import pformat
+from pprint import pprint
 
 import click
 import requests
@@ -149,8 +149,6 @@ def parse_seconds(text: str) -> timedelta:
     "--config",
     "config_path",
     type=Path,
-    default=lambda: Path(os.getenv("BUILD_WORKSPACE_DIRECTORY", ""))
-    / "projects/gh-unnotifier/config.toml",
 )
 @click.option("--schedule", "schedule", default="30 seconds", type=parse_seconds)
 def maintain(config_path: Path, schedule: timedelta):
@@ -160,72 +158,107 @@ def maintain(config_path: Path, schedule: timedelta):
     client = Client(config["gh-unnotifier"]["api_key"])
     org_shitlist = config["gh-unnotifier"].get("org_shitlist", [])
     team_shitlist = config["gh-unnotifier"].get("team_shitlist", [])
+    author_shitlist = config["gh-unnotifier"].get("author_shitlist", [])
     user = client.get_user()
     user_teams = {it["slug"] for it in client.get_user_teams()}
-    mark = None
+    mark = savepoint = prev = None
 
     def _resolve(notif, reason):
         client.read(notif)
         client.unsubscribe(notif)
         click.echo(f"Resolved {notif['id']} {reason} ({notif['subject']})")
 
+    def _pr(subject, notif):
+        pr = client.get_pr(url=subject["url"])
+
+        if pr.get("merged", False):
+            _resolve(notif, "Merged")
+            return
+
+        if pr.get("state") == "closed":
+            _resolve(notif, "Closed")
+            return
+
+        if pr["user"]["login"] in author_shitlist:
+            _resolve(notif, "Ignoring PR by author")
+            return
+
+        if notif["reason"] == "review_requested":
+            reviewers = client.get_pr_reviewers(pr)
+            if (
+                any(org in subject["url"] for org in org_shitlist)
+                and not any(
+                    it["login"] == user["login"]
+                    for it in reviewers.get("users", [])
+                )
+                and not any(
+                    it["slug"] in user_teams
+                    and it["slug"] not in team_shitlist
+                    for it in reviewers.get("teams", [])
+                )
+            ):
+                _resolve(notif, "Reviewed")
+                return
+
+        print("Oustanding PR notification")
+        pprint({"subject": subject, "notif": notif, "pr": pr})
+
+    def _mention(subject, notif):
+        pr = client.get_pr(url=subject["url"])
+
+        reviewers = client.get_pr_reviewers(pr)
+        if (
+            any(org in subject["url"] for org in org_shitlist)
+            and not any(
+                it["login"] == user["login"]
+                for it in reviewers.get("users", [])
+            )
+            and not any(
+                it["slug"] in user_teams
+                and it["slug"] not in team_shitlist
+                for it in reviewers.get("teams", [])
+            )
+        ):
+            _resolve(notif, "Ignoring team mention")
+            return
+
+    def _issue(subject, notif):
+        issue = client.get_issue(url=subject["url"])
+
+        if issue["state"] == "closed":
+            comments = client.get_comments(url=issue["comments_url"])
+            if not any(
+                it["user"]["login"] == user["login"] for it in comments
+            ):
+                _resolve(notif, "Unengaged issue closed")
+
+        if issue["user"]["login"] in author_shitlist:
+            _resolve(notif, "Ignoring issue by author")
+
     while True:
         try:
+            savepoint = prev
             prev = mark
             mark = datetime.now(timezone.utc)
             tick = mark + schedule
             assert tick - schedule == mark
             for notif in client.get_all_notifications(since=prev):
+                notif_timestamp = datetime.fromisoformat(notif["updated_at"])
+                if (mark - notif_timestamp).days > 3:
+                    _resolve(notif, "More than 3 days old")
+                    continue
+
                 subject = notif["subject"]
 
-                pr = None
-                if subject["type"] == "PullRequest":
-                    if notif["reason"] == "review_requested":
-                        pr = client.get_pr(url=subject["url"])
+                match subject["type"].lower():
+                    case "pullrequest":
+                        _pr(subject, notif)
 
-                        reviewers = client.get_pr_reviewers(pr)
-                        if (
-                            any(org in subject["url"] for org in org_shitlist)
-                            and not any(
-                                it["login"] == user["login"]
-                                for it in reviewers.get("users", [])
-                            )
-                            and not any(
-                                it["slug"] in user_teams
-                                and it["slug"] not in team_shitlist
-                                for it in reviewers.get("teams", [])
-                            )
-                        ):
-                            _resolve(notif, "Reviewed")
-                            continue
+                    case "mention":
+                        _mention(subject, notif)
 
-                    elif notif["reason"] == "team_mention":
-                        pr = client.get_pr(url=subject["url"])
-
-                        reviewers = client.get_pr_reviewers(pr)
-                        if (
-                            any(org in subject["url"] for org in org_shitlist)
-                            and not any(
-                                it["login"] == user["login"]
-                                for it in reviewers.get("users", [])
-                            )
-                            and not any(
-                                it["slug"] in user_teams
-                                and it["slug"] not in team_shitlist
-                                for it in reviewers.get("teams", [])
-                            )
-                        ):
-                            _resolve(notif, "Ignoring team mention")
-                            continue
-
-                elif subject["type"] == "Issue":
-                    issue = client.get_issue(url=subject["url"])
-                    if issue["state"] == "closed":
-                        comments = client.get_comments(url=issue["comments_url"])
-                        if not any(
-                            it["user"]["login"] == user["login"] for it in comments
-                        ):
-                            _resolve(notif, "Unengaged issue closed")
+                    case "issue":
+                        _issue(subject, notif)
 
             duration = (tick - datetime.now(timezone.utc)).total_seconds()
             if duration > 0:
@@ -234,6 +267,11 @@ def maintain(config_path: Path, schedule: timedelta):
         except KeyboardInterrupt:
             break
 
+        except requests.exceptions.HTTPError as e:
+            print("Encoutered exception", e, "backing off")
+            prev = savepoint
+            sleep(schedule.total_seconds())
+
 
 if __name__ == "__main__":
     cli()
diff --git a/projects/tentacles/src/python/tentacles/workers.py b/projects/tentacles/src/python/tentacles/workers.py
index e3f500c..a1cb34d 100644
--- a/projects/tentacles/src/python/tentacles/workers.py
+++ b/projects/tentacles/src/python/tentacles/workers.py
@@ -70,7 +70,7 @@ def poll_printers(app: App, db: Db) -> None:
         def _set_status(status: str):
             if printer.status != status:
                 log.info(f"Printer {printer.id} {printer.status} -> {status}")
-            db.update_printer_status(pid=printer.id, status=status)
+                db.update_printer_status(pid=printer.id, status=status)
 
         def _bed_clear():
             if not (
@@ -117,7 +117,7 @@ def poll_printers(app: App, db: Db) -> None:
                 if mapped_job:
                     db.start_job(jid=mapped_job.id)
 
-            elif printer_job.get("state").lower() == "connecting":
+            elif printer_job.get("state", "").lower() == "connecting":
                 _set_status("connecting")
 
             elif printer_state.get("ready"):
@@ -171,8 +171,8 @@ def assign_jobs(app: App, db: Db) -> None:
                 db.assign_job(jid=job.id, pid=printer.id)
                 log.info(f"Mapped job {job.id} to printer {printer.id}")
                 break
-            else:
-                print("Could not map\n", job, "\n", printer)
+        else:
+            log.info(f"Could not map job {job!r}")
 
 
 def push_jobs(app: App, db: Db) -> None:
@@ -307,7 +307,8 @@ def analyze_files(app: App, db: Db):
     for file in db.list_unanalyzed_files():
         p = Path(file.path)
         if not p.is_file():
-            log.error(f"Invalid file {file.id}!")
+            log.error(f"Deleting missing file {file.id}!")
+            db.delete_file(uid=file.user_id, fid=file.id)
             continue
 
         record = analyze_gcode_file(p)
diff --git a/tools/python/requirements.in b/tools/python/requirements.in
index 7546ca0..c88d77b 100644
--- a/tools/python/requirements.in
+++ b/tools/python/requirements.in
@@ -54,3 +54,4 @@ unify
 yamllint
 yaspin
 pytimeparse
+git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d#egg=jaraco.text
diff --git a/tools/python/requirements_lock.txt b/tools/python/requirements_lock.txt
index e2c15b9..912d645 100644
--- a/tools/python/requirements_lock.txt
+++ b/tools/python/requirements_lock.txt
@@ -42,7 +42,7 @@ itsdangerous==2.1.2
 jaraco.collections==4.3.0
 jaraco.context==4.3.0
 jaraco.functools==3.8.0
-jaraco.text==3.11.1
+jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d
 jedi==0.18.2
 Jinja2==3.1.2
 jsonschema==4.18.4