Problem
When the CLI preflight (#791) determines the environment is broken (missing or outdated), the first click on a guarded command produces a modal warning toast with a Run Setup action. Every subsequent click within the same session is silent. The setupToastShown flag at packages/vscode/src/preflight/preflight.ts:245-261 suppresses repeat toasts for the rest of the session, resetting only when the user explicitly runs Codev: Recheck CLI and the recheck confirms status === 'ok'.
The intent was anti-spam. The defect is lack of point-of-action feedback. After the first click, the user clicks Spawn Builder, Approve Gate, Send Message, or any of the 13 other guarded commands and gets no signal whatsoever. The click registered with the guard, the guard rejected it, the helper bailed silently. From the user's perspective, the four scenarios below look identical:
- The command silently no-op'd (what's actually happening)
- The click missed the menu item
- The command ran but didn't visibly do anything
- The extension is hung
All four read as "nothing happened". The toast was the only signal that disambiguated them, and the suppression eliminates it.
Why this is the wrong default
The original design intent (don't spam modal toasts on rapid clicks) is sound. The execution overshot: it traded modal spam for zero feedback. The right shape is attenuated feedback, not absent feedback.
Point-of-action feedback is the load-bearing principle. Asking the user to notice a state indicator elsewhere (sidebar Status row, status-bar item, etc.) is asking the UI to push the work back onto the user. The fix needs to keep the feedback co-located with the action.
Proposed fix
Two-stage feedback that preserves point-of-action signal without modal spam:
First click while state is bad: modal warning toast
Unchanged from today. Full showWarningMessage with the Run Setup action button. The user is being told the state for the first time; modal is appropriate.
Subsequent clicks within the session: ephemeral status-bar message
VS Code has vscode.window.setStatusBarMessage(text, durationMs). It shows a brief temporary line at the bottom-left for N seconds, no action button, no modal interrupt. VS Code itself uses this pattern for things like "Saving..." / "Loaded N files".
Something like:
vscode.window.setStatusBarMessage(
`Codev: CLI ${cachedStatus === 'outdated' ? 'outdated' : 'not installed'} — run Codev: Recheck CLI when ready`,
4000,
);
User gets immediate, visible, co-located confirmation that the click registered AND the same problem still applies. Not a modal interrupt; doesn't break flow.
Flag reset semantics
Unchanged. The flag still resets when recheckCli confirms ok. The only change is what fires on subsequent suppressed clicks.
Refactor for reusability (composes with #983)
This issue's primary work is the per-click feedback pattern. Worth designing the implementation as a reusable helper rather than inline logic, because the same shape is needed by #983 (Tower running-vs-installed version divergence). When #983 ships, it should call the same helper, with the helper branching on which preflight dimension is currently broken (CLI version vs Tower version).
Suggested shape:
export function showPreflightFeedback(state: PreflightState): void {
// First call this session for the current bad state: modal
// Subsequent calls: ephemeral status-bar message
// Wording derived from state (CLI missing / outdated / Tower stale / etc.)
}
The current showSetupRequiredToast becomes a thin wrapper or merges into the helper.
Acceptance
What this isn't
Related
Problem
When the CLI preflight (#791) determines the environment is broken (
missingoroutdated), the first click on a guarded command produces a modal warning toast with aRun Setupaction. Every subsequent click within the same session is silent. ThesetupToastShownflag atpackages/vscode/src/preflight/preflight.ts:245-261suppresses repeat toasts for the rest of the session, resetting only when the user explicitly runsCodev: Recheck CLIand the recheck confirmsstatus === 'ok'.The intent was anti-spam. The defect is lack of point-of-action feedback. After the first click, the user clicks Spawn Builder, Approve Gate, Send Message, or any of the 13 other guarded commands and gets no signal whatsoever. The click registered with the guard, the guard rejected it, the helper bailed silently. From the user's perspective, the four scenarios below look identical:
All four read as "nothing happened". The toast was the only signal that disambiguated them, and the suppression eliminates it.
Why this is the wrong default
The original design intent (don't spam modal toasts on rapid clicks) is sound. The execution overshot: it traded modal spam for zero feedback. The right shape is attenuated feedback, not absent feedback.
Point-of-action feedback is the load-bearing principle. Asking the user to notice a state indicator elsewhere (sidebar Status row, status-bar item, etc.) is asking the UI to push the work back onto the user. The fix needs to keep the feedback co-located with the action.
Proposed fix
Two-stage feedback that preserves point-of-action signal without modal spam:
First click while state is bad: modal warning toast
Unchanged from today. Full
showWarningMessagewith theRun Setupaction button. The user is being told the state for the first time; modal is appropriate.Subsequent clicks within the session: ephemeral status-bar message
VS Code has
vscode.window.setStatusBarMessage(text, durationMs). It shows a brief temporary line at the bottom-left for N seconds, no action button, no modal interrupt. VS Code itself uses this pattern for things like "Saving..." / "Loaded N files".Something like:
User gets immediate, visible, co-located confirmation that the click registered AND the same problem still applies. Not a modal interrupt; doesn't break flow.
Flag reset semantics
Unchanged. The flag still resets when
recheckCliconfirmsok. The only change is what fires on subsequent suppressed clicks.Refactor for reusability (composes with #983)
This issue's primary work is the per-click feedback pattern. Worth designing the implementation as a reusable helper rather than inline logic, because the same shape is needed by #983 (Tower running-vs-installed version divergence). When #983 ships, it should call the same helper, with the helper branching on which preflight dimension is currently broken (CLI version vs Tower version).
Suggested shape:
The current
showSetupRequiredToastbecomes a thin wrapper or merges into the helper.Acceptance
regCli-guarded command produces visible feedback at point of action while the preflight state ismissingoroutdated. No silent no-ops.Run Setupaction (unchanged from today).Codev: Recheck CLIconfirmsok, the per-session counter resets; the next click after the state breaks again restarts the modal-first pattern.okorpending→ command runs normally, no feedback noise).showPreflightFeedbackor equivalent) that vscode + tower: detect installed-vs-running Tower version divergence; preflight needs an in-memory version probe, not justcodev --version#983 can call when its Tower-version branch needs to surface feedback, without re-inventing the suppression logic.What this isn't
codev --version#983. This issue lays the helper foundation that vscode + tower: detect installed-vs-running Tower version divergence; preflight needs an in-memory version probe, not justcodev --version#983 will reuse.regCli-guarded commands stay guarded; the guard's behaviour when it rejects is what changes.Related
packages/vscode/src/preflight/preflight.ts:240-261—showSetupRequiredToastandsetupToastShown(where the suppression lives today).packages/vscode/src/extension.ts:525-530— theguardwrapper that callsshowSetupRequiredToast(where the helper would be invoked instead).