An empty ls-remote advertisement cannot tell "merged and deleted upstream"
from "never pushed". The source check now re-pins a missing branch to main
only when the local branch was once published (remote-tracking ref or a
configured upstream) and git cherry finds no commits missing from main.
Otherwise it keeps the branch and reports branch-local-only.
Diagnosis from #105053 by @0xalydev and #105055 by @kokhlo, which targeted
the Electron healer since moved into hermes_cli/source_check.py.
Fixes#105042
The refusal matrix no longer carries ("main", "missing"). State the
intended behaviour at the source-check level instead: a 404 for
releases/channels/main.json yields no error, probes the configured
branch exactly as a published source-branch channel would, and leaves
the Desktop branch setting untouched.
1dd9a9955d made a missing releases/channels/main.json resolve to the
main branch on purpose (main IS the source branch; its record can only
add a retirement), and pinned that in
test_source_channel_integration.py. The older refusal matrix in
test_source_check.py still expected `release-unavailable` for
("main", "missing") and failed with `fetch-failed` on the Python lane.
The case contradicts the intended behaviour; stable/canary/preview
refusals stay covered.
The passive source check moved into hermes_cli/source_check.py during the
pm/ rewrite, and with it the GitHub calls left the desktop — but the four
main fixes that lived on the desktop side (gh-CLI token fallback
c61a11474b, anonymous retry e79ba2d7f2, rate-limit headers
c7d4d32800, failure copy 66a13703b3) did not follow. `_request` sent no
Authorization, so every client behind one NAT/VPN exit shared the anonymous
60/hour budget, and `_branch_tip` collapsed every failure into "Could not
resolve the remote branch tip." — a 403 rate limit read as a Hermes bug.
hermes_cli/github_api.py owns the credential ladder (GITHUB_TOKEN, GH_TOKEN,
`gh auth token` cached per process, anonymous; a 401 on a token retries
anonymously) and the failure copy (rate limit with reset time and the
GITHUB_TOKEN remedy, 5xx as GitHub trouble, else the status/transport
error). `_branch_tip` returns that reason and check_for_updates puts it in
the message.
apps/desktop/electron/github-api-auth.ts had no caller left (the desktop no
longer talks to api.github.com) — removed with its test; the behaviour now
lives where the request is made.
- tui_gateway/test_protocol: import hermes_bootstrap before the MagicMock
sys.modules window (PM activation reads the real install root at boot).
- test_source_check serves R2 records over loopback, so it opts out of the
local source-branch channel stub (real_release_channels).
- browser_use_cli: discovery/install tests target the PM seams (python_tool /
ensure_python_tool) instead of the retired shutil.which/uvx/managed-bin path.
- test_update_stage: reset tempfile's cached tempdir so TMPDIR is observed.
- test_update_finish: the selected interpreter is a symlink to the relocatable
build; a copied binary cannot find its stdlib.
- drop test_desktop_half_installed_get_windows (node-deps.mjs owns npm ci).
Use the exact target root and profile for branch, release, and cache decisions. Keep the desktop as a transport and handoff adapter. Remove the competing TypeScript checker and switch the banner, dashboard, and updater count consumers.
Preserve fork origins, unknown counts, publication checks, old-probe recovery, and official SSH branch healing through public HTTPS. Keep the historical unstamped-root policy unchanged.