Skip to content

Answer the create-a-workspace prompts with --org and --slug - #667

Merged
alexeyzimarev merged 7 commits into
mainfrom
georgepayne/ai-2163-non-interactive-workspace
Aug 26, 2026
Merged

alexeyzimarev merged 7 commits into
mainfrom
georgepayne/ai-2163-non-interactive-workspace

Conversation

@George-Payne

Copy link
Copy Markdown
Member

AI-2163

What & why

kcap setup can authenticate anywhere since the device grant landed, but creating a workspace still needs a terminal: the zero-workspace fork asks three questions through Spectre prompts, and Spectre throws from inside a prompt rather than returning. --org "<name>" --slug <slug> supply those answers up front, so the fork raises nothing and the command is scriptable end to end.

Both or neither — the slug becomes a permanent public hostname, so nothing derives one from the name in a run with nobody watching. Every combination that would take the flags and then not act on them is refused: alongside --server-url or a positional tenant, alongside --github, a value that is blank or is the next flag, the --org= spelling, a slug that could never be a hostname, and a non-interactive session without --no-prompt (which would create the workspace, then throw at the first step the flags do not answer).

Where to look

WrongWorkspaceError compares the landed workspace by slug, not by URL. Only the zero-workspace fork consults the provisioner, so an account that already has one never reaches it, and the run would otherwise configure a workspace nobody named — silently, with hooks and imports behind it. Profile names are the comparison because the server names the workspace it creates and the profile is stamped from the url it returns, which need not be the origin the CLI would guess.

Two contract changes ride along. ITenantPicker returning null now means the picker has already said why, so discovery adds no second line of its own; without that, a headless run gets per-workspace guidance followed by a contradicting "No tenant selected." And a headless multi-workspace discovery declines with guidance instead of throwing NotSupportedException, which reaches kcap login --discover too.

Verification

Suites run in the devcontainer: SetupCommandTests 69, SetupFacadeParityTests 16, TenantProvisionerHeadlessTests 16, SetupDecisionsTests 22, LoginFacadeParityTests 10, TenantPickerHeadlessTests 2; Core auth 110, SetupFunnelTests 7; App WizardAuthBridgesTests 25, SignInStepViewModelTests 28.

dotnet publish -c Release emits no IL2026/IL3050.

Each guard was checked by breaking it and watching the named test fail: the slug comparison, the flag-value parse, the taken-slug exit, and both RunDiscoveryAsync wiring arguments — deleting either left the feature dead with the suite green until these tests existed.

Signing in needs no terminal thanks to the device grant, but the workspace
fork is three Spectre prompts, and Spectre throws from inside one rather than
returning. The flags go together: the slug is a permanent public hostname,
and a collision cannot be re-asked for, so it ends the run naming the slug.
Only the zero-workspace fork consults the provisioner, so an account that
already has one configures that workspace instead and exits 0. Argv gives the
same trouble twice: `--org --slug acme` reads a flag as the name, and a blank
value reads as absent. Every runtime hint names --no-prompt, without which the
steps after creation throw on the session it was printed to.
Backing out of a prompt and having no terminal to prompt on are different
reasons needing different follow-ups, and a bare null cannot carry which. So
null now promises the picker has already said, and discovery adds no line —
"no tenant selected" would otherwise land under guidance that just explained
the session cannot prompt. Prompt capability is resolved once and handed to both.
The server names the workspace it creates and the profile is stamped from
that, so comparing against https://{slug}.kcap.ai would call a successful
create a failure the day those differ; a WorkOS profile is its slug either
way. The flags now also refuse the equals spelling, an invalid slug, and a
session that cannot answer the steps they leave unanswered.
@George-Payne George-Payne self-assigned this Aug 25, 2026
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

AI-2163

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Enable unattended workspace creation during setup

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Enable unattended workspace creation with paired --org and --slug setup options.
• Reject unsafe flag combinations, invalid slugs, and mismatched discovered workspaces.
• Make tenant pickers own null-selection guidance across interactive and headless discovery.
Diagram

graph TD
  A["Setup flags"] --> B["Argument validation"] --> C["Tenant discovery"] --> D{"Workspace count"}
  D -->|Zero| E["Workspace provisioner"] --> F["Provisioning API"] --> G["Profile verification"]
  D -->|Many| H["Tenant picker"]
Loading
High-Level Assessment

