diff --git a/projects/gh-unnotifier/BUILD b/projects/gh-unnotifier/BUILD
deleted file mode 100644
index a820dab..0000000
--- a/projects/gh-unnotifier/BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-py_project(
-    name = "gh-unnotifier",
-    main = "src/python/ghunnotif/__main__.py",
-    main_deps = [
-        py_requirement("click"),
-        py_requirement("requests"),
-        py_requirement("pytimeparse"),
-    ]
-)
diff --git a/projects/gh-unnotifier/src/python/ghunnotif/__main__.py b/projects/gh-unnotifier/src/python/ghunnotif/__main__.py
deleted file mode 100644
index d4a7e44..0000000
--- a/projects/gh-unnotifier/src/python/ghunnotif/__main__.py
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-from pathlib import Path
-import tomllib
-from typing import Optional
-from datetime import datetime, timedelta, timezone
-from time import sleep
-
-import click
-import requests
-import pytimeparse
-
-class Client(object):
-    BASE = "https://api.github.com"
-    HEADERS = {"Accept": "application/vnd.github+json"}
-
-    def __init__(self, token):
-        self._token = token
-        self._session = requests.Session()
-        self._session.headers["Authorization"] = f"Bearer {token}"
-
-    def get_notifications(self, page=1):
-        resp = self._session.get(f"{self.BASE}/notifications", headers=self.HEADERS, params={"page": page, "all": "false"})
-        resp.raise_for_status()
-        return resp.json()
-
-    def get_all_notifications(self):
-        page = 1
-        while True:
-            results = self.get_notifications(page=page)
-            if not results:
-                return
-            yield from results
-            page += 1
-
-    def read(self, notif):
-        return self._session.patch(notif["url"]).raise_for_status()
-
-    def unsubscribe(self, notif):
-        return self._session.delete(notif["subscription_url"]).raise_for_status()
-
-    def get_pr(self,
-               url: Optional[str] = None,
-               repo: Optional[str] = None,
-               id: Optional[int] = None):
-        url = url or f"{self.BASE}/{repo}/pulls/{id}"
-        resp = self._session.get(url, headers=self.HEADERS)
-        resp.raise_for_status()
-        return resp.json()
-
-
-@click.group()
-def cli():
-    pass
-
-
-@cli.command()
-@click.option("--config", "config_path", type=Path, default=lambda: Path(os.getenv("BUILD_WORKSPACE_DIRECTORY", "")) / "projects/gh-unnotifier/config.toml")
-def oneshot(config_path: Path):
-    with open(config_path, "rb") as fp:
-        config = tomllib.load(fp)
-
-    client = Client(config["gh-unnotifier"]["api_key"])
-    for notif in client.get_all_notifications():
-        subject = notif["subject"]
-        pr = None
-        if "/pulls/" in subject["url"]:
-            pr = client.get_pr(url=subject["url"])
-            if pr["state"] == "closed":
-                client.read(notif)
-                client.unsubscribe(notif)
-
-                print("Resolved", notif["id"])
-            continue
-
-        print(notif["id"], notif["subscription_url"], notif["reason"], notif["subject"], pr)
-
-
-def parse_seconds(text: str) -> timedelta:
-    return timedelta(seconds=pytimeparse.parse(text))
-
-
-@cli.command()
-@click.option("--config", "config_path", type=Path, default=lambda: Path(os.getenv("BUILD_WORKSPACE_DIRECTORY", "")) / "projects/gh-unnotifier/config.toml")
-@click.option("--schedule", "schedule", default="15 seconds", type=parse_seconds)
-def maintain(config_path: Path, schedule: timedelta):
-    with open(config_path, "rb") as fp:
-        config = tomllib.load(fp)
-
-    client = Client(config["gh-unnotifier"]["api_key"])
-    mark = None
-    while True:
-        try:
-            prev = mark
-            mark = datetime.now(timezone.utc)
-            tick = mark + schedule
-            assert tick - schedule == mark
-            for notif in client.get_all_notifications():
-                subject = notif["subject"]
-
-                # Don't waste time on notifications which haven't changed since the last scrub
-                updated_at = datetime.fromisoformat(notif["updated_at"])
-                if prev and updated_at < prev:
-                    continue
-
-                pr = None
-                if "/pulls/" in subject["url"]:
-                    pr = client.get_pr(url=subject["url"])
-                    if pr["state"] == "closed":
-                        client.read(notif)
-                        client.unsubscribe(notif)
-
-                        click.echo(f"Resolved {notif['id']}")
-                    continue
-
-            click.echo("Napping...")
-            sleep((tick - datetime.now(timezone.utc)).total_seconds())
-
-        except KeyboardInterrupt:
-            break
-
-
-if __name__ == "__main__":
-    cli()
diff --git a/tools/python/requirements.in b/tools/python/requirements.in
index 7546ca0..f48906d 100644
--- a/tools/python/requirements.in
+++ b/tools/python/requirements.in
@@ -53,4 +53,3 @@ toml
 unify
 yamllint
 yaspin
-pytimeparse
diff --git a/tools/python/requirements_lock.txt b/tools/python/requirements_lock.txt
index e2c15b9..5c93185 100644
--- a/tools/python/requirements_lock.txt
+++ b/tools/python/requirements_lock.txt
@@ -1,26 +1,25 @@
-aiohttp==3.8.5
+aiohttp==3.8.4
 aiohttp-basicauth==1.0.0
 aiosignal==1.3.1
-aiosql==9.0
+aiosql==8.0
 alabaster==0.7.13
