Login lane returns its outcome; the wizard stops reading its prose (LLP 0179) - #593
Login lane returns its outcome; the wizard stops reading its prose (LLP 0179)#593platypii wants to merge 2 commits into
Conversation
…LP 0179)
The wizard classified a failed join by substring-matching three exported
English sentences out of the login lane's captured stderr, which made
user-facing text load-bearing. remoteLogin() now returns { exitCode, reason },
runRemoteLogin is the exit-code adapter over it, and classifyLoginFailure and
printJoinFailure switch on the reason code. The three message constants are
module-local again. Output and exit codes are unchanged.
…unLogin doc Assert the reason on the three post-auth failure paths the outcome return names but nothing tested (seed, enroll, daemon install); each one's reason could be swapped for another and the suite stayed green. RunWizardJoinOptions.runLogin still documented a captured stderr as what classifyLoginFailure reads, which this branch is exactly what changes. Co-Authored-By: Claude <noreply@anthropic.com>
PR #593 (created earlier) also adds an llp/0179-*, and the duplicate-numbers CI check validates each PR against master alone, so both pass and whichever merges second lands a duplicate number. This PR yields: 0180 is free repo-wide (master's highest is 0178). Mechanical renumber only, per CLAUDE.md ("renumbering that does not change meaning" is a permitted edit to an Accepted doc). Every updated reference means this PR's decision; none refer to #593's separate 0179 (the login lane returning its outcome). Also repairs the @ref in test/core/walkthrough-backfill.test.js, which had an empty gloss and a blank line between it and the test it annotates (a blank line breaks attachment, per CLAUDE.md). Co-Authored-By: Claude <noreply@anthropic.com>
Review: findings (no blockers)The refactor is faithful. Every prose branch has a code, exit codes and terminal output are preserved return for return, and no failure path can return Old-behavior enumerationTwo places read the login lane's prose on
Nothing lost. The three message constants had no consumer outside the wizard, so de-exporting them breaks nothing. Failure and edge paths. All four D7 codes come out of Faithfulness. Exit codes and output. All 18 converted returns checked against Findings1. minor, and the one worth acting on -
Not pushed, because deleting 2. minor - 3. minor - 4. nit - 5. nit - stale 6. nit - LLP docs
The Revert tests
Three did not fail: swapping Pushed (
|
philcunliffe
left a comment
There was a problem hiding this comment.
Requesting changes on one small point; the full review is in the thread. No blockers, and the refactor itself is faithful: I enumerated every prose branch the wizard used to take and each has a corresponding outcome, with exit codes and terminal output preserved return for return.
The ask: llp/0179-...decision.md (§no-prose-control-flow) justifies keeping the stderr tee by saying WizardJoinResult.detail echoes the lane's explanation to the user. It does not. detail is set at join.js:93 and read by nobody once this PR removes printJoinFailure's read of it. What actually surfaces the lane's output is the tee's write-through to ctx.stderr, which would happen without capturing at all. So teeWriter and detail are now dead weight, and the doc records a reason for keeping them that does not hold. types.d.ts:186-190 says the same thing.
Worth fixing before merge specifically because the doc becomes immutable on acceptance, and this would freeze an incorrect rationale. Either drop detail and the tee, or reword to "kept as an unread diagnostic field".
Everything else is optional: a dropped @ref LLP 0058#d7 on classifyLoginFailure, a --token-file read failure reporting login_failed where the sibling empty-token case correctly says usage, and three nits.
Also pushed 7cf0a43 under neutral:adopt: your "post-auth failures name their step" test only asserted store_failed, so swapping seed_failed/enroll_failed/daemon_incomplete for any other reason left the suite green. It now asserts all four and pins exitCode: 3 on daemon_incomplete.
…t contributions (LLP 0177/0180) (#594) * The init picker enables OpenClaw but never attaches it: issue LLP 0177 filed The finale's attach lane is generic; the clientsPicked list feeding it is a hardcoded claude/codex pair in two call sites, so a picked OpenClaw lands enabled-but-unattached with the sweep already recording - the limbo LLP 0175's investigation started from. Fix direction: derive picked clients from manifest client contributions; sibling of LLP 0174's manual-path consent design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * The wizard finale attaches OpenClaw: clientsPicked derives from client contributions (LLP 0177/0179) Picking OpenClaw in hyp init enabled the adapter and then dropped it from the finale: no attach, no import, a client_attach_missing limbo. Both clientsPicked sites now derive the client list from the picked rows' manifest client contributions instead of a hardcoded claude/codex pair, so the finale attaches OpenClaw like any other client and a future adapter joins by declaring contributes.client. The backfill consent question stays honest for a sweep-backed provider: its history imports on the daemon sweep schedule regardless of any answer (LLP 0170), so the finale discloses that and runs the first import instead of asking, and the question names only the providers the answer can control. The openclaw-gateway-restart instruction rides the adapter's attach output (LLP 0169), no wizard copy needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Renumber this PR's decision LLP 0179 -> 0180; repair an orphaned @ref PR #593 (created earlier) also adds an llp/0179-*, and the duplicate-numbers CI check validates each PR against master alone, so both pass and whichever merges second lands a duplicate number. This PR yields: 0180 is free repo-wide (master's highest is 0178). Mechanical renumber only, per CLAUDE.md ("renumbering that does not change meaning" is a permitted edit to an Accepted doc). Every updated reference means this PR's decision; none refer to #593's separate 0179 (the login lane returning its outcome). Also repairs the @ref in test/core/walkthrough-backfill.test.js, which had an empty gloss and a blank line between it and the test it annotates (a blank line breaks attachment, per CLAUDE.md). Co-Authored-By: Claude <noreply@anthropic.com> * An adapterless client contribution is not applicable to the attach lane, not failed (review of #594) Addresses the requested change and both test gaps from the review: - Picking Claude Desktop no longer prints a false attach failure. Its plugin contributes a client for skill/agent ownership but deliberately registers no runtime adapter (LLP 0115#no-attach-on-join), so the finale records it as noAdapter with ok: true and the run summary prints nothing for it; a registered adapter that throws still reports a real failure. LLP 0180#decision now states that not every client contribution is attachable, and names the row-to-plugin-to-clients fan-out invariant. - buildPickerBackfillRunner is exported and pinned against the real provider contributions, so sweeping: [] (or a sweep rename) now fails a test instead of riding through green CI. - The full derived clientsPicked set is pinned against the bundled catalog: claude, claude-desktop, codex, openclaw. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * LLP 0180 cites LLP 0115 in its decision; list it in Related and References --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: test <test@example.com> Co-authored-by: neutral <neutral@example.com>
The wizard's join phase classified a failed login by substring-matching three exported English sentences (
LOGIN_NO_MEMBERSHIP_MESSAGEand friends) out of the login lane's tee'd stderr, andprintJoinFailurematched a fourth. That made user-facing text load-bearing: rewording a message silently changed which fork the wizard offered, and nothing in the types said so.The information was never lost, just discarded at the command boundary: the loopback receiver attaches the server's refusal code as
callbackError,explainLoginErrorswitches on it one frame below, andrunRemoteLogincollapsed everything to a number.What changed
remoteLogin(argv, ctx, deps)returns{ exitCode, reason }.runRemoteLoginis now a one-line adapter over it, so the CLI dispatch table still seesPromise<number>.reasonis aLoginOutcomeReason:ok, the three D7 refusals,denied,login_failed,usage,connected_elsewhere, and the post-auth steps (store_failed,seed_failed,enroll_failed,daemon_incomplete). Every return in the browser and static paths names one.classifyLoginFailureandprintJoinFailureswitch on the code. The message constants are module-local again.LoginLaneResultcarriesreason;stderrstays, but only as the narration echoed indetail.Same exit codes, same output, byte for byte. LLP 0179 records the decision, including why the fuller "extract a pure core, reduce the command to printing" refactor was rejected for now: the lane's printing is ordered by its work (consent notice before the browser, hold message after the marker write), and hoisting it risks a silent regression in messages other LLPs pin. LLP 0135 gets an Extended-by forward-ref.
Full suite: 3348 pass, 3 failures all pre-existing on master (blob-store, usage-policy-fold, attach-enable-resume). Typecheck clean apart from the pre-existing ai-gateway error.