The approach is appropriate: keep CLI-only flag state in RequestedWorkspace, inject it into the existing provisioner, and preserve the shared ITenantProvisioner contract for non-CLI hosts. Deriving a slug, silently ignoring incompatible flags, or extending the cross-host interface would either make an irreversible choice unattended or couple UI hosts to CLI concerns.

Files changed (20) +906 / -89

Enhancement (4) +280 / -54
OAuthLoginFlow.csAdd flag-based workspace creation guidance +6/-5

Add flag-based workspace creation guidance

• Updates headless zero-workspace guidance to offer '--org' and '--slug'. All suggested setup commands now include '--no-prompt'.

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

RequestedWorkspace.csModel a workspace requested through flags +13/-0

Model a workspace requested through flags

• Introduces a CLI-specific record carrying organization name and canonical slug. It also resolves the expected workspace origin consistently with positional tenant input.

src/Capacitor.Cli/Commands/RequestedWorkspace.cs

SetupCommand.csWire workspace flags into setup discovery +119/-7

Wire workspace flags into setup discovery

• Parses and validates paired workspace flags, rejects unsafe combinations and non-interactive misuse, and passes the request into discovery. After authentication, setup verifies the committed profile slug matches the requested workspace before continuing.

src/Capacitor.Cli/Commands/SetupCommand.cs

SpectreTenantProvisioner.csProvision requested workspaces without prompts +142/-42

Provision requested workspaces without prompts

• Adds a scripted provisioning path using supplied organization and slug values, including validation, availability checks, plain stderr messaging, and terminal-free polling. Shared error helpers keep interactive and scripted outcomes consistent while preserving mode-specific retry guidance.

src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs

Bug fix (5) +51 / -15
WizardAuthBridges.csLet the wizard picker report cancelled selection +9/-5

Let the wizard picker report cancelled selection

• Injects auth progress into 'WizardTenantPicker' so it reports when the user selects nothing. This satisfies the revised picker contract without duplicate discovery errors.

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

OnboardingFacade.csPreserve already-reported discovery failures +5/-2

Preserve already-reported discovery failures

• Handles picker-owned GitHub discovery failures without rendering the same error again. Failure reason propagation remains unchanged.

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

TenantDiscovery.csMark picker-owned null-selection messages +11/-2

Mark picker-owned null-selection messages

• Extends discovery outcomes with 'AlreadyReported' and clarifies that null selections must be explained by the picker. This prevents contradictory or duplicate caller output.

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

WorkOSDiscovery.csSuppress duplicate WorkOS selection errors +3/-3

Suppress duplicate WorkOS selection errors

• Returns a failed discovery result directly when the picker chooses nothing. The picker remains solely responsible for user-facing guidance.

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

SpectreTenantPicker.csHandle multi-workspace discovery without a terminal +23/-3

Handle multi-workspace discovery without a terminal

• Adds an interactivity seam and avoids opening Spectre prompts in headless sessions. Instead, it lists canonical '--server-url' commands for every discovered workspace.

src/Capacitor.Cli/Commands/SpectreTenantPicker.cs

Tests (8) +535 / -11
WizardAuthBridgesTests.csTest wizard-owned null-selection reporting +18/-5

Test wizard-owned null-selection reporting

• Updates picker construction for injected progress and verifies backing out reports 'No tenant selected.' through the picker.

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

OnboardingFacadeTests.csTest silent propagation of picker failures +20/-0

Test silent propagation of picker failures

• Verifies GitHub discovery does not add an error after the picker has already explained a null selection.

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

TenantDiscoveryTests.csTest discovery message ownership metadata +16/-0

Test discovery message ownership metadata

• Verifies null picker results are marked already reported while unrelated discovery failures remain caller-owned.

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

WorkOSDiscoveryTests.csTest WorkOS duplicate-message suppression +28/-0

Test WorkOS duplicate-message suppression

• Confirms WorkOS discovery returns failure without writing another error when the picker returns null.

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

SetupCommandTests.csCover workspace flag parsing and safety guards +172/-0

Cover workspace flag parsing and safety guards

• Adds coverage for paired values, malformed input, incompatible modes, slug validation, command exits, and committed-profile slug checks. Tests ensure invalid requests stop before side effects.

test/Capacitor.Cli.Tests.Unit/Commands/SetupCommandTests.cs

SetupFacadeParityTests.csTest requested-workspace discovery wiring +39/-0

