| awk '$1 == "origin" && $3 == "(fetch)" {print $2; exit}' \
Root cause: foreman signal scan passed a hardcoded/board-assumed repo (totalwindupflightsystems/Kobayashi-Maru) to gh run list/gh issue list. When the origin moved to dexdat/Kobayashi-Maru, gh 404s on the stale name and the scan silently returns nothing.
Fix: derive the canonical owner/repo from git remote -v (fetch URL) before every scan, then pass it via -R <actual>. Fall back to the assumed name only if there's no fetch remote, with a visible warning instead of a silent 404.
Core function (foreman/lib/gh-repo.sh):
gh_repo_from_remote() {
local url=""
# 1. Prefer origin's fetch URL; fall back to any fetch remote.
url="$(git remote -v 2>/dev/null \
| awk '$1 == "origin" && $3 == "(fetch)" {print $2; exit}' \
|| true)"
[[ -z "$url" ]] && url="$(git remote -v 2>/dev/null \
| awk '$3 == "(fetch)" {print $2; exit}' || true)"
[[ -z "$url" ]] && return 1
local path owner repo
if [[ "$url" == *"://"* ]]; then
# URL form: scheme://host[:port]/owner/repo[.git]
[[ "$url" =~ ^[a-zA-Z][a-zA-Z0-9+.-]*://[^/]+/([^/]+/[^/]+) ]] || return 1
path="${BASH_REMATCH[1]}"
else
# scp-style: [user@]host:owner/repo[.git]
[[ "$url" =~ ^([^/@]+@)?[^/:]+:([^/]+/[^/]+) ]] || return 1
path="${BASH_REMATCH[2]}"
fi
path="${path%/}"; path="${path%.git}"
owner="${path%%/*}"; repo="${path#*/}"
[[ -n "$owner" && -n "$repo" && "$repo" != "$owner" ]] || return 1
printf '%s/%s\n' "$owner" "$repo"
}
Fixed scan pattern (drop-in replacement for the stale hardcode):
SIGNAL_REPO="$(gh_repo_for_scan totalwindupflightsystems/Kobayashi-Maru)" # verifies git remote first
gh run list -R "$SIGNAL_REPO" --limit 5
gh issue list -R "$SIGNAL_REPO" --limit 5
gh_repo_for_scan warns on drift (repo moved: assumed X, using Y from git remote), so staleness is visible, and falls back to the assumed name only when no fetch remote exists.
Verified with a **stub `gh` that reproduces the real failure mode** — it prints `HTTP 404: Not Found` and exits 1 unless `-R` matches the actual repo. Test harness: `foreman/tests/run-tests.sh` (18/18 passing, `HARNESS_EXIT=0`), plus a real-git smoke test against `/tmp/pi` (`git remote -v` → `earendil-works/pi`). **Regression reproduced first** (test 7): `gh run list -R totalwindupflightsystems/Kobayashi-Maru` → rc=1 (404). **Fixed** (tests 8–10): verified repo → rc=0, runs + issues listed. | Edge case | Result | |---|---| | `https://github.com/dexdat/Kobayashi-Maru.git` | `dexdat/Kobayashi-Maru` | | `<email>:dexdat/Kobayashi-Maru.git` (scp-style) | `dexdat/Kobayashi-Maru` | | `git://github.com/dexdat/Kobayashi-Maru` (no `.git`) | `dexdat/Kobayashi-Maru` | | Trailing slash / extra path (`/tree/main/ci`) | `dexdat/Kobayashi-Maru` | | `ssh://<email>:2222/...` (port) | `dexdat/Kobayashi-Maru` (port doesn't leak into owner) | | Credentials in URL (`https://user:token@host/...`) | `dexdat/Kobayashi-Maru` | | Dotted repo name (`dexdat/repo.name.git`) | `dexdat/repo.name` | | Remote named `upstream` (not origin) | still picked up | | No remote → falls back to assumed repo, warning emitted | rc=0, warning visible | | No remote + no assumed → hard failure | rc=1 (caller surfaces it) | | Malformed URL | rc=1 | Notably, during development the harness caught two real bugs in my first attempt (host leaking into owner for `https://`/`git://` URLs; test-stub arg parsing) — both fixed and re-verified.
{"model": "deepseek-v4-flash", "problem_class": "ci-gh-repo-name-verification", "result": "passed", "tests": 18}