Skip to content

Apply the visibility answer the Agents screen already recorded - #680

Open
George-Payne wants to merge 6 commits into
mainfrom
georgepayne/ai-2282-apply-default-visibility
Open

Apply the visibility answer the Agents screen already recorded#680
George-Payne wants to merge 6 commits into
mainfrom
georgepayne/ai-2282-apply-default-visibility

Conversation

@George-Payne

Copy link
Copy Markdown
Member

AI-2282

What & why

The Agents step asks who may read the sessions this machine records from now on, records it on FirstRunAgentsDecidedEvent, and serves it on the poll as default_visibility. No CLI read it: the field was absent from Capacitor.Cli.Core/FirstRun's wire models, so it was dropped at deserialisation, and kcap setup's step 3 then prompted unconditionally and wrote its own answer over it — the one place in the flow that asked a question and discarded the answer.

It now rides the Agents answer, since it comes from the same decision and the same timestamp, and step 3 spends it instead of asking again.

Where to look

  • The two nulls are not the same, and this is the part worth reading. The field is null both when the step is unanswered and when it was answered and left unset. Only the first should reach the prompt: the prompt's cursor starts on org_public, so a Return on a re-run would widen an existing private on a question the user had already answered. An answered-but-unset screen therefore re-writes what the profile already holds — the lane's contract for a null answer, and a no-op downstream. SetupCommand.DecideVisibility is the single place that decides, extracted because HandleSetupAsync's interactive branches have no coverage at all (every HandleAsync_* test drives --no-prompt, which never reaches the browser leg).
  • Validated against AppConfig.ValidVisibilities, never forwarded. This is the one field on the lane that persists locally: it lands in profile config and is stamped on every session afterwards, so a stop a newer server invented would be written to a file this build owns and read back by something whose meaning for it cannot be checked here.
  • No precedence rule against --default-visibility. That flag is read only under --no-prompt, and the browser leg is skipped there entirely, so the two can never both be present. Interactively the flag is ignored today, before and after this change; making it live is a separate behaviour change.
  • The stop labels moved into one VisibilityLabel so the prompt's converter and the browser-answer line cannot describe the same stop differently.

Verification

Core 2425, Cli 3718, App 1161, Integration 233 — all pass, bar two unrelated timing tests that pass in isolation (Callback_fault_losing_to_dispose_is_logged_once_and_run_settles_detached, local IPC; and Codex_StdoutHandshake_UnaffectedByALargeUnreachableSpoolBacklog). dotnet publish -c Release clean, no IL2026/IL3050.

Five mutations, each killed. On the boundary: ignoring the validation, and never reading the field — both needed reshaping to compile, since dropping the reference trips IDE0005/IDE0051 as errors, which is analyzer protection rather than coverage. On the rule: an answered-but-unset screen falling through to the prompt, an unsettled screen treated as answered, and the kept branch inventing a fallback instead of carrying the profile's value.

The field is round-tripped through the source-generated JSON context against WireMock, present and absent — nothing else covered that, since every other test builds FirstRunFlowResponse directly, so a naming or AOT-binding slip would have left the profile untouched for ever with the suite green.

The field was absent from the wire models, so the flow asked who may read
future sessions and step 3 overwrote the answer seconds later. It rides the
Agents decision, so it is read off the same answer and validated against the
CLI closed set rather than forwarded: the value lands in profile config and
is stamped on every session after.
Falling through to the prompt does not leave the profile alone: its cursor
starts on org_public, so a Return on a re-run widens an existing private on
a question already answered. Whether the step settled separates the two
nulls, and DecideVisibility is extracted because step 3 interactive branches
had no coverage at all.
@George-Payne George-Payne self-assigned this Aug 26, 2026
@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

AI-2282

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Apply browser visibility choice during CLI setup

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Preserve the Agents screen’s visibility choice through polling and setup.
• Keep existing profile visibility when an answered screen leaves visibility unset.
• Validate browser values locally and cover serialization, decision, and labeling behavior.
Diagram

graph TD
  A["Agents screen"] -->|returns choice| B["Flow response"] -->|validates value| C["Agents answer"] -->|selects behavior| D["Setup step 3"] -->|writes default| E["Profile config"] -->|stamps visibility| F["Future sessions"]
Loading
High-Level Assessment

The chosen approach is appropriate: visibility belongs on the existing Agents answer because both values share one decision and timestamp. Validating against AppConfig.ValidVisibilities avoids persisting unsupported server values, while the extracted decision function cleanly distinguishes a settled null from an unanswered step. A separate visibility reader or parallel enum would duplicate gating and validation without improving behavior.

Files changed (10) +375 / -12

Bug fix (4) +92 / -11
FirstRunAgentsAnswer.csCarry default visibility on Agents answers +11/-3

Carry default visibility on Agents answers

• Extends FirstRunAgentsAnswer with an optional DefaultVisibility value. Clarifies that declining all agent harnesses is independent from choosing a future-session audience.

src/Capacitor.Cli.Core/FirstRun/FirstRunAgentsAnswer.cs

FirstRunFlowModels.csDeserialize default_visibility from flow polling +14/-0

Deserialize default_visibility from flow polling

• Adds the default_visibility JSON property to FirstRunFlowResponse so the browser decision survives source-generated deserialization.

src/Capacitor.Cli.Core/FirstRun/FirstRunFlowModels.cs

FirstRunFlowOutcomes.csValidate and map the browser visibility +14/-1

Validate and map the browser visibility

• Maps default_visibility into FirstRunAgentsAnswer only when it belongs to AppConfig.ValidVisibilities. Unknown, empty, or absent values degrade to null rather than being persisted locally.