Test requested-workspace discovery wiring

• Verifies discovery receives a scripted provisioner and rejects a committed workspace that differs from the requested slug.

test/Capacitor.Cli.Tests.Unit/Commands/SetupFacadeParityTests.cs

TenantPickerHeadlessTests.csTest headless multi-workspace guidance +40/-0

Test headless multi-workspace guidance

• Adds tests proving the Spectre picker returns null instead of throwing and prints a reusable command for every workspace.

test/Capacitor.Cli.Tests.Unit/Commands/TenantPickerHeadlessTests.cs

TenantProvisionerHeadlessTests.csTest terminal-free workspace provisioning +202/-6

Test terminal-free workspace provisioning

• Expands headless provisioning coverage across successful creation, slug validation and availability failures, API refusals, polling, stderr output, and account-owned reservations. A stub HTTP handler verifies network sequencing and outcomes.

test/Capacitor.Cli.Tests.Unit/Commands/TenantProvisionerHeadlessTests.cs

Documentation (3) +40 / -9
README.mdDocument unattended workspace creation +8/-1

Document unattended workspace creation

• Adds examples and behavioral guidance for creating a workspace with '--org', '--slug', and '--no-prompt'. Documents pairing, validation, provider, and existing-workspace constraints.

README.md

help-setup.txtDescribe new setup creation options +20/-2

Describe new setup creation options

• Adds '--org' and '--slug' option help and expands '--no-prompt' requirements. Documents invalid combinations and hosted-auth provisioning behavior.

src/Capacitor.Cli.Core/Resources/help-setup.txt

SetupFunnel.csClarify pre-request workspace failures +12/-6

Clarify pre-request workspace failures

• Documents that scripted slug validation can emit 'WorkspaceFailed' before 'WorkspaceRequested'. Enumerates availability failure reasons for funnel interpretation.

src/Capacitor.Cli.Core/Telemetry/SetupFunnel.cs

@qodo-code-review

qodo-code-review Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Historical telemetry rationale retained ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The SetupFunnel comment relies on unspecified prior incidents of live-display garbling instead of
stating the current technical constraint. This historical narrative is not durable without
repository history and violates the comment standard.
Code

src/Capacitor.Cli.Core/Telemetry/SetupFunnel.cs[R17-18]

+/// (for the poll outcomes) inside a Spectre live-display callback, which this codebase has prior
+/// form for garbling when work blocks there.
Evidence
Rule 24 forbids comments that preserve unnecessary historical narrative. The changed comment says
the codebase has prior form for garbling, rather than documenting only the present constraint.

