Repository navigation
wacli sign-in: a paired wacli reads as signed in, "already signed in" instead of waiting, and a sign-in that shows nothing ends with a reason (#98) - #100
Conversation
…nal names it (#98) The wacli sign-in dialog could stay on "Starting the sign-in…" forever: the page awaited the start and poll requests without a deadline, showed the same sentence as status and detail, and a tool that printed no QR code was bounded only by the 5-minute pairing lifetime. Nothing reached the Launchpad's journal. - login.ts: a sign-in without a challenge within one minute is killed and fails as `no-challenge`; start and end of every sign-in go to an optional journal (tool, outcome, fixed reason code only). - server.ts: the Launchpad journals them as JSON lines on stdout. - Dialog: status and detail never repeat one sentence; each request is awaited at most 45 s; not-installed, spawn-failed, tool-exit and no-challenge say what to do (cs/en). - Route tests with stand-in wacli executables: QR late on stderr without a terminal, QR on stderr only, exit at once, silent and running, missing from PATH. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s "already signed in" (#98) On the Machine, wacli was paired before the attempt: `wacli auth --events` connected without any `qr_code` event, and the Launchpad listed it as not signed in. The catalog probe read `authenticated`, `phone` and `linked_jid` at the top level of `wacli auth status --json`, but wacli 0.18.2 wraps every JSON answer in {"success","data","error"} (internal/out WriteJSON), so every paired wacli read as signed out and the sign-in waited for a challenge that never comes. - catalog.ts: the wacli probe reads data.authenticated, data.phone, data.linked_jid. - login.ts: `connected` without a challenge first, confirmed by the probe, ends the session as signed-in with `already: true`; `connected` also suspends the no-challenge bound. - Dialog and CLI say "already signed in"; the card state is re-read. - Stand-ins print wacli's real envelope; a route test replays the observed event stream of an already paired wacli. - Docs and the F19 addendum name the cause. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — exact-head review of 754b969. Changes requested for one reproducible failure of the promised bounded sign-in.
[P2] Keep the no-challenge deadline when connected is not confirmed — src/tools/login.ts:983-992.
The timer exempts every session that has merely emitted connected, even if the read-only status probe continues to say signed out or unknown and no challenge has appeared. A wacli stand-in that emits connected and then remains alive with auth status --json returning {success:true,data:{authenticated:false},error:null} still returns pending after the configured 500 ms challenge bound (observed at 800 ms through the actual /api/tools/login/start and /api/tools/login/poll routes); the expected failed/no-challenge assertion fails. With production defaults, this leaves the dialog on “Starting the sign-in…” and the process running until the five-minute lifetime, contradicting the one-minute termination and journal outcome promised for a sign-in with no challenge. Keep F19's status-probe confirmation as the only path to signed-in, but let an unconfirmed connected session with no QR/pair code hit the challenge deadline and kill its process group. Add this route-level regression alongside the silent-tool test; update the exception in the docs and F19 addendum accordingly.
The primary repair otherwise checks out: wacli v0.18.2 source wraps auth status --json under success/data/error, with authenticated and the optional phone/linked_jid inside data; the tests now use that envelope. A confirmed connected ends as already: true, while a QR challenge is delivered from stderr. The journal emits structured tool/event/outcome/fixed reason only, the account is confined to the existing signed-in surfaces, and redact.ts is untouched. The Czech/English dialog copy and 45-second start/poll response bounds are present. The documentation correctly discloses that real pairing and a browser exercise of this head have not been verified; its unconfirmed-connected exception is the specific mismatch above.
Local bun run check passed on the pristine 754b969 checkout: 1033 passed, 7 skipped, lint/typecheck/public guard/proof build/smoke passed. Both exact-head CI jobs, ubuntu-24.04 and macos-14, passed. The negative route probe above was a temporary test in my isolated clone only, not pushed or part of the PR. No merge performed.
…n 20 s (#98 review) At 754b969 a wacli that reported `connected` while its status check never confirmed, and that showed no code, switched the no-challenge bound off and stayed pending until the 5-minute lifetime. Only the check confirms a sign-in, so now: 20 s after the first `connected` without a code (retried every 2 s), and at the latest at the one-minute bound, the process group is killed and the session fails as `not-confirmed`. A session that has shown a code keeps its pairing lifetime. The not-confirmed sentence no longer claims the tool ended by itself. Route test with a stand-in whose status check keeps answering not signed in. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — re-review on exact head 731eaae against 754b969: APPROVED. The sole defect from review 5358791450 is fixed. The unconfirmed connected path now keeps the no-challenge deadline and additionally fails as not-confirmed 20 s after its first connected event without a challenge; only the status probe can confirm sign-in. A shown QR or pairing code retains the pairing lifetime. The new actual Launchpad start/poll route test observes not-confirmed inside the bound, process termination, and the journal outcome; all seven wacli route tests pass. CLI/Launchpad wording and F19/docs reflect the bound. Local bun run check passed on the isolated exact-head clone: 1034 passed, 7 skipped, lint/typecheck/public guard/proof build/smoke green. Both exact-head CI check jobs (Ubuntu 24.04 and macOS 14) succeeded; no unresolved review threads. No real WhatsApp pairing or browser exercise claimed. No merge performed.
…t wording Conflicts resolved so both sides stay: - src/launchpad/messages.ts: #100's longer not-installed sentence kept, in the new wording (en and cs); the other hunks take this branch's wording. #100's new strings follow the rule too: already signed in (2 en, 2 cs) and the no-challenge sentence (en, cs). - src/tools/curated-cli.ts: #100's no-challenge and already-signed-in sentences say Environment. - docs/decisions.md: main's entries unchanged (F19 addendum of #98, F25, F26), F27 after them. - src/folder/render.ts, tests/folder-tools.test.ts: main is still at base-instructions-14; this branch stays base-instructions-15. - src/modules/module-cli.ts and the snapshots: main's content equals this branch's base there; this branch's side taken, snapshots unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
What the operator saw
On a hosted personal Machine (Launchpad 0.1.8-rc.7 as a systemd user unit), Settings → Tools listed
wacli0.18.2 as "Nepřihlášeno" (not signed in) with a "Přihlásit" button. The operator clicked it. The dialog showed the step "Waiting for you: in progress" and the sentence "Starting the sign-in…" twice, as the status line and as the detail, and no QR code ever appeared. Afterwards the Launchpad's journal had no line about the sign-in. The same dialog signed in tocomposiowithout a problem.Proven cause on the Machine: wacli was already signed in, and the Platform could not see it
Observed on the Machine with the operator's account, with the unit's PATH and HOME, no TTY and stdin
/dev/null(only event kinds and shapes were recorded, never values):wacli auth status --jsonanswers{"success":true,"data":{"authenticated":true,"linked_jid":…,"phone":…},"error":null}.wacli auth --events --idle-exit 30semitsauth_starting,warning,connected(0.8 s),offline_sync_completed,warning×2 andapp_state_recovery_requested, thenidle_exit(33 s), one plain text line, and exit status 0. There is noqr_codeevent, because the store was paired before.What the code did with that:
wacli auth status --json --read-only. It is run from the Launchpad on page load and on refresh (signIn: true), for 10 s, with only PATH/HOME/XDG. It readauthenticated,phoneandlinked_jidat the top level of the answer (src/tools/catalog.tswaclisignInProbe,flag: ["authenticated"]). wacli 0.18.2 wraps every--jsonanswer in{"success","data","error"}(internal/out/out.go,WriteJSON). Soat(document, ["authenticated"])wasundefined, andreadSignInreturnedsigned-out(src/tools/status.ts:351). Every paired wacli therefore showed as "Nepřihlášeno"; the state was "not signed in", not "unknown", which shows the probe did run. The test stand-ins had used the same top-level shape, so the tests never caught it.connectedtriggered the probe, and the probe said "not yet" because of point 1. The session then waited for a QR code that an already-paired wacli never prints.main, a stand-in that replays the observed event stream ends asfailedwithnot-confirmedonce the process exits. On the Machine that is after about 33 s.Further hang paths, fixed as additional hardening
startLogin(src/launchpad/tools-panel.ts:346-348) puttoolsLoginStartinginto both the status line and the body.src/launchpad/ui.ts:231-243), and Close cannot cancel a session whose handle the page never got (tools-panel.ts:683).src/tools/login.tswrote nothing on a start or an end.The QR delivery itself was never broken: with
--events, wacli printsqr_codeas NDJSON on stderr whatever the TTY, and the Launchpad reads stderr. The tests prove this without a terminal.What changes
data.authenticatedmust betrue; the account label isdata.phone, elsedata.linked_jid. Asuccess:falseanswer, or the old top-level shape, is not signed in.connectedwithout a QR or pairing code first ends the sign-in, once the probe confirms it, assigned-inwithalready: true, usually within a second. The dialog says "{name} was already signed in on this Machine (as {account}); nothing was paired or changed." (Czech and English) and reads the card state again. wacli finishes its own sync to its idle exit in the background, as after a pairing. The CLI says the same.connectedalso ends with a reason: only the check confirms a sign-in. If it has not confirmed 20 s afterconnected(it is retried every 2 s), and always within the one-minute limit, and no code was shown, the process group is killed and the sign-in ends asfailedwithnot-confirmed. A session that has shown a QR or pairing code keeps its 5-minute pairing lifetime, as before. Thenot-confirmedsentence now says the sign-in was not completed and was stopped, instead of "the tool ended".connected, is killed and ends asfailedwith the fixed codeno-challenge. gh's "Link SSH key" is not affected; its steps have their own bounds.{"scope":"tools-login","tool":"wacli","event":"end","outcome":"failed","reason":"no-challenge"}. It never carries output, a challenge, a phone number or a JID.not-installed,spawn-failed,tool-exitandno-challengenow say what to do next.signInLabel). It never reaches the journal.docs/environment-tools.md: the probe shape, "already signed in",no-challengeand the journal lines.docs/launchpad-development.md: the dialog.docs/decisions.md, because how a session ends and what the probe reads changed.What does not change
The
waclicommand lines, the QR drawing, the lifetimes, the composio and gh flows, the redaction rules insrc/tools/redact.ts, and the rule that a challenge never reaches a log. No new process wrapper, pty or second state machine.How it was verified
New route tests in
tests/launchpad-wacli-sign-in.test.tsdrive/api/tools/login/start|poll|canceland/api/tools/statusagainst stand-inwacliexecutables. HOME and XDG live in temporary directories, there is no network, and all values are fictional. The stand-ins:signed-in, already: truein under 5 s, the status route then reads signed in, and the process exits on its own idle;tool-exit);no-challengewithin the bound, process killed);not-installed);connectedwhile its status check keeps answering not signed in (the reviewer's case). It ends asnot-confirmedwithin the bound, and the process is killed.Each test also checks the journal (start and end, no payload, no phone number).
The probe's shapes are in
tests/tools.test.ts: signed in with phone and JID, JID only, not authenticated, a non-boolean flag, the former top-level shape, and the error envelope on stderr. The shared fakewacliintests/fixtures/fake-login-tools.tsnow prints the real envelope.View tests in
tests/tools-view.test.ts: the status and the detail differ in both languages,no-challengeandalreadyare parsed and have their sentences, and a request that never settles is bounded.On unchanged
main, the already-paired replay ends asnot-confirmed, the silent stand-in stays pending, and the others fail on the journal checks.bun run checkpasses on731eaae: lint (332 files), typecheck,bun test1034 pass, 7 skip, 0 fail (1041 tests, 118 files), check:public PASS (389 files), proof build and smoke PASS. Two earlier runs under heavy machine load (load average about 30) each had 2 unrelated tests hit the 5 s bun timeout (in recover, git-checkout and legacy-paths); each of those passes when run alone.What could NOT be verified
None of this was run with the real
waclibinary, and no real WhatsApp pairing was done on a real Machine. Specifically:main, have ended the dialog asnot-confirmedafter about 33 s. The report only describes the "Starting the sign-in…" phase, and that difference is not explained by anything available here. The journal line added by this PR will name the outcome of the next attempt.Closes #98
🤖 Generated with Claude Code