Skip to content

[AI-1992] Report a provisioning timeout as pending, not as a failed sign-in - #608

Merged
alexeyzimarev merged 2 commits into
mainfrom
ai-1992/provisioning-in-progress-outcome
Aug 19, 2026
Merged

alexeyzimarev merged 2 commits into
mainfrom
ai-1992/provisioning-in-progress-outcome

Conversation

@George-Payne

@George-Payne George-Payne commented Aug 19, 2026 •

Copy link
Copy Markdown
Member

When WorkOS workspace provisioning outran its 4s × 150 poll, Core collapsed the ProvisionOffer.InProgress outcome into an undistinguished AuthResult.Failed, so the wizard's Sign-in step headlined "Sign-in failed." over a workspace that was on its way — sign-in had already succeeded to get that far. The accurate copy survived only as a detail line and a log entry.

  • AuthFailureReason gains ProvisioningInProgress. It rides Failed rather than becoming its own AuthResult case deliberately: every consumer discriminates on Committed and funnels the rest into a catch-all (SignInStepViewModel:422, LoginCommand:66, SetupCommand:934, App.axaml.cs:429), so a new case would have landed silently in the very default: arm that prints the wrong headline. Riding the reason forces the one switch that had to change to change, and SetupCommand:50 already sets the { Reason: … } precedent.
  • ProvisionOffer carries the pending slug, so a caller can name the workspace it is telling the user to come back to. InProgress becomes a factory, matching the file's existing split between outcomes that carry data (Created, ExistingWorkspace) and ones that do not.
  • The headline is the fact; the sink's line is what to do about it. Both land in front of the same reader in a GUI, so the headline is deliberately not a paraphrase of the guidance. WizardAuthBridges reports that line as a Notice rather than an Error, since nothing went wrong.
  • App.axaml.cs printed the same untruth to stderr when the window was closed mid-poll — narrow, but it is literally the claim this ticket is about.

kcap setup needed no change, which is worth stating rather than implying otherwise. Its failure path prints nothing for this reason (SetupAuthProgress.ReportFailure only speaks for Unreachable, and OnboardingFacade.Stop never prints), so the only line a terminal user sees is the provisioner's own yellow "Still provisioning" — already correct. The slug is passed there too for symmetry, but nothing reads it on that path yet.

Declined is left generic, and deliberately not pinned by a test. It is arguably the same defect — the user chose to decline and the step still says sign-in failed — but it is a separate decision from this one, so asserting the current behaviour here would lock the wrong answer in with a passing test. Happy to file it.

Tests: the discovery-level reason and slug, the reason surviving the flow→AuthResult mapping, and the wizard headline itself (SignInStepViewModelTests) — which is the user-visible half and had no coverage. Verified by reverting each production change in turn and confirming the matching test reddens. WizardAuthBridgesTests also now pins PendingSlug, which nothing did.

Closes #573

#573

When WorkOS workspace provisioning outran its 4s x 150 poll, Core collapsed the
InProgress outcome into an undistinguished AuthResult.Failed, so the wizard
headlined "Sign-in failed." over a workspace that was on its way. Sign-in had
already succeeded to get that far. The accurate line - "still provisioning,
finish later by joining <slug> from the Connect step" - survived only as a detail.

- AuthFailureReason gains ProvisioningInProgress. It rides Failed rather than
  becoming its own AuthResult case on purpose: every consumer discriminates on
  Committed and funnels the rest into a catch-all, so a new case would have
  landed in the very default arm that prints the wrong headline. Riding the
  reason forces the one switch that had to change to change.
- ProvisionOffer carries the pending slug, so a caller can name the workspace it
  is telling the user to come back to. InProgress becomes a factory, matching the
  file's existing split between outcomes that carry data and ones that do not.
- The headline is the fact and the sink's line is what to do about it. They reach
  the same reader in a GUI, so the headline deliberately does not repeat the
  guidance.
- App.axaml.cs printed the same untruth to stderr when the window closed mid-poll.

Declined is left generic for now, and deliberately NOT pinned by a test: it is
arguably the same defect - the user chose it, and the step still says sign-in
failed - but it is a different decision from this one and wants its own.

The terminal setup path needed no change: its failure path prints nothing for
this reason, so the only line a terminal user sees is the provisioner's own. The
slug is passed there too for symmetry, though nothing reads it on that path yet.

Closes #573
@linear-code

linear-code Bot commented Aug 19, 2026

Copy link
Copy Markdown

AI-1992

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Report provisioning timeouts as pending instead of failed sign-ins

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Preserve provisioning timeout identity and workspace slug through onboarding result mapping.
• Present pending provisioning as informational guidance instead of a failed sign-in.
• Add regression coverage across discovery, facade, provisioner, and wizard presentation.
Diagram

