From a053e9d1ecbf583b4a6f054c34f64ad1f359365e Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 23 Jul 2026 23:57:58 -0500 Subject: fix(inbox-send): clean error on an unreadable source, dedupe overlapping roots main caught ValueError and FileNotFoundError around the send, so an unreadable source raised a PermissionError as a raw traceback instead of the clean "inbox-send: " every other failure produces. Widening the catch to OSError covers the unreadable source, the missing source, and any atomic-write failure alike. discover_projects appended without deduping, so a roots config naming both a parent directory and one of its children listed the same project at two indices. It now dedupes on the resolved path, first-seen order preserved. --- .ai/scripts/inbox-send.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to '.ai/scripts/inbox-send.py') diff --git a/.ai/scripts/inbox-send.py b/.ai/scripts/inbox-send.py index 5787a84..663efcb 100755 --- a/.ai/scripts/inbox-send.py +++ b/.ai/scripts/inbox-send.py @@ -70,17 +70,28 @@ def discover_projects(roots: list[Path]) -> list[Path]: a specific project root (included directly if it qualifies). """ projects: list[Path] = [] + seen: set[Path] = set() + + def _add(p: Path) -> None: + # Dedupe on the resolved path: a roots config naming both a parent and + # one of its children would otherwise list the child project twice, at + # two different indices. + key = p.resolve() + if key not in seen: + seen.add(key) + projects.append(p) + for root in roots: if not root.is_dir(): continue if _is_project(root): - projects.append(root) + _add(root) continue for child in sorted(root.iterdir()): if not child.is_dir(): continue if _is_project(child): - projects.append(child) + _add(child) return projects @@ -345,7 +356,10 @@ def main() -> int: else: assert args.file is not None dest = send_file(target_inbox, args.file, source_name, args.name, now) - except (ValueError, FileNotFoundError) as exc: + except (ValueError, OSError) as exc: + # OSError covers FileNotFoundError (missing source), PermissionError + # (unreadable source), and any atomic-write failure — all should + # surface as the clean "inbox-send: " error, never a traceback. print(f"inbox-send: {exc}", file=sys.stderr) return 1 -- cgit v1.2.3