Skip to content

feat(wifi): auth-failure feedback on the secret prompt (#218)#252

Open
vibechoom wants to merge 1 commit into
mainfrom
feat/wifi-auth-feedback-218
Open

feat(wifi): auth-failure feedback on the secret prompt (#218)#252
vibechoom wants to merge 1 commit into
mainfrom
feat/wifi-auth-feedback-218

Conversation

@vibechoom

Copy link
Copy Markdown
Contributor

Ships the robust subset of #218 (per the ## Triage): distinguishable retry feedback + a non-empty submit guard on the Wi-Fi/VPN secret prompt. The busy/spinner half is deliberately deferred — the triage proved it unbuildable off the prompt signal.

What changed

PromptRequest::prior_failure: bool (wifi/types.rs)

A bool, not an attempt: u32 — NM cannot produce a retry count, only a single "the last secret was rejected" bit, so a count would over-promise.

  • NM agent (wifi/nm_agent.rs): maps NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW (0x2, already delivered in the same GetSecrets flags arg that we only tested ALLOW_INTERACTION on) → prior_failure. Stateless, per-call, authoritative. Added const FLAG_REQUEST_NEW: u32 = 0x2; and set the field at both construction sites (Wi-Fi + VPN).
  • iwd agent (wifi/agent.rs): RequestPassphrase carries no equivalent failure signal, so it sets prior_failure: false (honest no-op on that backend).

The nm_agent.rs system tests read struct fields rather than building PromptRequest, so this is low-churn — only the struct + the two construction sites.

Prompt overlay (overlays/prompt.rs)

  • Non-empty Connect guard: Connect starts insensitive; connect_changed on the entry toggles set_sensitive(!text.is_empty()). Enter/activate and the Connect click also no-op on empty text. Kills the silent empty-submit loop.
  • Failure feedback on a reopened prompt: when prior_failure is set, render an error subtitle ("Authentication failed — check the passphrase") and add the stock Adwaita .error CSS class to the PasswordEntry (red). The overlay already rebuilds per request, so no structural change — a retry no longer reads as a byte-identical re-ask.

assets/trollshell/style.css

Adds a .ts-prompt-error rule (uses @error_color). The entry red comes free from Adwaita's built-in .error.

Deferred (NOT built): the busy/spinner in-flight state

The triage proved the spinner-as-specified has a timing bug in its premise: both agents clear the prompt to None the instant the secret is handed to the daemon (agent.rs, nm_agent.rs), not when the connection result is known. On submit the sequence is Some(a) → None → Some(b); the overlay tears down within milliseconds, the seconds-long association runs with no overlay on screen, and only a failure re-emits Some. A spinner keyed on active_prompt would flash and vanish. None means "secret submitted", not "success". A genuine in-flight indicator has to key off wifi::station() StationState::Connecting / NM device state — a separate, larger seam. Left as a follow-up.

Scope notes

  • iwd retry-tracking is not attempted (would need shell-side mutable state + rests on an unverified iwd re-prompt assumption). Only NM reports prior_failure; iwd reopens plainly. Matches the triage recommendation.

Live-verify

  • Wrong Wi-Fi passphrase → the reopened prompt shows the error subtitle + red .error entry (not a byte-identical re-ask).
  • Connect is disabled on an empty entry; Enter on empty is inert.

Closes #218 (robust subset; busy-state follow-up tracked as deferred above).

A rejected Wi-Fi passphrase re-prompted byte-identically (reads as a shell
bug), and an empty submit was a silent no-op the agent just re-asked for.
Ship the robust subset from the #218 triage:

- `PromptRequest` gains `prior_failure: bool` — a bool, not an attempt count,
  because NM can only produce the one bit, not a count. The NM secret agent
  maps `NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW` (0x2) on `GetSecrets`
  into it (stateless, per-call, authoritative "the last secret was rejected").
  The iwd agent has no equivalent signal on `RequestPassphrase`, so it sets
  `false`.
- Prompt overlay: a non-empty Connect guard (`connect_changed` →
  `set_sensitive`; Enter/Connect also no-op on empty), plus an error subtitle
  ("Authentication failed — check the passphrase") and the `.error` CSS class
  on the password entry when a reopened prompt carries `prior_failure`.

Deferred (not built): the busy/spinner in-flight state. The triage proved it
unbuildable off the prompt signal — the agents clear the prompt to `None` the
instant the secret is handed to the daemon (not when the connection result is
known), so a spinner keyed on `active_prompt` would flash and vanish while the
seconds-long association runs with no overlay on screen. A genuine in-flight
indicator has to key off `wifi::station()` `StationState::Connecting` / NM
device state — a separate, larger seam. Follow-up.

Live-verify: enter a wrong Wi-Fi passphrase → the reopened prompt shows the
error styling + message (not a byte-identical re-ask); Connect is disabled on
an empty entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vibechoom

Copy link
Copy Markdown
Contributor Author

Adversarial self-review (reviewer: vibechoom) — clean, ready for live-verify.

  • FLAG_REQUEST_NEW = 0x2 correctly mapped to prior_failure at both NM construction sites (Wi-Fi + VPN); iwd sets false (no equivalent on RequestPassphrase). prior_failure: bool is the honest cross-backend field (NM gives a bit, not a count).
  • Prompt shows the error subtitle + .ts-prompt-error styling only on a prior_failure retry — so a wrong passphrase no longer re-asks byte-identically. Connect starts disabled and enables on non-empty entry (no empty-passphrase submit).
  • Busy/spinner correctly deferred (unbuildable off the prompt signal — it clears to None on secret-handoff, not connection result; a real indicator needs StationState::Connecting). Scope: 5 files (+60), clippy-clean.

Live-verify: enter a wrong Wi-Fi passphrase → the reopened prompt shows the error message + red entry (distinguishable from a fresh ask); Connect is greyed on an empty field. 🚂

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.

feat(wifi): auth-failure feedback and a busy state on the secret prompt

1 participant