src/Capacitor.Cli.Core/FirstRun/FirstRunFlowOutcomes.cs

SetupCommand.csApply or preserve visibility without re-prompting +53/-7

Apply or preserve visibility without re-prompting

• Uses the settled Agents answer during setup step 3, applying a valid browser choice or retaining the current profile when unset. Extracts the decision rule and shared visibility labels while preserving prompting for unanswered flows and existing no-prompt behavior.

src/Capacitor.Cli/Commands/SetupCommand.cs

Tests (3) +159 / -0
FirstRunFlowClientTests.csTest default visibility JSON deserialization +46/-0

Test default visibility JSON deserialization

• Adds WireMock-backed polling tests for present and absent default_visibility fields, covering the source-generated JSON context and outcome mapping.

test/Capacitor.Cli.Core.Tests.Unit/FirstRun/FirstRunFlowClientTests.cs

FirstRunFlowOutcomesTests.csTest visibility validation and outcome mapping +50/-0

Test visibility validation and outcome mapping

• Covers every supported visibility, null and empty values, unsupported server values, and the independent handling of visibility when all agent harnesses are declined.

test/Capacitor.Cli.Core.Tests.Unit/FirstRun/FirstRunFlowOutcomesTests.cs

SetupCommandTests.csTest setup visibility decisions and labels +63/-0

Test setup visibility decisions and labels

• Covers applying browser choices, preserving existing profile values for settled null answers, prompting for unsettled screens, and labeling every supported visibility consistently.

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

Documentation (3) +124 / -1
README.mdDocument browser-driven default visibility +3/-1

Document browser-driven default visibility

• Explains that setup step 3 reports the Agents screen’s visibility choice instead of asking again. Clarifies that an answered-but-unset choice preserves the existing profile, while an unanswered screen still prompts.

README.md

CHANGES.mdRecord the visibility propagation behavior +24/-0

Record the visibility propagation behavior

• Adds a change note describing the missing CLI wire field, local validation, null-state distinction, and separation from harness decline and no-prompt flag behavior.

docs/CHANGES.md

2026-08-26-ai2282-default-visibility-cli-half-design.mdAdd AI-2282 visibility design specification +97/-0

Add AI-2282 visibility design specification

• Documents the problem, placement on the Agents answer, closed-set validation, settled-null semantics, command-line precedence analysis, copy reuse, and test strategy.

docs/superpowers/specs/2026-08-26-ai2282-default-visibility-cli-half-design.md

@qodo-code-review

qodo-code-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. DefaultVisibility null contract misstated ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new XML comment says an unanswered Agents step does not ask for a default, but
DecideVisibility prompts when no Agents answer exists. This inaccurate contract can cause future
callers to collapse the two null cases the PR deliberately keeps distinct.
Code

src/Capacitor.Cli.Core/FirstRun/FirstRunFlowModels.cs[R216-218]

+    /// <para><b>Null means leave the profile alone</b>, and it is null in two situations this build must
+    /// not tell apart: the step is unanswered, and the user declined everything. Neither asks for a
+    /// default.</para>
Evidence
PR Compliance ID 17 requires comments to remain true using the current source. The cited comment
says an unanswered step does not ask for a default, while DecideVisibility returns null for a
missing browser answer, which the setup branch uses to display the prompt.

CLAUDE.md: Write Only Current, Necessary, and Concise Comments
src/Capacitor.Cli.Core/FirstRun/FirstRunFlowModels.cs[216-218]
src/Capacitor.Cli/Commands/SetupCommand.cs[1248-1253]

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

## Issue description
Correct the `DefaultVisibility` XML documentation so it distinguishes an unsettled Agents step from a settled answer with no visibility.

## Issue Context
An unsettled step reaches the terminal prompt, while a settled answer with null visibility preserves the profile value. The current comment incorrectly says neither asks for a default.

## Fix Focus Areas
- src/Capacitor.Cli.Core/FirstRun/FirstRunFlowModels.cs[216-218]

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


2. Test comment cites finding ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The added test comment describes the branch as existing for a prior “finding,” embedding review
history rather than stating only the live behavior being pinned. The remaining prompt-default
rationale is sufficient without that review artifact.
Code

test/Capacitor.Cli.Tests.Unit/Commands/SetupCommandTests.cs[180]

+    // The finding this branch exists for: falling through to the prompt does NOT leave the profile
Evidence
PR Compliance ID 18 prohibits review artifacts and PR-finding narration in comments. The added
phrase The finding this branch exists for explicitly records review provenance rather than a
current code constraint.

CLAUDE.md: Exclude Historical and Review Metadata from Comments Except for Live Compatibility, Actionable Work, or Measurements
test/Capacitor.Cli.Tests.Unit/Commands/SetupCommandTests.cs[180-182]

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

## Issue description
Remove the review-artifact wording from the test comment while retaining the timeless behavioral rationale.

## Issue Context
The comment should explain that prompting can widen `private` to `org_public`; it should not say the branch exists for a prior finding.

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/Commands/SetupCommandTests.cs[180-182]

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


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This is a behavioral change spanning wire deserialization, visibility validation, setup branching, profile persistence, and multiple test paths; it has meaningful localized risk but not enough independent logic density to justify extended review.

Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.Cli.Core/FirstRun/FirstRunFlowModels.cs Outdated
Comment thread test/Capacitor.Cli.Tests.Unit/Commands/SetupCommandTests.cs Outdated
Its claim is that the attempt gave up rather than waiting the 30s hold, but
its bound was the hook 5s budget itself, so a loaded runner overshot it by
240ms with the spool correct. The two sibling tests for the same claim use
15s and 10s; this brings the outlier into line, still well under the hold.
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