CLAUDE.md: Write Only Current, Non-Obvious, Durable Comments
src/Capacitor.Cli.Core/Telemetry/SetupFunnel.cs[17-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The telemetry comment justifies behavior by saying the codebase has `prior form`, which depends on unspecified project history.

## Issue Context
Document the current live-display or synchronous-blocking constraint directly, without referring to past incidents.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Telemetry/SetupFunnel.cs[17-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Parity comment narrates coverage ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The added parity-test comment discusses wiring halves and what other tests cannot observe. This is
review-oriented test-coverage rationale, not a durable code constraint or a statement of the
behavior the tests pin.
Code

test/Capacitor.Cli.Tests.Unit/Commands/SetupFacadeParityTests.cs[R193-195]

+    // Both halves of the --org/--slug wiring: the flags must reach the provisioner discovery is given,
+    // and the landed workspace must be checked against them once discovery commits. Each is a single
+    // argument in RunDiscoveryAsync, invisible to every test that drives the pieces directly.
Evidence
Rule 24 permits comments only for durable, non-obvious constraints and rejects review artifacts or
historical narration. The comment's references to both halves of wiring and being `invisible to
every test` explain test coverage rather than production behavior.

CLAUDE.md: Write Only Current, Non-Obvious, Durable Comments
test/Capacitor.Cli.Tests.Unit/Commands/SetupFacadeParityTests.cs[193-195]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The comment narrates the test strategy and coverage gap instead of documenting the current behavior under test.

## Issue Context
Describe the contract directly: discovery must pass the requested workspace to provisioning and reject a committed workspace with a different profile slug.

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/Commands/SetupFacadeParityTests.cs[193-195]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Provisioner docs discuss other tests ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The test-class documentation explains that façade tests substitute ITenantProvisioner and
therefore miss these prompts. That describes test-suite implementation and placement rather than the
behavior this class pins or a required precondition.
Code

test/Capacitor.Cli.Tests.Unit/Commands/TenantProvisionerHeadlessTests.cs[R10-12]

+/// prompt and Spectre throws rather than returning. Either the two answers arrive as flags, or there
+/// is nothing to ask and the run has to say so. The façade tests substitute ITenantProvisioner, so
+/// the prompts these cover never run there.
Evidence
Rule 26 requires test documentation to focus on the current contract and necessary preconditions.
These lines instead justify the class by describing how façade tests are implemented and which path
they do not exercise.

CLAUDE.md: Make Test Documentation State the Behavior the Test Pins
test/Capacitor.Cli.Tests.Unit/Commands/TenantProvisionerHeadlessTests.cs[10-12]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The class documentation discusses what another test suite substitutes and does not cover.

## Issue Context
Test documentation should state the current headless provisioning behavior and any preconditions needed to prove it, without explaining suite coverage or prior test implementation.

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/Commands/TenantProvisionerHeadlessTests.cs[10-12]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Retry bypasses workspace creation ✓ Resolved 🐞 Bug ≡ Correctness
Description
Scripted polling failures tell the user to rerun kcap setup <slug> --no-prompt, but a positional
slug is treated as an existing server and skips discovery and CreateRequestedAsync. For failed,
forbidden, or not-found provisioning states, this command cannot resume creation with the requested
organization and slug.
Code

src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs[242]

+        var retryPlain  = Scripted ? $"Re-run kcap setup {slug} --no-prompt" : $"Re-run kcap setup {slug}";
Evidence
The changed retry string contains only a positional slug. Setup interprets that token as
serverUrlArg and takes ResolveServerAndProviderAsync, while requested creation is only reached
through discovery; the provisioner also explicitly supports resuming a slug reserved as yours, but
that requires the --org/--slug path.

src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs[239-274]
src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs[120-141]
src/Capacitor.Cli/Commands/SetupCommand.cs[102-105]
src/Capacitor.Cli/Commands/SetupCommand.cs[205-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Scripted provisioning failures print a positional setup command, which selects the existing-server path instead of retrying workspace creation.

## Issue Context
The retry needs to retain the original organization and slug, or explicitly tell the user to rerun the original command. Keep the positional command only where the workspace is expected to have become live already.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs[239-285]
- src/Capacitor.Cli/Commands/SetupCommand.cs[205-216]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.Cli.Core/Telemetry/SetupFunnel.cs Outdated
Comment thread test/Capacitor.Cli.Tests.Unit/Commands/TenantProvisionerHeadlessTests.cs Outdated
Comment thread test/Capacitor.Cli.Tests.Unit/Commands/SetupFacadeParityTests.cs Outdated
Comment thread src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs Outdated
`kcap setup <slug>` reads a positional as an existing server, so it resolves
that host and never reaches creation — fine once a workspace is merely still
building, useless for one that failed, was forbidden, or is unlinked, since
none of those exist to point at.
Comment thread src/Capacitor.Cli/Commands/SetupCommand.cs Outdated
Comment thread src/Capacitor.Cli/Commands/SetupCommand.cs Outdated
Comment thread src/Capacitor.Cli/Commands/SetupCommand.cs Outdated
Comment thread src/Capacitor.Cli/Commands/SpectreTenantProvisioner.cs Outdated

@realtonyyoung realtonyyoung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Four P2 findings: the requested-workspace safety check occurs after auth/config publication, --no-prompt can still reach tenant selection, a present but valueless server flag can be ignored before creation, and retry guidance does not safely preserve the organization argument. Details are inline.

The check ran on a committed result, so an account that already belonged
elsewhere kept the profile, stamp and tokens that commit had written; it moves
onto the boundary's last cancellable step. --no-prompt now settles whether the
picker may open, a valueless --server-url counts as the conflict it is, and no
re-run command carries an organization name through a shell.
…on-interactive-workspace

# Conflicts:
#	src/Capacitor.Cli/Commands/SetupCommand.cs
@alexeyzimarev
alexeyzimarev merged commit fdec588 into main Aug 26, 2026
6 checks passed
@alexeyzimarev
alexeyzimarev deleted the georgepayne/ai-2163-non-interactive-workspace branch August 26, 2026 10:59
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.

3 participants