Skip to content

Machine create inherits profile default visibility; status surfaces machine auth - #522

Merged
realtonyyoung merged 5 commits into
mainfrom
tonyyoung/machine-visibility-profile-default
Aug 10, 2026
Merged

Machine create inherits profile default visibility; status surfaces machine auth#522
realtonyyoung merged 5 commits into
mainfrom
tonyyoung/machine-visibility-profile-default

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

CLI half of the machine-session-visibility work (Linear AI-1852; server half is kcap-server#1396). Companion to the server change that makes service-owned org-default sessions org-visible.

Problem

kcap machine create defaulted its printed default_visibility to private and instructed the operator to run kcap config set default_visibility private on the runner — contradicting the visibility they already chose at kcap setup and the product model that visibility lives in the recording profile. Separately, a developer who exports KCAP_CLIENT_ID/KCAP_CLIENT_SECRET into an interactive shell silently records every terminal session as the machine, with no indication anywhere.

Changes

  • kcap machine create inherits the creator's configured visibility. New pure MachineCommand.ResolveCreateVisibility(flag, profileDefault)(value, provenance): an explicit --visibility wins (from --visibility), else the active profile's default_visibility (your profile default — what kcap setup wrote), else org_public (product default). The setup instructions print the resolved value labeled with its provenance and never introduce private on the command's own authority. help-machine.txt updated to match.
  • kcap status surfaces machine-auth diversion. New MachineAuth.DescribeDiversion(idSet, secretSet) prints a line naming whichever KCAP_CLIENT_* variable(s) are present (the trigger is either-var, so a fixed "ID is set" text would be false in the secret-only case), above the token-store state.

No wire/protocol change; independent of the server PR (either merge order works).

Tests

MachineCreateVisibilityTests (4 — flag wins, profile inherited, private-profile honored-and-labeled, no-profile product default); MachineAuthStatusLineTests (id-only / secret-only / both / silent-when-neither); existing MachineCommandTests help pins unchanged (20/20). All green locally.

🤖 Generated with Claude Code

realtonyyoung and others added 2 commits August 10, 2026 17:22
…venance labels

kcap machine create no longer defaults its printed default_visibility to
private; it resolves from the explicit --visibility flag, else the active
profile's default_visibility (what kcap setup wrote), else org_public,
each labeled with its provenance. A machine is never steered to private.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When KCAP_CLIENT_ID/KCAP_CLIENT_SECRET are present the CLI records as the
machine, bypassing the token store; kcap status now says so, naming the
exact variable(s) set so the secret-only case is not mislabeled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Machine create inherits profile visibility; status warns on machine auth

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Make kcap machine create print visibility inherited from the active profile, with provenance.
• Update setup instructions/help text to avoid suggesting private unless explicitly chosen.
• Add kcap status warning when KCAP_CLIENT_* env vars divert auth from token store.
Diagram

graph TD
  MC["MachineCommand create"] --> AC["AppConfig active profile"] --> RCV["ResolveCreateVisibility()"] --> OUT["Setup instructions"]
  SC["StatusCommand"] --> ENV{{"KCAP_CLIENT_* env"}} --> MA["MachineAuth.DescribeDiversion()"] --> OUT
  SC --> TS[("TokenStore")] --> OUT

  subgraph Legend
    direction LR
    _cmd["Command"] ~~~ _env{{"Environment"}} ~~~ _db[("Local store")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Let MachineAuth.DescribeDiversion read environment variables directly
  • ➕ Less duplication at call sites (StatusCommand wouldn’t need to compute idSet/secretSet).
  • ➕ Keeps env-variable naming logic fully encapsulated in MachineAuth.
  • ➖ Harder to unit-test deterministically without env mutation/isolation.
  • ➖ Makes the method impure; increases coupling to process environment.
2. Remove `--visibility` from `kcap machine create` and always instruct “set profile default_visibility”
  • ➕ Eliminates the misleading notion that create-time visibility config exists.
  • ➕ Simplifies CLI surface area and help text.
  • ➖ Reduces ergonomics for operators who want setup output tailored to an intended value.
  • ➖ Loses explicit provenance of “operator intentionally chose this for the runner” via flag.

Recommendation: Current approach is the best trade-off: keep ResolveCreateVisibility/DescribeDiversion pure for straightforward unit tests, and explicitly label provenance in user-facing output. The small duplication in StatusCommand (reading env vars) is acceptable given the improved testability and clarity.

Files changed (6) +135 / -9

Enhancement (3) +61 / -6
MachineAuth.csAdd machine-auth diversion status line helper +16/-0

Add machine-auth diversion status line helper

• Introduces 'MachineAuth.DescribeDiversion(idSet, secretSet)' to generate a one-line 'kcap status' warning when machine credentials are present. The message precisely names which 'KCAP_CLIENT_*' variable(s) are set and returns null when neither is present.

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

MachineCommand.csResolve create-time visibility from profile default with provenance +37/-6

Resolve create-time visibility from profile default with provenance

• Loads the active profile and uses a new pure 'ResolveCreateVisibility(flag, profileDefault)' helper to choose the visibility printed in setup instructions. Updates setup output to describe the resolved value with provenance and avoids introducing 'private' unless explicitly selected.

src/Capacitor.Cli/Commands/MachineCommand.cs

StatusCommand.csShow machine-auth diversion before token-store auth status +8/-0

Show machine-auth diversion before token-store auth status

• Adds an early 'Auth:' line in 'kcap status' when machine credential env vars are set, explaining that recording/auth is diverted to machine auth and bypasses the token store. Leaves existing token-store status reporting intact.

src/Capacitor.Cli/Commands/StatusCommand.cs

Tests (2) +69 / -0
MachineAuthStatusLineTests.csAdd tests for machine-auth diversion status messaging +27/-0

Add tests for machine-auth diversion status messaging

• Adds unit coverage ensuring the diversion warning names exactly the env var(s) present and is silent when neither is set.

test/Capacitor.Cli.Tests.Unit/MachineAuthStatusLineTests.cs

MachineCreateVisibilityTests.csAdd tests for machine create visibility resolution and provenance +42/-0

Add tests for machine create visibility resolution and provenance

• Adds unit tests for precedence and labeling: '--visibility' wins, profile default is inherited (including private), and no-profile falls back to 'org_public'. Ensures provenance strings match expected output semantics.

test/Capacitor.Cli.Tests.Unit/MachineCreateVisibilityTests.cs

Documentation (1) +5 / -3
help-machine.txtUpdate machine create visibility help to match profile-default behavior +5/-3

Update machine create visibility help to match profile-default behavior

• Adjusts '--visibility' documentation to reflect that defaults come from the operator profile (or 'org_public' when no profile exists). Clarifies that the command does not independently steer machines to 'private'.

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

@qodo-code-review

qodo-code-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. README missing machine/status updates ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
This PR changes user-facing CLI behavior/output (kcap machine create visibility default messaging
and kcap status machine-auth diversion line) but does not update README.md, leaving public docs
out of sync. This violates the requirement to update README in the same PR for CLI surface changes.
Code

src/Capacitor.Cli.Core/Resources/help-machine.txt[R32-36]

+                     private, org_public, public. Defaults to your own
+                     profile's default_visibility (org_public if you have no
+                     profile) — a machine is not steered to private. Printed in
+                     the setup instructions — see below for why it is set on the
+                     machine rather than here.
Evidence
PR Compliance ID 6 requires updating README.md in the same PR whenever user-facing CLI surface
changes. This PR modifies the machine-create help/default visibility messaging and adds a new
machine-auth diversion line to kcap status, but the README sections that describe kcap machine
and kcap status do not document these updated behaviors.

CLAUDE.md: Update README.md in the same PR for any user-facing CLI surface change
src/Capacitor.Cli.Core/Resources/help-machine.txt[31-36]
src/Capacitor.Cli/Commands/StatusCommand.cs[40-47]
README.md[110-121]
README.md[1666-1706]

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

## Issue description
This PR changes user-facing CLI behavior/output but does not update `README.md` accordingly.

## Issue Context
- `kcap machine create` now resolves the *printed* visibility default from the active profile (or product default), and help text was updated.
- `kcap status` now prints an additional auth line when `KCAP_CLIENT_ID` and/or `KCAP_CLIENT_SECRET` are set.
- The compliance checklist requires README updates in the same PR for user-facing CLI changes.

## Fix Focus Areas
- README.md[110-121]
- README.md[1666-1706]

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


2. Project visibility blocked ✓ Resolved 🐞 Bug ≡ Correctness
Description
MachineCommand.CreateAsync now inherits the active profile’s default_visibility, but the machine
visibility allowlist omits the valid value project, so users with default_visibility=project will
hit the --visibility must be one of... error and cannot create a machine without changing their
profile or overriding the flag. The updated help text also omits project, drifting from what
config accepts.
Code

src/Capacitor.Cli/Commands/MachineCommand.cs[R63-66]

+        var profile = await AppConfig.GetActiveProfileAsync();
+        var (visibility, visibilityProvenance) =
+            ResolveCreateVisibility(GetArg(args, "--visibility"), profile?.DefaultVisibility);
+        var role = GetArg(args, "--role");
Evidence
The PR made machine create derive the printed/setup visibility from the active profile
(profile?.DefaultVisibility) and then validate it against MachineCommand.Visibilities. Config
and AppConfig explicitly treat project as a valid default_visibility, but
MachineCommand.Visibilities (and the updated help text) omit it, so a profile configured with
project will now cause machine creation to error out at validation.

src/Capacitor.Cli/Commands/MachineCommand.cs[55-85]
src/Capacitor.Cli/Commands/MachineCommand.cs[25-32]
src/Capacitor.Cli.Core/Config/AppConfig.cs[227-233]
src/Capacitor.Cli/Commands/ConfigCommand.cs[174-175]
src/Capacitor.Cli.Core/Resources/help-machine.txt[29-36]

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

### Issue description
`kcap machine create` now resolves the visibility from the active profile’s `default_visibility`. The CLI config supports `default_visibility=project`, but `MachineCommand`’s visibility allowlist and the help text only recognize `private`, `org_public`, and `public`. This makes machine creation fail for users configured with the valid `project` visibility.

### Issue Context
- Config accepts `project` as a `default_visibility` value.
- AppConfig normalization treats `project` as valid.
- The PR made `machine create` pull visibility from the active profile, so `project` is now reachable input and will be rejected by `MachineCommand.Visibilities`.

### Fix Focus Areas
- src/Capacitor.Cli/Commands/MachineCommand.cs[25-85]
- src/Capacitor.Cli.Core/Resources/help-machine.txt[29-36]
- test/Capacitor.Cli.Tests.Unit/MachineCreateVisibilityTests.cs[1-42]

### What to change
1. Add `"project"` to `MachineCommand`’s `Visibilities` allowlist (and ensure any printed error text reflects it).
2. Update `help-machine.txt` to list `project` as an allowed value for `--visibility`.
3. Add/extend unit coverage to include resolving (and ideally accepting) `project` as a profile default and/or flag value.

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


Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.Cli.Core/Resources/help-machine.txt Outdated
Comment thread src/Capacitor.Cli/Commands/MachineCommand.cs
realtonyyoung and others added 3 commits August 10, 2026 17:30
…te-credential status

- ResolveCreateVisibility falls back to org_public when the profile default
  is a value a machine cannot record with (e.g. 'project'), instead of
  inheriting it and hitting the machine-only validation with a message that
  falsely blames --visibility.
- DescribeDiversion distinguishes a complete credential (records as the
  machine) from an incomplete one (one var set: diverted but nothing records).
- kcap status prints the machine line INSTEAD of the token-store line when a
  credential is present, so a runner never shows both 'records as the machine'
  and 'not authenticated (run: kcap login)'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…te kcap status machine-auth line

Addresses qodo rule violation: user-facing CLI changes must update README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fallback

Codex round 2: the flag-default help implied it always inherits the profile
default; note the fallback for a profile value a machine cannot record with.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

Codex code review: complete (findings fixed).

Round 1 found three should-fix issues, all addressed:

  1. A profile with default_visibility: project would have blocked machine create with a message falsely blaming --visibility. Fixed by falling back to org_public (a machine can't be a project member) with a provenance explaining why — rather than adding project to the machine allowlist, which would let a machine adopt a viewer-less visibility.
  2. kcap status/DescribeDiversion claimed "records as the machine" when only one of KCAP_CLIENT_ID/KCAP_CLIENT_SECRET was set — but an incomplete credential is diverted-then-refused and records nothing. Now reports an incomplete credential honestly.
  3. kcap status could print both "records as the machine" and "not authenticated (run: kcap login)". The machine line now replaces the token-store line.

Round 2 confirmed the three fixes and flagged one help-text nit (document the projectorg_public fallback), also fixed.

Qodo's two findings are resolved: the README rule violation (README updated) and the project-blocked bug (same as codex #1, fixed via the fallback — reply left on the thread explaining why the allowlist approach it suggested would be incorrect).

@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

Windows CI red is the known AI-1848 flake, not this change.

Build and test (windows-latest) failed on SetupFunnelTests.No_funnel_event_collides_with_a_server_event_name (Expected 12 but found 0 — an empty telemetry sink). This is the flake tracked in AI-1848 ("kcap-cli Windows CI lane flaky-red since #501: telemetry funnel/update-check tests fail serially with empty sinks") — process-global-state leakage between serially-ordered telemetry tests, Windows-ordering-specific, a different funnel/update-check test each run. It fails on main's own runs and every open PR inherits it.

Evidence it's not this PR:

  • ubuntu-latest passed on the identical commit; both AOT publish checks and the No-Linear-IDs check passed.
  • This PR's diff (MachineCommand / MachineAuth / StatusCommand / help-machine.txt / README + two unit tests) has zero relationship to telemetry/funnel code.
  • The failing test lives in test/…/Telemetry/SetupFunnelTests.cs, untouched here.

Reran the failed Windows job to confirm it's intermittent. It is not fixable from this PR (AI-1848 has its own owner).

@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

Rerun confirms AI-1848 (not this PR): the Windows lane failed again on a different telemetry test — SetupFunnelTests.Provisioning_failure_carries_a_reason, with CliTelemetry did not enable after Initialize("setup") (process-global telemetry-state leakage from a serially-earlier test, the exact mechanism AI-1848 describes). "Provisioning" is a funnel telemetry event here, not machine provisioning. A different Telemetry/SetupFunnelTests.cs test fails each run, so re-running won't clear it — the lane stays red until AI-1848 is fixed (owned separately). ubuntu, both AOT checks, and No-Linear-IDs remain green. No further reruns from here.

@realtonyyoung
realtonyyoung merged commit 184040e into main Aug 10, 2026
9 of 11 checks passed
@realtonyyoung
realtonyyoung deleted the tonyyoung/machine-visibility-profile-default branch August 10, 2026 22:06
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.

1 participant