-annotated-types==0.5.0
-async-lru==2.0.4
+async-lru==2.0.2
 async-timeout==4.0.2
 attrs==23.1.0
 autocommand==2.2.2
-autoflake==2.2.0
+autoflake==2.1.1
 Babel==2.12.1
 beautifulsoup4==4.12.2
-black==23.7.0
+black==23.3.0
 blinker==1.6.2
 build==0.10.0
 cachetools==5.3.1
-certifi==2023.7.22
-charset-normalizer==3.2.0
+certifi==2023.5.7
+charset-normalizer==3.1.0
 cheroot==10.0.0
 CherryPy==18.8.0
-click==8.1.6
-colored==2.2.3
+click==8.1.3
+colored==1.4.4
 commonmark==0.9.1
 coverage==7.2.7
 decorator==5.1.1
@@ -29,54 +28,53 @@ docutils==0.20.1
 ExifRead==3.0.0
 flake8==6.0.0
 Flask==2.3.2
-frozenlist==1.4.0
-hypothesis==6.82.0
+frozenlist==1.3.3
+hypothesis==6.75.9
 ibis==3.2.0
 icmplib==3.0.3
 idna==3.4
 imagesize==1.4.1
-inflect==7.0.0
+inflect==6.0.4
 iniconfig==2.0.0
 isort==5.12.0
 itsdangerous==2.1.2
-jaraco.collections==4.3.0
+jaraco.collections==4.2.0
 jaraco.context==4.3.0
-jaraco.functools==3.8.0
-jaraco.text==3.11.1
+jaraco.functools==3.7.0
+jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d
 jedi==0.18.2
 Jinja2==3.1.2
-jsonschema==4.18.4
-jsonschema-spec==0.2.3
-jsonschema-specifications==2023.7.1
-lark==1.1.7
+jsonschema==4.17.3
+jsonschema-spec==0.1.4
+lark==1.1.5
 lazy-object-proxy==1.9.0
 libsass==0.22.0
 livereload==2.6.3
-lxml==4.9.3
-Markdown==3.4.4
+lxml==4.9.2
+Markdown==3.4.3
 MarkupSafe==2.1.3
 mccabe==0.7.0
-meraki==1.34.0
+meraki==1.33.0
 mirakuru==2.5.1
 mistune==2.0.5
-more-itertools==10.0.0
+more-itertools==9.1.0
 multidict==6.0.4
 mypy-extensions==1.0.0
 octorest==0.4
-openapi-schema-validator==0.6.0
-openapi-spec-validator==0.6.0
+openapi-schema-validator==0.4.4
+openapi-spec-validator==0.5.6
 packaging==23.1
 parso==0.8.3
 pathable==0.4.3
 pathspec==0.11.1
 picobox==3.0.0
 pip==23.1.2
-pip-tools==7.1.0
-platformdirs==3.9.1
-pluggy==1.2.0
-port-for==0.7.1
-portend==3.2.0
-prompt-toolkit==3.0.39
+pip-tools==6.13.0
+platformdirs==3.5.1
+pluggy==1.0.0
+port-for==0.6.3
+portend==3.1.0
+prompt-toolkit==3.0.38
 proquint==0.2.1
 psutil==5.9.5
 psycopg==3.1.9
@@ -85,34 +83,30 @@ pudb==2022.1.3
 py==1.11.0
 pycodestyle==2.10.0
 pycryptodome==3.18.0
-pydantic==2.1.1
-pydantic_core==2.4.0
+pydantic==1.10.8
 pyflakes==3.0.1
 Pygments==2.15.1
 pyproject_hooks==1.0.0
 pyrsistent==0.19.3
-pytest==7.4.0
+pytest==7.3.1
 pytest-cov==4.1.0
 pytest-postgresql==5.0.0
 pytest-pudb==0.7.0
 pytest-timeout==2.1.0
-pytimeparse==1.1.8
 pytz==2023.3
-PyYAML==6.0.1
+PyYAML==6.0
 recommonmark==0.7.1
-redis==4.6.0
-referencing==0.29.3
+redis==4.5.5
 requests==2.31.0
 retry==0.9.2
 rfc3339-validator==0.1.4
-rpds-py==0.9.2
-setuptools==68.0.0
+setuptools==67.7.2
 six==1.16.0
 smbus2==0.4.2
 snowballstemmer==2.2.0
 sortedcontainers==2.4.0
 soupsieve==2.4.1
-Sphinx==7.1.1
+Sphinx==7.0.1
 sphinx_mdinclude==0.5.3
 sphinxcontrib-applehelp==1.0.4
 sphinxcontrib-devhelp==1.0.2
@@ -123,19 +117,19 @@ sphinxcontrib-openapi==0.8.1
 sphinxcontrib-programoutput==0.17
 sphinxcontrib-qthelp==1.0.3
 sphinxcontrib-serializinghtml==1.1.5
-tempora==5.5.0
+tempora==5.2.2
 termcolor==2.3.0
 toml==0.10.2
 tornado==6.3.2
-typing_extensions==4.7.1
+typing_extensions==4.6.3
 unify==0.5
 untokenize==0.1.1
-urllib3==2.0.4
+urllib3==2.0.2
 urwid==2.1.2
 urwid-readline==0.13
 wcwidth==0.2.6
-websocket-client==1.6.1
-Werkzeug==2.3.6
+websocket-client==1.5.2
+Werkzeug==2.3.4
 wheel==0.40.0
 yamllint==1.32.0
 yarl==1.9.2