fix(forge): an expired sign-in isn't renewed where the new token can't be saved, so a sandboxed run no longer loses it #855
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!855
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:fix/forge-renew-where-saved"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: fix · Risk: low · Tested on: Linux (CachyOS), no GPU involved · Needs: none
Why
The git.magicbean.dev sign-in (forge.py, git and jj pushes, the team chat) kept being lost, and people had to run
forge.py loginagain several times a day. One way to lose it: an agent's command ran in a sandbox that makes~/.config/longshotread-only. When the token had run out, forge.py renewed it with Forgejo, then couldn't save thenew one. With Forgejo's default, the old refresh token was then used up, and every later run failed with "token was
already used". Even where it didn't fail, it told you to sign in again when there was nothing wrong with the saved
sign-in.
The main cause, #435 (one OAuth grant per user, so a
sign-in elsewhere, two renewals at once or a lost answer signs you out), is fixed on the server, not here:
git.magicbean.dev now has
INVALIDATE_REFRESH_TOKENS = false(2026-11-15, MagicBeanDev/Infraf1cdc2ea), so a refreshtoken can be used again until it expires. This PR is the client side: don't renew where the result can't be kept, and
say so plainly.
See #435.
What changed
Before renewing an expired token,
forge_session()checks that the config folder can be written. Where it can't, itsays why and returns without asking Forgejo, so the saved sign-in is kept for the next run that can save it. forge.py
says "the saved sign-in could not be used (why is above)" instead of "not signed in, run login" when a sign-in is saved.
scripts/magicbean_login.py:cannot_save()(makes and removes a probe file, aswrite_privatewould);forge_session()calls it before the lock and the renewal;_SessionLock's note says the server now takes refreshtokens again.
scripts/forge.py:not_signed_in()fortoken()and the credential helper'sget;loginrefuses to start whereit couldn't save the result.
tests/test_forge_session_lock.py:test_no_renewal_where_it_cannot_be_saved: a read-only config folder with anexpired sign-in. Nothing is renewed (the stand-in for Forgejo is never asked), the message is printed and
forge.jsonis unchanged. Skipped on Windows and as root, where a 0o500 folder is still writable.Measurements
Nothing to measure: a script and its test, nothing the game runs.
Flags and settings
None
Testing
Tested: On Linux, after the server change: two copies of one expired sign-in each renewed from the same refresh
token. Both came back as HumanBean, so a reused refresh token now works.
py_compileof the three files. I didn't runtests/run_all.pyon this PC (this PC runs no test suites). CI runs it, with the new test.Not tested: Windows (the probe is a plain
os.openandos.remove, the same callswrite_privatemakes), and areal sandboxed run with an expired token (the new test covers that case).
Must be tested:
Risks
What could break: a config folder that can be written but where the probe fails for another reason would stop
renewals there. The message names the folder and the error.
Turning it off: revert the commit.
Compatibility: Nothing changes:
forge.jsonis the same, and an existing sign-in keeps working.Issues
See #435
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). (CI runs it: not run on this PC)third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.🤖 Generated with Claude Code
c32d78688c42a50203a5Pushed
42a50203: the first push (c32d7868) took in 7 host/ and nx2/ files from another session's change (game title and icon), which shared my workspace. They're out, so the diff is only scripts/forge.py, scripts/magicbean_login.py and tests/test_forge_session_lock.py.WIP: fix(forge): an expired sign-in isn't renewed where the new token can't be saved, so a sandboxed run no longer loses itto fix(forge): an expired sign-in isn't renewed where the new token can't be saved, so a sandboxed run no longer loses it