feat: Catalog Console -- maintainer-only review + signing tool (#62) #66

Merged
the_og merged 2 commits from feat/62-catalog-console into main 2026-07-12 18:04:54 -04:00
Showing only changes of commit d6fc6845c4 - Show all commits
+26 -9
View File
@@ -15,10 +15,16 @@ Flow: Load -> Review -> Sign.
blob SHA for the rest of this review pass.
2. Review -- a semantic diff (catalog_review.diff_catalogs), one card per
changed entry, with risk annotations
(catalog_review.entry_risk_findings) and a live registry
lookup. Every changed entry must be individually
acknowledged (its checkbox ticked) before Sign unlocks.
There is no "acknowledge all" -- see catalog_review.py.
(catalog_review.entry_risk_findings). A registry lookup for
each entry's npm/PyPI package kicks off automatically, one
worker thread per entry, the moment the cards are built --
it is the one check a reviewer can't do by eye, so it must
never depend on a click. It fails soft (a dead registry
shows "unavailable", never blocks review or Sign) and a
per-card "Re-check" button covers manual retries. Every
changed entry must be individually acknowledged (its
checkbox ticked) before Sign unlocks. There is no
"acknowledge all" -- see catalog_review.py.
3. Sign -- re-fetches the current blob SHA and refuses to sign unless
it still matches the pinned SHA from step 1 (TOCTOU fix:
catalog_review.can_sign). On success, writes
@@ -497,11 +503,11 @@ class EntryCard(QWidget):
flabel.setStyleSheet("color: #1565c0;")
layout.addWidget(flabel)
self.registry_label = plain_label("Registry lookup: not checked yet.")
self.registry_label = plain_label("Registry lookup: loading...")
layout.addWidget(self.registry_label)
check_btn = QPushButton("Check registry")
check_btn.clicked.connect(self._run_registry_lookup)
layout.addWidget(check_btn)
recheck_btn = QPushButton("Re-check")
recheck_btn.clicked.connect(self._run_registry_lookup)
layout.addWidget(recheck_btn)
self.blocking = any(f.severity == "blocking" for f in findings)
self.checkbox = QCheckBox(
@@ -519,13 +525,24 @@ class EntryCard(QWidget):
)
layout.addWidget(self.checkbox)
# Registry lookup is the one check a reviewer can't do by eye -- it's
# what catches a typosquatted/hijacked package (it already caught
# firecrawl-mcp in the seed data). It must run automatically as soon
# as the card exists, not wait on a click a tired maintainer might
# skip at 11pm. Off the GUI thread (RegistryLookupWorker is a
# QThread) and fails soft: a dead/slow registry can never gate
# review or signing, it just leaves this entry's lookup showing
# "unavailable". The "Re-check" button above stays for retrying a
# failed/unavailable lookup by hand.
self._run_registry_lookup()
def _run_registry_lookup(self):
entry = self.change.new or {}
refs = review.extract_package_refs(entry)
if not refs:
self.registry_label.setText("Registry lookup: no npm/PyPI package in this entry.")
return
self.registry_label.setText("Registry lookup: checking...")
self.registry_label.setText("Registry lookup: loading...")
self._worker = RegistryLookupWorker(refs, self._all_entry_ids)
self._worker.done.connect(self._on_registry_result)
self._worker.start()