graph TD
    A["Tenant provisioners"] --> B["Provision offer"] --> C["WorkOS discovery"] --> D["Onboarding facade"] --> E["Auth result"] --> F["Pending-state consumers"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add a dedicated pending AuthResult case
  • ➕ Models pending provisioning without placing it under a failure-shaped result.
  • ➕ Could make pending behavior explicit for every authentication consumer.
  • ➖ Existing consumers use catch-all branches and could silently retain incorrect failure messaging.
  • ➖ Requires broader API and consumer changes despite no durable workspace being committed.
2. Treat timeout as a successful or retargeted result
  • ➕ Avoids exposing pending provisioning through AuthResult.Failed.
  • ➕ Could route users directly toward later connection steps.
  • ➖ Incorrectly implies onboarding committed a usable workspace.
  • ➖ Could satisfy or advance workflows before provisioning actually completes.

Recommendation: Keep the PR's reason-discriminated AuthResult.Failed approach. It preserves existing non-committed semantics while forcing affected presentation code to distinguish pending provisioning; a dedicated result case should only be considered after consumers adopt exhaustive handling.

Files changed (12) +148 / -17

Enhancement (2) +10 / -5
ITenantProvisioner.csCarry pending workspace slugs in provisioning offers +9/-4

Carry pending workspace slugs in provisioning offers

• Adds PendingSlug to ProvisionOffer and replaces the static InProgress instance with a factory accepting an optional slug. Existing data-bearing offer patterns remain unchanged.

src/Capacitor.Cli.Core/Auth/ITenantProvisioner.cs

SpectreTenantProvisioner.csAttach the slug to CLI in-progress offers +1/-1

Attach the slug to CLI in-progress offers

• Returns the provisioned workspace slug when CLI polling times out. Existing terminal messaging remains unchanged.

src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs

Bug fix (6) +48 / -9
App.axaml.csAvoid failure wording for pending provisioning during wizard handoff +5/-0

Avoid failure wording for pending provisioning during wizard handoff

• Handles ProvisioningInProgress before the generic failure case when a wizard window closes mid-poll. Stderr now prints the pending message without claiming onboarding sign-in failed.

src/Capacitor.App/App.axaml.cs

WizardAuthBridges.csReturn pending slugs and informational provisioning notices +4/-2

Return pending slugs and informational provisioning notices

• Reports poll exhaustion as a notice rather than an error because workspace creation remains active. Returns an in-progress offer containing the workspace slug.

src/Capacitor.App/Services/Onboarding/WizardAuthBridges.cs

SignInStepViewModel.csRender provisioning timeouts as a non-error pending state +9/-0

Render provisioning timeouts as a non-error pending state

• Adds reason-specific handling for ProvisioningInProgress results. The view model displays a pending headline, preserves the provisioner's guidance as detail, and keeps the sign-in step unsatisfied.

src/Capacitor.App/ViewModels/Onboarding/SignInStepViewModel.cs

AuthResult.csAdd a provisioning-in-progress authentication reason +10/-2

Add a provisioning-in-progress authentication reason

• Extends AuthFailureReason with ProvisioningInProgress and documents why this non-committed outcome remains represented by AuthResult.Failed. Clarifies that its headline message complements progress guidance rather than repeating it.

src/Capacitor.Cli.Core/Auth/AuthResult.cs

OnboardingFacade.csPreserve discovery failure reasons in authentication results +1/-1

Preserve discovery failure reasons in authentication results

• Passes the WorkOS discovery failure reason into Stop so ProvisioningInProgress survives conversion to AuthResult.Failed.

src/Capacitor.Cli.Core/Auth/OnboardingFacade.cs

WorkOSDiscovery.csDistinguish pending provisioning from genuine creation failures +19/-4

Distinguish pending provisioning from genuine creation failures

• Adds a reason to WorkOSDiscoveryFlow.Failed and maps in-progress offers to ProvisioningInProgress with slug-aware messaging. Declined and failed offers retain the generic failure path.

src/Capacitor.Cli.Core/Auth/WorkOSDiscovery.cs

Tests (4) +90 / -3
SignInStepViewModelTests.csTest the wizard's pending provisioning presentation +30/-0

Test the wizard's pending provisioning presentation

• Verifies that a provisioning timeout displays the workspace-specific pending headline and guidance without error styling. Also confirms the step remains unsatisfied.

test/Capacitor.App.Tests.Unit/SignInStepViewModelTests.cs

WizardAuthBridgesTests.csTest pending slug and notice reporting +5/-1

Test pending slug and notice reporting

• Asserts that an exhausted wizard poll returns the requested slug and emits its recovery guidance as a notice rather than an error.

test/Capacitor.App.Tests.Unit/WizardAuthBridgesTests.cs

OnboardingFacadeTests.csTest reason propagation through the onboarding facade +8/-2

Test reason propagation through the onboarding facade

• Extends provisioning-offer assertions to verify the expected AuthFailureReason. Confirms ProvisioningInProgress survives discovery-to-AuthResult mapping without durable configuration or tokens.

test/Capacitor.Cli.Core.Tests.Unit/Auth/OnboardingFacadeTests.cs

WorkOSDiscoveryTests.csTest pending provisioning discovery outcomes +47/-0

Test pending provisioning discovery outcomes

• Covers slug-aware and slugless in-progress offers, ensuring both receive ProvisioningInProgress. Also verifies genuine provisioning failures retain the generic reason.

test/Capacitor.Cli.Core.Tests.Unit/Auth/WorkOSDiscoveryTests.cs

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/App.axaml.cs Outdated
From Tony's review of the previous commit.

Round one of review said the headline and the progress line said the same thing
twice in the wizard, so the headline became the bare fact and the guidance stayed
with the sink. But the stderr handoff runs after the window has closed, and that
view is where the guidance was - so a user who closes mid-poll learned the
workspace was pending and nothing about how to resume.

The two surfaces have different context available, so the handoff spells the
instruction out rather than the message carrying it for everyone.
@alexeyzimarev
alexeyzimarev merged commit 58ce23d into main Aug 19, 2026
8 of 10 checks passed
@alexeyzimarev
alexeyzimarev deleted the ai-1992/provisioning-in-progress-outcome branch August 19, 2026 20:22
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.

Surface workspace provisioning-in-progress as a distinct sign-in outcome

3 participants