fix(installer): a missed sign-in code costs one prompt, not a whole re-run (cli#517) - #738
Conversation
…e-run cli#517. `tracebloc login` failing was fatal to provision_client, so a device code that lapsed — the ordinary outcome when the human approving it is reading a phone — threw away every step the installer had already completed and asked for a full re-run. _device_sign_in wraps the call in the same shape the name prompt has used since the 2026-07-09 type-ahead report: one in-place retry, offered only when there is a live terminal to hand a fresh code to, and abandoned on a failed read (EOF / no live input) because re-prompting cannot fix that. A second failure is evidence of something other than a missed code, so it falls through to the same fatal error as before. The sign-in now runs with TRACEBLOC_INSTALLER=1 (a command prefix, not an export — no later CLI call inherits it), which makes the CLI drop its own "run `tracebloc login`" line. Under the installer that advice was actively wrong: a bare login leaves the client mint and the Helm install undone. The installer's "re-run the installer" is now the only next step printed. The /dev/tty openability probe moves into _login_tty_ok, unchanged, purely so the tests can force either branch without a controlling terminal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
saadqbal
left a comment
There was a problem hiding this comment.
Clean, careful PR 👍
The retry loop holds up on every direction I traced: bounded at one in-place retry via SIGN_IN_ATTEMPTS (used as both the loop bound and the next-attempt test, so they can't drift), it breaks before prompting on the final attempt so the offer shows exactly once, and both the no-tty and EOF-on-read paths fall straight through to the fatal error rather than spinning. _run_device_login && return 0 keeps the login failure on the left of &&, so a lapsed code can't trip set -e in install-k8s.sh. The retry read just discards Enter into _ — nothing to leak, the CLI still owns the device-code entry. Nice touch pinning TRACEBLOC_INSTALLER as a command prefix (not an export) and testing that it doesn't leak past the sign-in.
Manifest hash matches the new provision.sh; all 32 provision.bats pass locally.
#738 and #739 landed; #738 also touches common.sh. Code merged cleanly and all three of this branch's changes are intact (_docker_answers, _assess_handle_runtime_down, error()'s BASH_SOURCE[1] record). manifest.sha256 conflicted again, as it must when two branches edit libs. Regenerated over the merged tree rather than hand-resolved. This merge is also the fix for the missing CI, and my earlier diagnosis of that was wrong. I blamed a dropped push event from the 08-17 outage. The evidence says otherwise: another PR on this repo got a full run set at 08:03 UTC, well after my 07:40 push. What my branch had that it did not was mergeable_state DIRTY — and `pull_request` workflows run against the merge ref, which GitHub cannot create for a conflicted PR, so no runs fire at all. Not a dropped event, and not something a force-push or a PR reopen would have fixed. The empty commit 72c529e was therefore a no-op against the real cause; it stays as history rather than being amended away, since amending means force-pushing.
Part 2 of tracebloc/cli#517 — pairs with tracebloc/cli#521, which fixes the
CLI half. The two are meant to land together.
What was wrong
tracebloc loginfailing was fatal toprovision_client:error()isexit 1, so a device code that lapsed — the ordinary outcome whenthe human approving it is reading a phone, against a hard ten-minute deadline —
threw away every step the installer had already completed and asked for a full
re-run.
The pattern to copy was already in the same file: the client-name prompt retries
three times, with a comment explaining why an empty read must not abort the
install. The step with the ten-minute deadline had no such treatment.
The change
_device_sign_ingives the sign-in one in-place retry:(
_prompt_tty);that, exactly as the name prompt already reasons;
falls through to the same fatal error as before.
SIGN_IN_ATTEMPTSisdeclared once and used as both the loop bound and the "is there a next
attempt?" test, so the two cannot drift into disagreeing.
The contradicting advice (#517 §3)
The sign-in now runs with
TRACEBLOC_INSTALLER=1, which makes the CLI drop itsown
run `tracebloc login`recovery line. Under the installer that advice wasactively wrong — a bare login leaves the client mint and the Helm install undone
— and it printed one line above the installer's own "re-run the installer". Now
the installer's is the only next step on screen.
It is a command prefix, not an export:
client createand every later CLIcall see the environment they always did. A test pins that.
Seam, not behaviour
The
/dev/ttyopenability probe moved into_login_tty_ok, body unchanged(
{ : </dev/tty; } 2>/dev/null). It exists purely so the tests can force eitherbranch without a controlling terminal — reusing
_prompt_ttythere broke fourexisting name-prompt tests, which force it on while having no real
/dev/ttytoredirect a child process onto.
scripts/manifest.sha256regenerated viascripts/gen-manifest.sh.Evidence
Eight mutations, each asserting the anchor was unique and applied before running
the test — all 8 caught, none vacuous:
Three of these only became non-vacuous after the first mutation run showed them
inert, and each fix is a real strengthening rather than a re-aimed mutation:
prompt after its final try changes nothing countable, so the test now asserts
the retry is offered exactly once: the user must never be asked to press
Enter for a code that is never fetched.
_prompt_ttygate was masked by an unreadableTB_TTY: without thegate the loop stopped on the failed read instead, and the assertion could not
tell the two apart. That test now uses a readable
TB_TTY, so only the gatecan produce the single attempt.
2),so mutating either was inert. Both now read
SIGN_IN_ATTEMPTS.Test plan
make check— greenmake bats— 1069/1069, 0 failuresbash scripts/gen-manifest.shre-run (provision.sh changed)for the
_prompt_ttyreuse)🤖 Generated with Claude Code
Note
Medium Risk
Changes the interactive provisioning path in the installer (sign-in failure handling and CLI env), which is user-critical but narrowly scoped shell logic with extensive bats coverage.
Overview
Fixes cli#517: a failed or lapsed
tracebloc loginduring provisioning no longer aborts the whole installer. Sign-in is wrapped in_device_sign_in, which allows one in-place retry when a terminal is available (warn/hint + “Press Enter for a fresh code”), matching the existing client-name prompt pattern. Without a TTY, or after a second failure, or on EOF on the retry read, behavior stays fatal with “re-run the installer” as the only recovery advice.Login runs through
_run_device_loginwithTRACEBLOC_INSTALLER=1as a command prefix (not exported) so the CLI suppresses its own “runtracebloc login” line, which would contradict the installer’s guidance._login_tty_okis split from_prompt_ttyso tests can stub TTY behavior without breaking name-prompt tests.SIGN_IN_ATTEMPTS(default 2) ties the loop bound and “last attempt” guard together.scripts/manifest.sha256is updated; provision.bats adds tests for retry, no-retry without TTY, EOF, and env scoping.Reviewed by Cursor Bugbot for commit b79cca9. Bugbot is set up for automated code reviews on this repo. Configure here.