Skip to content

fix(installer): a missed sign-in code costs one prompt, not a whole re-run (cli#517) - #738

Merged
LukasWodka merged 1 commit into
developfrom
fix/517-device-login-retry
Aug 18, 2026
Merged

fix(installer): a missed sign-in code costs one prompt, not a whole re-run (cli#517)#738
LukasWodka merged 1 commit into
developfrom
fix/517-device-login-retry

Conversation

@LukasWodka

@LukasWodka LukasWodka commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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 login failing was fatal to provision_client:

tracebloc login </dev/tty >/dev/tty 2>/dev/tty || error "Sign-in didn't complete — re-run the installer to try again."

error() is exit 1, so a device code that lapsed — the ordinary outcome when
the 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_in gives the sign-in one in-place retry:

  • offered only when there is a live terminal to hand a fresh code to
    (_prompt_tty);
  • abandoned on a failed read (EOF / no live input) — re-prompting cannot fix
    that, exactly as the name prompt already reasons;
  • a second failure is evidence of something other than a missed code, so it
    falls through to the same fatal error as before. SIGN_IN_ATTEMPTS is
    declared once and used as both the loop bound and the "is there a next
    attempt?" test, so the two cannot drift into disagreeing.
⚠ Sign-in didn't complete — the code lapsed or wasn't approved.
  Nothing is lost: the install is paused right here, not restarted.

  Press Enter for a fresh code (or Ctrl-C to stop):

The contradicting advice (#517 §3)

The sign-in now runs with TRACEBLOC_INSTALLER=1, which makes the CLI drop its
own run `tracebloc login` recovery line. Under the installer that advice was
actively 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 create and every later CLI
call see the environment they always did. A test pins that.

Seam, not behaviour

The /dev/tty openability probe moved into _login_tty_ok, body unchanged
({ : </dev/tty; } 2>/dev/null). It exists purely so the tests can force either
branch without a controlling terminal — reusing _prompt_tty there broke four
existing name-prompt tests, which force it on while having no real /dev/tty to
redirect a child process onto.

scripts/manifest.sha256 regenerated via scripts/gen-manifest.sh.

Evidence

Eight mutations, each asserting the anchor was unique and applied before running
the test — all 8 caught, none vacuous:

CAUGHT  no retry at all (the cli#517 bug itself)          'retried in place'
CAUGHT  retry loop is unbounded, not one try              'second failure is fatal'
CAUGHT  the last-attempt guard drifts from the bound      'second failure is fatal'
CAUGHT  retry offered with no terminal to prompt on       'no terminal there is no retry'
CAUGHT  a failed read (EOF) keeps looping, not stopping   'EOF on the retry prompt'
CAUGHT  the CLI is not told the installer is driving it   'told the installer is driving it'
CAUGHT  TRACEBLOC_INSTALLER exported, not scoped          'does not leak past the sign-in'
CAUGHT  the fatal advice points at the CLI, not installer 'second failure is fatal'

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:

  • the last-attempt guard was invisible to an attempt count — letting the loop
    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.
  • the _prompt_tty gate was masked by an unreadable TB_TTY: without the
    gate 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 gate
    can produce the single attempt.
  • the attempt bound lived in two places (the loop list and a hardcoded 2),
    so mutating either was inert. Both now read SIGN_IN_ATTEMPTS.

Test plan

  • make check — green
  • make bats — 1069/1069, 0 failures
  • bash scripts/gen-manifest.sh re-run (provision.sh changed)
  • 8/8 mutations caught, anchors verified applied
  • the four pre-existing name-prompt tests still green (they were the tripwire
    for the _prompt_tty reuse)

🤖 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 login during 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_login with TRACEBLOC_INSTALLER=1 as a command prefix (not exported) so the CLI suppresses its own “run tracebloc login” line, which would contradict the installer’s guidance. _login_tty_ok is split from _prompt_tty so 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.sha256 is 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.

…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 saadqbal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@LukasWodka
LukasWodka merged commit 8be3815 into develop Aug 18, 2026
49 checks passed
@LukasWodka
LukasWodka deleted the fix/517-device-login-retry branch August 18, 2026 06:31
LukasWodka added a commit that referenced this pull request Aug 18, 2026
#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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants