Skip to content

Host Antigravity CLI agents: posture, queued input, and the reviewer split - #482

Merged
realtonyyoung merged 7 commits into
mainfrom
claude-tyoung/ai-1412-hosted-agy
Aug 7, 2026
Merged

realtonyyoung merged 7 commits into
mainfrom
claude-tyoung/ai-1412-hosted-agy

Conversation

@realtonyyoung

@realtonyyoung realtonyyoung commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

Makes agy usable as a hosted dashboard agent, alongside the unattended reviewer that landed in #479. One factory now serves both, so most of this is getting that split right.

Fixes AI-1412

Server-side half: kurrent-io/kcap-server#1347 (merged).

The split

hosted reviewer
--dangerously-skip-permissions ✅ ❌
KCAP_ANTIGRAVITY_UNATTENDED_REVIEWER consent — ✅
kcap-flow-result MCP + result-channel validation — ✅
platform / binary / version floor ✅ ✅
isolated per-launch HOME ✅ ✅

The flag is the entire read boundary; the owned worktree is not one. Measured on 1.1.10: with it, an absolute out-of-workspace view_file succeeds; without it, refused with a typed tool_info.error. Claude is the precedent for a single-axis no-prompt posture existing at all (Codex's is two axes, so its no-prompt mode still sits on a sandbox) — but explicitly not for which launch kind gets it, since Claude widens its reviewer and prompts on interactive, the mirror image of this.

Consent is reviewer-only. Its justification is cross-principal exposure — a reviewer returns what it read to whoever requested the review. A hosted launch has no such exposure: DaemonRegistry is keyed (TeamId, OwnerUserId, Name) and CapacitorHub resolves the daemon from the caller's own id, so the launcher is the owner. Hosted agy ships on by default, per the owner's decision.

The isolated HOME stays for hosted too, and not for the reason originally supposed. Four piped runs under a real HOME reached EOF in 6–16s, so the "capture hooks wedge the turn's stdout" premise doesn't hold. What the probe did find: every run was also captured by the hook lane as a second watcher session, while the runtime already forwards its own transcript. An inherited HOME duplicates capture rather than adding it.

Also here

  • Bounded input queue. Input sent mid-turn is delivered in order as the next turn. Fixing this uncovered a real defect: the channel used BoundedChannelFullMode.DropWrite, where TryWrite returns true and silently discards — so the existing "queue full" branch was unreachable dead code and an over-cap message vanished without a log line. Over-cap now faults the send and writes a system_note, one per rejected message.
  • kcap agent attach refused by name, daemon-side on !EmitsTerminalOutput — the client sends an id verbatim and cannot know the vendor. Replaces a blank-screen hang; the remove-the-refusal mutant fails by timeout, reproducing it.
  • A PR-review launch is refused. LaunchKind.Review is wire-reachable independently of vendor (CapacitorHub takes vendor and kind as separate client arguments and rejects only the Codex pair), and would otherwise have got the hosted arm with no review tools and no error.
  • A rejected input no longer advances the liveness clock — the queue is full only because a turn is wedged, so retries were refreshing the very attestation that would report it idle.

Testing

253 Antigravity, 377 across all reviewer suites, 53 DaemonRunner — all green; daemon AOT clean. Every guard mutation-verified.

Two worth calling out. The consent fix's one production line was untested — reverting it passed 248/0, because nothing invokes DaemonRunner.RunAsync and the test called the seeding helper directly, pinning the directory but not the conditionality. Hoisted into SeedReviewerFloors so the asymmetry is the thing under test. And one mutant "survived" six runs having never compiled: a scripted edit left the source mtime not newer than the last build output, so MSBuild skipped it.

One accepted trade is documented in ReviewerVersionStore: antigravity's floor is seeded from the binary resolving rather than from a consent event, because hosted launches need one without consent. That leaves a narrow window where a downgrade below the pre-consent build is admitted; the remedy is kcap daemon reviewer affirm --vendor antigravity.

🤖 Generated with Claude Code

realtonyyoung and others added 5 commits August 7, 2026 11:16
Lifts the interactive-launch refusal and passes
--dangerously-skip-permissions on the hosted arm only.

The refusal claimed an inherited HOME lets agy's capture hooks spawn a
watcher that holds the turn's stdout open, wedging every turn. Measured:
four piped `agy -p ... --output-format stream-json | cat` runs under a real
HOME, on a kcap predating the descriptor fix, all reached EOF in 6-16s, with
the hook log and the real-HOME transcript present as positive controls. The
wedge does not reproduce. What those runs did show is that each was also
recorded by the hook lane as its own watcher session, while this runtime is
already the transcript source (it implements IAcpTranscriptSource and the
factory returns it as Transcript) - so an inherited HOME would duplicate
capture rather than add it. The per-launch isolated HOME is therefore kept
for hosted launches too.

A hosted agent exists to do work: without the flag, agy soft-denies shell
and out-of-workspace operations while still exiting 0, so it merely looks
broken. Measured on agy 1.1.10, that flag is the read boundary and the
daemon-owned worktree is not - with it, an absolute out-of-workspace
view_file succeeds; without it the same read is refused with a typed
tool_info.error. The comments say so rather than claiming worktree
containment. The precedent cited is Claude (--permission-mode
bypassPermissions, single-axis, already shipped) and deliberately not Codex,
whose posture is two axes and still sits on a sandbox with prompting off.

A reviewer reads an owned worktree and nothing else, so it keeps the
soft-deny. Both directions are pinned as whole argv vectors in one test.

The vendor ladder (consent, platform, recorded build minimum) still applies
to every launch including an interactive one, since the containment it
protects is the isolated home both shapes rely on; a test pins that too.
Task 3 lifted the interactive-launch refusal, but StartAsync still ran the
whole reviewer ladder for both shapes. A hosted agy launch on a daemon that
had never set KCAP_ANTIGRAVITY_UNATTENDED_REVIEWER therefore failed with
antigravity_unattended_reviewer_disabled - a review complaint about a launch
that is not a review, and a consent gate the owner explicitly declined for
hosted agy (ship on by default, like Claude/Codex/Cursor).

Consent is reviewer-only because its own justification is cross-principal: an
unattended reviewer runs under the daemon user's authority and returns what it
read to whoever requested the review, who need not be the operator. A hosted
launch has no counterpart - the server's DaemonRegistry is keyed
(TeamId, OwnerUserId, Name), GetDaemonsForRepoAsync takes the caller's own
normalized user id, and the launch hub resolves the daemon with that same id,
so the launcher IS the daemon's owner (verified in kcap-server, read-only).

Platform, binary presence and the recorded build minimum still gate BOTH
shapes: they protect the per-launch isolated HOME, which a hosted launch
depends on exactly as a review does. One ladder takes the launch shape as a
parameter (LaunchRefusal(bool reviewFlow)) rather than splitting into two that
must agree - a shape this file has already paid for once. Advertisement keeps
the full ladder including consent, since advertising IS the offer to review
unattended and the server refuses an unadvertised reviewer.

The daemon now seeds this vendor's version floor whenever agy resolves, not
from the reviewer consent event. Without that the fix would be worthless:
seeded from consent, a consent-less daemon records no minimum, so every hosted
launch would refuse as version_no_minimum instead - the same gate removed from
the front of the ladder and reinstated behind it. SeedVersionFloor is a
separate name rather than SeedReviewerAffirmation(..., enabled: true), because
a literal true between two config.XUnattendedReviewerEnabled siblings reads as
an oversight and would be tidied back.

Denial text, coded tokens unchanged (they are load-bearing for tests, the
server's failure reasons and the README):
- version_no_minimum no longer tells the operator to restart with the reviewer
  flag set. That is wrong for a hosted launch and, after the seeding change,
  stale for a reviewer too.
- unsupported_platform, version_below_minimum, auth_unavailable and
  requires_owned_worktree now describe the containment rather than the
  reviewer. requires_owned_worktree in particular guards on ctx.Work, not
  IsReviewFlow, so it must not call the launch a review.
- The launch log line drops "reviewer" and carries reviewFlow instead.
- Left alone: binary_missing, version_unresolved, version_incomparable,
  launch_timeout and launch_failed were already shape-neutral, and the
  consent refusal itself is now correct because only a review can reach it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A turn is one `agy -p` process and between turns there is no process at all,
so mid-turn input cannot steer the running turn: it has to become the NEXT
turn. That already worked in order, and never coalesced - the tests here pin
it, since a concatenated pair of user messages is unrecoverable rather than
untidy - but the over-cap half was broken in the way that is hardest to see.

The pending-turns channel used BoundedChannelFullMode.DropWrite, under which
TryWrite returns TRUE and discards the item. So the "queue full" branch was
unreachable: an over-cap message vanished with not even a log line, let alone
anything the user could see. FullMode.Wait (still TryWrite, never WriteAsync)
makes the rejection observable without blocking - blocking would stall the
daemon's serial command lane behind the very stuck turn that filled the queue.

An over-cap send now faults for EVERY caller, not just one that asked for a
write ack (the ack-less path is every server-driven SendInput, i.e. exactly
the caller that would otherwise never learn), and emits a system_note onto the
transcript, which is the only surface the person who typed the message sees.
One note per rejected message: each is a separately lost message. A TERMINAL
runtime stays as it was - its transcript channel is already completed, so no
note could be delivered, and the session ending is its own signal.

`kcap agent attach` against a runtime that emits no terminal output attached
the client to a blank screen that never repaints, and only admitted the problem
if the user typed - indistinguishable from a wedged daemon. It is now refused
by name, identifying the vendor and pointing at the dashboard. Enforced in the
daemon, not the CLI: attach sends a full agent id verbatim without ever
fetching the agent table, so the client cannot know the vendor. That covers
hosted agy and the ACP vendors alike, which have the same empty output buffer.
The raw-input NotSupportedException path stays covered as defence in depth for
a runtime whose two answers disagree.
StartAsync serves both a hosted agent and a review-flow reviewer, but it
injected the kcap-flow-result MCP channel unconditionally — and validated
that channel's inputs unconditionally too, so a hosted launch could be
refused with antigravity_reviewer_result_channel_incomplete for a channel
it has no flow to report to, and the definition's MCP allowlist could
refuse it for a definition it does not have.

Both the injection and its fail-closed validation now sit behind
ctx.IsReviewFlow, in one BuildReviewFlowMcp helper — the same split
AcpHostedAgentRuntimeFactory.ValidateAndBuildReviewFlowMcp already draws.
The hosted arm forwards ctx.McpServers, mirroring that factory's hosted
arm; nothing populates it today, so a hosted launch's surface is empty
deliberately rather than by a drop. The isolated home still gets a valid
mcp_config.json either way — empty for hosted, which is what replaces the
operator's global config and keeps the surface closed.

Tests pin both directions: a hosted launch with no result-channel inputs
succeeds with an exactly-empty config; a review missing them still fails
closed with the coded reason, injects the channel with its command and
both env vars, materializes an auto-approvable allowlist entry, refuses a
flow-starting one, and never forwards a caller-supplied server.
…rejection faking liveness

Six review findings on the hosted-Antigravity branch.

1. The one production line the consent-less-seeding commit exists to change was
   untested: swapping SeedVersionFloor back to SeedReviewerAffirmation(...,
   config.AntigravityUnattendedReviewerEnabled, ...) survived the whole
   Antigravity and DaemonRunner suites. Nothing invokes DaemonRunner.RunAsync
   from a test (it builds the entire DI host), and the existing test called
   SeedVersionFloor by hand -- pinning the directory shape but not the
   conditionality, which is the half that changed. The three seeding calls now
   sit in SeedReviewerFloors(stateDir, config), which RunAsync invokes and the
   tests drive: the hosted-launch test goes through it, and a new sibling asserts
   the asymmetry directly (antigravity records a floor without consent; kiro and
   gemini do not, with all three binaries resolvable so the siblings' absence is
   the consent condition and not a missing stub). Both mutants are red.

2. Antigravity's floor is no longer anchored to a consent event, so a
   pre-consent build stays the minimum across an upgrade and a later downgrade
   to it is admitted. Accepted deliberately -- hosted launches need a floor
   without consent, the window is bounded to builds at or above the pre-consent
   one, and the affirm verb is the remedy -- and recorded as an explicit
   per-vendor exception on ReviewerVersionStore, whose contract it bends, with a
   pointer from DaemonReviewerCommand and an operator-facing note in the README.
   The gate is not weakened and consent does not re-seed.

3. BuildTurnPsi's doc cited Claude as precedent for widening a hosted launch.
   Claude's split runs the other way: ClaudeLauncher.BuildArgs adds
   --permission-mode bypassPermissions only inside its IsReviewFlow branch, and
   DisablesApprovalPrompts is IsReviewFlow && Work == OwnedWorktree. A
   maintainer "aligning with the precedent" would move the flag onto the reviewer
   arm -- the one direction this split exists to prevent. Rewritten to claim only
   what is true: Claude is the precedent for a single-axis no-prompt posture
   existing at all (unlike Codex's Sandbox x Approval), and explicitly not for
   which launch kind receives it.

4. LaunchKind.Review silently took the hosted arm. The lift keys on
   !IsReviewFlow, true for Default AND Review, and only PtyHostedAgentRuntimeFactory
   builds the review MCP config and prompt -- so a PR review would have run with
   --dangerously-skip-permissions, an empty MCP surface and no review prompt.
   Refused up front with antigravity_pr_review_unsupported, ahead of the
   containment ladder since no install or affirmation could clear it. The shipped
   UI cannot request it (the server hard-codes Claude, and the dialog has no
   vendor picker), but the hub takes vendor and kind as independent client
   arguments and rejects only Codex+Review, so it is wire-reachable. The factory
   doc no longer claims the context carries two launch shapes.

5. A rejected input advanced the liveness clock: the full-queue notice went out
   through EmitEnvelope, which advances first. The queue is full only because a
   turn is wedged, so a user's retries against a stuck agent refreshed the very
   attestation that would report it idle -- bounded for a reviewer by its TTL arm,
   unbounded for a hosted agent. Daemon-authored notices now go through
   EmitDaemonNotice, which writes identically but does not advance. Agent output
   is unchanged, pinned in both directions.

6. Dropped a racy decorative assertion whose comment claimed it proved "a note
   per rejection, not per send" -- notes.Count == 2 already carries that, and
   HandleInit resolves the conversation-id TCS before emitting session_started
   with RunContinuationsAsynchronously, so it was a load-sensitive false red
   rather than a detector.

Antigravity 248/0, *Reviewer* 372/0, DaemonRunner 53/0 (5 skipped, all live-cert
gated). Three CodexLauncher Prepare_* failures under *Launch* are inherited --
reproduced identically on a detached cf63758 worktree. Daemon AOT publish clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 7, 2026

Copy link
Copy Markdown

AI-1412

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Enable hosted Antigravity (agy) agents with correct posture and launch gating

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

Grey Divider

AI Description

• Split Antigravity launch policy between hosted agents and unattended reviewers (consent, tools,
 permissions).
• Fix hosted agy input handling with a rejectable bounded queue and user-visible system notes.
• Refuse unsupported attach/launch shapes (no-terminal attach; PR-review kind) and expand
 coverage/tests.
Diagram

graph TD
  A["DaemonRunner"] --> B["Seed version floors"] --> C["ReviewerVersionStore"]
  A --> D["Antigravity runtime factory"] --> E["LaunchRefusal ladder"] --> F["Antigravity capability"]
  D --> G["Hosted agent runtime"]
  H["Local IPC attach"] --> I{"Emits terminal?"} -->|"no"| J["Refuse w/ vendor"]
  H -->|"yes"| K["Attach loop"]

  subgraph Legend
    direction LR
    _svc([Service/Component]) ~~~ _dec{"Decision"}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Split into separate hosted vs reviewer factories
  • ➕ Clearer separation of policy; fewer conditionals/parameters in a single ladder
  • ➕ Harder to accidentally allow PR-review/incorrect MCP wiring via shared code paths
  • ➖ Duplicated gating logic and denial text wiring (risk of drift between advertisement and launch)
  • ➖ More surface area to keep in sync as vendors evolve
2. Make permission posture configurable instead of hard-coded for hosted
  • ➕ Lets operators choose prompt vs bypass behavior per environment/security posture
  • ➕ Avoids coupling hosted functionality to a single vendor flag semantics
  • ➖ Increases config complexity and support burden; harder to reason about security guarantees
  • ➖ Risk of misconfiguration causing silent hosted failures (the issue this PR fixes)
3. Introduce an explicit sandbox/containment substrate for hosted agy
  • ➕ Could provide a true read boundary independent of --dangerously-skip-permissions semantics
  • ➕ Reduces reliance on vendor behavior and minimum-version flooring
  • ➖ Significant engineering effort; likely OS-specific; may not be feasible for exec-per-turn CLI model
  • ➖ Would delay shipping the hosted lane; still needs policy split + consent semantics

Recommendation: The PR’s approach (one factory + one parameterized ladder, with review-only consent and review-only MCP injection) is the best tradeoff for correctness and drift-prevention. It keeps a single source of truth for refusals/denial reasons while explicitly guarding the third launch kind (PR review) and preserving shared containment requirements (isolated HOME + version floor) across both shapes.

Files changed (13) +1399 / -150

Enhancement (4) +275 / -90
ReviewerVersionStore.csExplain Antigravity’s non-consent-anchored floor seeding exception +19/-0

Explain Antigravity’s non-consent-anchored floor seeding exception

• Adds detailed documentation describing why Antigravity’s minimum version is seeded on binary resolution (to support hosted launches) and the resulting bounded “upgrade then consent” window. Clarifies operator remedy via 'kcap daemon reviewer affirm --vendor antigravity'.

src/Capacitor.Cli.Core/ReviewerVersionStore.cs

AntigravityReviewerCapability.csClarify shared gating texts for hosted vs reviewer and improve denial wording +25/-13

Clarify shared gating texts for hosted vs reviewer and improve denial wording

• Updates comments and denial messages to reflect that most arms (platform/minimum) apply to hosted launches too, while consent is reviewer-only. Adjusts denial reasons so hosted operators aren’t misdirected to reviewer consent when the issue is version-floor seeding.

src/Capacitor.Cli.Daemon/Acp/AntigravityReviewerCapability.cs

DaemonRunner.csCentralize seeding of per-vendor version floors and seed Antigravity unconditionally +51/-15

Centralize seeding of per-vendor version floors and seed Antigravity unconditionally

• Replaces inline per-vendor seeding calls with 'SeedReviewerFloors' to make the consent/asymmetry explicit and testable. Adds 'SeedVersionFloor' helper used both from consent-gated seeding (Kiro/Gemini) and unconditional seeding (Antigravity).

src/Capacitor.Cli.Daemon/DaemonRunner.cs

AntigravityHostedAgentRuntimeFactory.csServe both hosted and review-flow Antigravity with a parameterized gate ladder +180/-62

Serve both hosted and review-flow Antigravity with a parameterized gate ladder

• Refactors refusal logic into 'LaunchRefusal(reviewFlow)' so consent is enforced only for review-flow while platform/binary/minimum apply to both. Adds explicit refusal for PR-review launch kind, scopes MCP result-channel injection + allowlist validation to review-flow only, and widens hosted launches with '--dangerously-skip-permissions'.

src/Capacitor.Cli.Daemon/Services/AntigravityHostedAgentRuntimeFactory.cs

Bug fix (2) +93 / -26
AgentOrchestrator.LocalIpc.csRefuse 'kcap agent attach' for agents that emit no terminal output +12/-0

Refuse 'kcap agent attach' for agents that emit no terminal output

• Adds a daemon-side guard in local attach to immediately return an error for hosted/ACP-style runtimes whose stdout is protocol traffic (no terminal output). Prevents blank-screen hangs and provides a vendor-specific message directing users to the dashboard.

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.LocalIpc.cs

AntigravityHostedAgentRuntime.csMake queued input rejectable and user-visible when over capacity +81/-26

Make queued input rejectable and user-visible when over capacity

• Fixes bounded-channel configuration so 'TryWrite' can fail when full (Wait mode), enabling correct full-queue handling. Over-cap inputs now fault the send task and emit a per-message 'system_note' without advancing the activity clock, preserving liveness semantics for stuck turns.

src/Capacitor.Cli.Daemon/Services/AntigravityHostedAgentRuntime.cs

Tests (5) +981 / -32
AntigravityReviewerCapabilityTests.csUpdate tests for revised ‘no minimum’ denial guidance +14/-5

Update tests for revised ‘no minimum’ denial guidance

• Adjusts expectations so 'version_no_minimum' guidance references restart/affirm without mentioning the reviewer consent flag. Renames comments to reflect 'LaunchRefusal' terminology and shared gating concerns.

test/Capacitor.Cli.Tests.Unit/Acp/AntigravityReviewerCapabilityTests.cs

AgentOrchestratorLocalAttachTests.csAdd tests for daemon-side attach refusal and real-socket behavior +146/-9

Add tests for daemon-side attach refusal and real-socket behavior

• Adds unit tests asserting attach is refused by name for no-terminal runtimes (including over the real Unix socket) and that refusal is immediate and vendor-identified. Refactors existing tests to separate the “no terminal” refusal from the “raw input unsupported” defense-in-depth path.

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

DaemonRunnerAntigravityFloorTests.csTest Antigravity unconditional floor seeding and sibling consent behavior +158/-0

Test Antigravity unconditional floor seeding and sibling consent behavior

• Adds tests verifying consent-less daemons still seed Antigravity’s floor (admitting hosted launches) while Kiro/Gemini do not seed without consent. Adds a positive test confirming Kiro/Gemini seed correctly when consent is enabled, and drives tests through 'SeedReviewerFloors' to pin vendor asymmetry.

test/Capacitor.Cli.Tests.Unit/Daemon/DaemonRunnerAntigravityFloorTests.cs

AntigravityReviewerLaunchTests.csExpand launch tests for hosted/reviewer split, MCP injection scoping, and PR-review refusal +461/-14

Expand launch tests for hosted/reviewer split, MCP injection scoping, and PR-review refusal

• Adds extensive coverage for hosted vs review-flow behavior: permission widening hosted-only, consent gating review-only, shared platform/binary/minimum checks, and corrected borrowed-workspace refusal wording. Introduces tests asserting review-flow-only MCP result-channel injection/allowlist validation and explicit refusal of PR-review launch kind.

test/Capacitor.Cli.Tests.Unit/Services/AntigravityReviewerLaunchTests.cs

AntigravityRuntimeLifecycleTests.csTest ordered mid-turn input queuing and visible rejection semantics +202/-4

Test ordered mid-turn input queuing and visible rejection semantics

• Adds deterministic tests asserting inputs sent during an in-flight turn become subsequent turns in order. Strengthens full-queue behavior tests: over-cap sends now throw, produce per-message 'system_note's, and do not advance the activity clock; introduces a gated turn fake for deterministic mid-turn state.

test/Capacitor.Cli.Tests.Unit/Services/AntigravityRuntimeLifecycleTests.cs

Documentation (2) +50 / -2
README.mdDocument hosted Antigravity posture, PR-review refusal, and attach refusal behavior +46/-2

Document hosted Antigravity posture, PR-review refusal, and attach refusal behavior

• Expands Antigravity reviewer docs to cover hosted agents: unconditional skip-permissions on hosted, reviewer-only consent, shared minimum-version floor rationale/window, and shared isolated HOME behavior. Adds documentation for refusing 'kcap agent attach' on agents with no terminal output and for refusing Antigravity PR-review launches.

README.md

DaemonReviewerCommand.csDocument Antigravity floor-seeding window and affirm command purpose +4/-0

Document Antigravity floor-seeding window and affirm command purpose

• Updates command documentation to note Antigravity’s floor can be seeded without consent (to support hosted), creating a window where 'affirm' is the remedy after enabling the reviewer. Keeps the separation between “affirm build” and “enable reviewer”.

src/Capacitor.Cli/Commands/DaemonReviewerCommand.cs

@qodo-code-review

qodo-code-review Bot commented Aug 7, 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


Remediation recommended

1. Agent file read lacks sharing ✓ Resolved 📘 Rule violation ☼ Reliability
Description
The new test helper reads an agent-written mcp_config.json using File.ReadAllTextAsync, which
opens the file without FileShare.ReadWrite and can deny concurrent writes. This violates the
requirement to use read-sharing I/O for agent-owned artifacts.
Code

test/Capacitor.Cli.Tests.Unit/Services/AntigravityReviewerLaunchTests.cs[R737-741]

+        if (!File.Exists(path))
+            throw new FileNotFoundException($"The launch wrote no mcp_config.json under '{home}'.", path);
+
+        return await File.ReadAllTextAsync(path);
+    }
Evidence
PR Compliance ID 5 forbids reading agent-written artifacts with File.ReadAllText* because it can
deny the agent writer access. The added helper McpConfigOf uses File.ReadAllTextAsync(path) to
read the per-launch mcp_config.json.

CLAUDE.md: Read agent-owned files using read-sharing I/O to avoid denying writes (especially on Windows)
test/Capacitor.Cli.Tests.Unit/Services/AntigravityReviewerLaunchTests.cs[733-741]

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

## Issue description
Agent-owned artifacts should be read with read-sharing semantics to avoid denying concurrent writes (notably on Windows).

## Issue Context
The test helper reads the per-launch `mcp_config.json` via `File.ReadAllTextAsync`, which does not guarantee `FileShare.ReadWrite` and can violate the repo standard for agent-written files.

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/Services/AntigravityReviewerLaunchTests.cs[733-741]

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


2. Verbose vendor-floor XML comment ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
New/updated XML documentation blocks are excessively long and encode design rationale that could be
captured in clearer code structure or external docs. This reduces readability and violates the
project guidance to keep comments concise.
Code

src/Capacitor.Cli.Core/ReviewerVersionStore.cs[R26-29]

+/// <para><b>ONE per-vendor exception: antigravity's record is not anchored to a consent event.</b>
+/// Every other vendor's floor is seeded when its reviewer is turned ON, so the recorded build is the
+/// one installed at the moment the operator consented. Antigravity's floor gates more than its
+/// reviewer — it also gates HOSTED <c>agy</c> launches, which ship on by default and take no consent
Evidence
PR Compliance ID 8 requires comments to be minimal and to prefer self-explanatory code. The added
blocks (e.g., the per-vendor exception and ladder rationale) are multi-paragraph, highly detailed
narratives that exceed concise contextual commentary.

CLAUDE.md: Keep comments concise and prefer self-explanatory code
src/Capacitor.Cli.Core/ReviewerVersionStore.cs[26-43]
src/Capacitor.Cli.Daemon/Services/AntigravityHostedAgentRuntimeFactory.cs[446-473]

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

## Issue description
New comments are excessively verbose, making the code harder to scan and maintain.

## Issue Context
The compliance checklist requires concise comments and prefers self-explanatory code; long design essays in inline comments should be shortened or moved to an ADR/design doc.

## Fix Focus Areas
- src/Capacitor.Cli.Core/ReviewerVersionStore.cs[26-43]
- src/Capacitor.Cli.Daemon/DaemonRunner.cs[845-869]
- src/Capacitor.Cli.Daemon/Services/AntigravityHostedAgentRuntimeFactory.cs[446-473]
- test/Capacitor.Cli.Tests.Unit/AgentOrchestratorLocalAttachTests.cs[459-471]

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


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/Capacitor.Cli.Core/ReviewerVersionStore.cs Outdated
realtonyyoung and others added 2 commits August 7, 2026 11:59
The queue-full rejection logged at Warning, so an operator saw that an input was
refused — but the transcript note is the only thing the person who typed it ever
sees (the faulted send reaches the daemon log, never their screen), and that note
is best-effort: the Terminal check releases the state lock before the emit, so a
TerminateAsync landing in the gap completes the transcript writer and the note is
discarded. The authoritative record therefore said "rejected" while staying silent
about the sender having been told nothing.

Write now reports whether the envelope landed, and the rejection path escalates a
miss to Warning itself. Deliberately not raised inside Write: an envelope arriving
after the channel closed is the ordinary shape of teardown — the turn worker drains
agy's last lines while TerminateAsync completes the writer — so warning there would
fire on every clean stop and train people to ignore the level.

The race itself is left open. Closing it means holding the state lock across a
channel write, and no foreign call under that lock is one of this class's standing
invariants.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The launch test read the per-launch mcp_config.json with File.ReadAllTextAsync.
That file lives in the child's own config dir, so agy may rewrite it, and a
write-denying open is mandatory sharing on Windows — invisible on macOS and Linux,
red only on the Windows CI leg. Reads via WatchCommand.ReadAllTextSharedAsync now,
the same helper the watcher uses for agent-written transcripts.

Also shortens the two longest doc comments this branch added. Both kept the facts
that are not recoverable from the code — that the antigravity floor is seeded from
the binary resolving rather than from consent, and that Claude's own posture split
runs the opposite way so nobody "aligns" this flag onto the reviewer arm — and lost
the surrounding essay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@realtonyyoung
realtonyyoung merged commit 5e84bed into main Aug 7, 2026
6 checks passed
@realtonyyoung
realtonyyoung deleted the claude-tyoung/ai-1412-hosted-agy branch August 7, 2026 16:34
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