Skip to content

[AI-1529] Render workspace diagnostics on every flow surface (CLI) - #380

Merged
realtonyyoung merged 4 commits into
mainfrom
tonyyoung/ai-1529-workspace-render
Jul 28, 2026
Merged

[AI-1529] Render workspace diagnostics on every flow surface (CLI)#380
realtonyyoung merged 4 commits into
mainfrom
tonyyoung/ai-1529-workspace-render

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

The CLI half of AI-1529. The server (kcap-server#1205) now reports whether a reviewer read your working tree or a checkout at the last commit; this PR makes that visible to the caller.

Ships independently of the server side — every field is optional, and a server that doesn't send them renders workspace: unknown.

Why

If a reviewer ran in an owned worktree, it reviewed the last commit, not your working tree. Ask it to review work you haven't committed and it reviewed something else — and today nothing tells you. AI-1527 makes it sharper: context_only_requested is a fallback the caller asked for, and it was equally invisible.

What it renders

workspace: borrowed (the reviewer saw your working tree, uncommitted changes included)

workspace: fallback (not_colocated)
  ⚠ The reviewer read a checkout at the last commit — it did NOT see uncommitted work.

workspace: unknown

One helper, five paths

AppendWorkspaceDiagnostics is reached from all five formatting paths: roundless start, the polled round result, the rounded round response, status, and close.

The tests assert through the real formatters, not the helper. A correct helper is worth nothing if a surface forgets to call it — and the polled path is what an agent reads on nearly every flow, so a parity test written against the blocking path alone would pass while the dominant path stayed silent.

The case that matters most

An older server, a multi-participant run, and a generic single-participant flow all omit the field. Rendering that as borrowed would be the one wrong answer that reads as reassurance, so absent and explicit-null both render unknown, and a test asserts the word "borrowed" appears nowhere in that output.

A mode this build doesn't recognise (a future server, or owned) is reported verbatim rather than collapsed into a known case. Inventing a category is how a caller ends up trusting a decision the server never made.

Pass-through, by construction

No allowlist, no mapping table, on either side. Pinned by a parameterised test whose last case is a_reason_this_cli_has_never_heard_of — a hardcoded example set fails it.

A fallback with no reason still discloses and still warns: an empty () would be worse than none, and swallowing the line entirely would hide the caution.

Also asserted

  • No rendered response leaks borrowed_cwd or any absolute path — that's a real path on the requester's machine; the mode/reason pair is the whole contract.
  • Equivalent payloads render identically whatever alias produced them. The server never sees an MCP alias name, so alias-independence can only be checked CLI-side.

Tool description

The start tool now states that uncommitted-work visibility is conditional on borrowing, names the three values, and scopes the disclosure to the reserved review aliases — generic flow kinds report unknown by design (that gap is AI-1548).


18 new tests, 118/118 across the McpFlows suites, check-linear-ids clean.

The CLI half of AI-1529. The server now reports whether a reviewer read the
caller's working tree or a checkout at the last commit; this makes it visible.

ONE helper (AppendWorkspaceDiagnostics) reached from all five formatting paths:
roundless start, the polled round result, the rounded round response, status and
close. A helper being correct is worth nothing if a surface forgets to call it,
so the tests assert through the REAL formatters rather than the helper -- the
polled path especially, since it is what an agent reads on nearly every flow and
a parity test written against the blocking path alone would pass while the
dominant path stayed silent.

Three outcomes, and the third is the one that matters:

  borrowed  -> "the reviewer saw your working tree, uncommitted changes included"
  fallback  -> "workspace: fallback (<reason>)" plus a caution that the reviewer
               read the last commit and did NOT see uncommitted work
  absent    -> "workspace: unknown", NEVER borrowed

An older server, a multi-participant run and a generic single-participant flow
all omit the field. Guessing "borrowed" there would be the one wrong answer that
reads as reassurance, so absent and explicit-null both render unknown. A mode
this build does not recognise is reported verbatim rather than collapsed into a
known case -- inventing a category is how a caller ends up trusting a decision
the server never made.

Reasons are pass-through: no allowlist, no mapping table. Pinned by a test that
includes a token this CLI has never heard of, which a hardcoded example fails.
A fallback with no reason still discloses and still warns -- an empty
parenthetical would be worse than none, and swallowing the line would hide the
caution.

Also asserted: no rendered response leaks borrowed_cwd or any absolute path, and
equivalent payloads render identically whatever alias produced them (the server
never sees an MCP alias name, so this can only be checked CLI-side).

The start tool description now states that uncommitted-work visibility is
CONDITIONAL on borrowing, names the three values, and scopes the disclosure to
the reserved review aliases.

18 new tests, 118/118 across the McpFlows suites, check-linear-ids clean.
@linear-code

linear-code Bot commented Jul 28, 2026

Copy link
Copy Markdown

AI-1529

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Render workspace diagnostics across CLI flow responses

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Render reviewer workspace provenance across every CLI flow response surface.
• Warn when fallback checkouts exclude uncommitted work; preserve unknown and future values.
• Add formatter-level coverage for compatibility, pass-through behavior, and path privacy.
Diagram

sequenceDiagram
    participant S as Flow Server
    participant F as Flow Formatters
    participant W as Workspace Diagnostics
    actor C as CLI Caller
    S->>F: Optional mode and reason
    F->>W: Roundless start
    F->>W: Polled result
    F->>W: Round response
    F->>W: Status response
    F->>W: Close response
    W-->>F: Provenance or warning
    F-->>C: Rendered flow output
Loading
High-Level Assessment

The shared rendering helper is the appropriate approach because workspace semantics must remain identical across five existing formatter paths. A typed response hierarchy or per-formatter implementations would add churn or drift risk, while an allowlist would undermine forward-compatible pass-through of server-provided modes and reasons.

Files changed (2) +189 / -1

Enhancement (1) +52 / -1
McpFlowsServer.csRender workspace provenance on every flow response surface +52/-1

Render workspace provenance on every flow response surface

• Adds a shared workspace diagnostic renderer for borrowed, fallback, absent, null, and unrecognized modes. Integrates it into roundless start, polled result, round response, status, and close formatting, and expands tool guidance about uncommitted-work visibility.

src/Capacitor.Cli/Commands/McpFlowsServer.cs

Tests (1) +137 / -0
McpFlowsWorkspaceRenderTests.csCover workspace diagnostics through real flow formatters +137/-0

Cover workspace diagnostics through real flow formatters

• Adds formatter-level tests for every response surface and all workspace outcomes. Coverage verifies verbatim fallback reasons, future-mode compatibility, missing-reason warnings, alias-independent output, and prevention of local path disclosure.

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

@qodo-code-review

qodo-code-review Bot commented Jul 28, 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. Workspace diagnostics undocumented ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The CLI now renders new workspace: borrowed, fallback, and unknown diagnostics across flow
responses, but README.md was not updated. The Getting started and flow-command documentation
therefore omit the new user-facing behavior.
Code

src/Capacitor.Cli/Commands/McpFlowsServer.cs[1319]

+                sb.AppendLine("workspace: borrowed (the reviewer saw your working tree, uncommitted changes included)");
Evidence
Rule 6 requires README updates for every user-facing CLI behavior change. The added formatter emits
a new workspace status, while the existing Getting started and flow-command sections do not describe
workspace_mode, fallback_reason, or the rendered diagnostics.

CLAUDE.md: Keep README Documentation Synchronized with User-Facing CLI Changes
src/Capacitor.Cli/Commands/McpFlowsServer.cs[1319-1336]
README.md[49-59]
README.md[404-419]

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

## Issue description
Document the new workspace diagnostics rendered by flow commands, including `borrowed`, `fallback`, `unknown`, fallback reasons, and the uncommitted-work warning.

## Issue Context
PR Compliance ID 6 requires user-facing CLI behavior changes to update both Getting started documentation and the relevant per-command section in `README.md`.

## Fix Focus Areas
- README.md[49-59]
- README.md[404-419]

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


2. AppendWorkspaceDiagnostics comment is verbose ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new 16-line XML comment extensively narrates implementation branches, examples, future behavior,
and rationale already expressed by the adjacent switch. This violates the requirement that comments
remain concise and necessary for understanding the code.
Code

src/Capacitor.Cli/Commands/McpFlowsServer.cs[R1298-1313]

+    /// <summary>Renders the run's reviewer workspace decision. ONE helper, reached from every surface
+    /// that formats a flow response — start (roundless and rounded), the polled round result, status
+    /// and close — so no path can quietly disagree with another about what the reviewer actually read.
+    ///
+    /// <para>Three outcomes, and the third is the one that matters:</para>
+    /// <list type="bullet">
+    /// <item><c>borrowed</c> — the reviewer saw the requester's working tree, uncommitted work included.</item>
+    /// <item><c>fallback</c> — an owned worktree at the last commit, with the server's reason rendered
+    /// VERBATIM. No allowlist, no mapping table: a reason introduced server-side tomorrow reaches the
+    /// user unchanged. The caution line is the point — a reviewer that read the last commit did not
+    /// see uncommitted work, and a caller who assumed otherwise drew conclusions about code that was
+    /// never reviewed.</item>
+    /// <item>absent or null — rendered <c>unknown</c>, and NEVER as borrowed. An older server, a
+    /// multi-participant run, or a generic single-participant flow all land here. Guessing "borrowed"
+    /// would be the one wrong answer that reads as reassurance.</item>
+    /// </list></summary>
Evidence
Rule 3 prohibits unnecessarily verbose comments. The cited helper documentation spans 16 lines and
restates the borrowed, fallback, null, pass-through, and warning behavior implemented
immediately below it.

CLAUDE.md: Keep Code Comments Concise and Free of Linear Issue Numbers
src/Capacitor.Cli/Commands/McpFlowsServer.cs[1298-1313]

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

## Issue description
Replace the lengthy `AppendWorkspaceDiagnostics` XML documentation with a concise summary that only records information not evident from the implementation.

## Issue Context
The current comment duplicates each switch outcome and includes extensive rationale, examples, and future-facing commentary. PR Compliance ID 3 requires concise, necessary comments.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/McpFlowsServer.cs[1298-1313]

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



Remediation recommended

3. Context-only fallback misreported ✓ Resolved 🐞 Bug ≡ Correctness
Description
AppendWorkspaceDiagnostics says every fallback reviewer read a checkout at the last commit,
including context_only_requested. Context-only mode explicitly avoids reading the repository, so
callers may incorrectly believe the last committed code was reviewed rather than only the submitted
context.
Code

src/Capacitor.Cli/Commands/McpFlowsServer.cs[1326]

+                sb.AppendLine("  ⚠ The reviewer read a checkout at the last commit — it did NOT see uncommitted work.");
Evidence
The fallback branch emits the last-commit-checkout claim unconditionally, while the tests explicitly
recognize context_only_requested as a fallback reason. The tool schema defines context-only as
treating submitted material as authoritative rather than reading the repository, proving that the
warning's checkout claim is false for that supported case.

src/Capacitor.Cli/Commands/McpFlowsServer.cs[1321-1326]
src/Capacitor.Cli/Commands/McpFlowsServer.cs[1466-1468]
test/Capacitor.Cli.Tests.Unit/McpFlowsWorkspaceRenderTests.cs[92-100]

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 fallback diagnostic always claims the reviewer read a checkout at the last commit. This is false when `fallback_reason` is `context_only_requested`, because context-only mode uses the submitted context or diff instead of reading the repository.

## Issue Context
Preserve disclosure that the working tree was not borrowed, but do not imply committed repository contents were inspected. Render a dedicated context-only message or use generic fallback wording that is accurate for every reason.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/McpFlowsServer.cs[1321-1326]
- src/Capacitor.Cli/Commands/McpFlowsServer.cs[1466-1468]
- test/Capacitor.Cli.Tests.Unit/McpFlowsWorkspaceRenderTests.cs[92-109]

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


Grey Divider

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

Qodo Logo

Comment thread src/Capacitor.Cli/Commands/McpFlowsServer.cs
Comment thread src/Capacitor.Cli/Commands/McpFlowsServer.cs Outdated
Comment thread src/Capacitor.Cli/Commands/McpFlowsServer.cs Outdated
3 (correctness) is the real one. The fallback caution claimed the reviewer
"read a checkout at the last commit" -- false for context_only_requested, where
the reviewer read the SUBMITTED CONTEXT and no repository at all. A caller would
have concluded committed code was reviewed when none was.

Special-casing that reason would fix the one case and reintroduce the allowlist
this feature exists without. The claim is now worded to hold for EVERY reason:
"Your working tree was NOT borrowed -- the reviewer did not see uncommitted
work." True for every fallback, and it makes no assertion about what WAS read.
A regression test asserts the context-only render contains neither "last commit"
nor "checkout".

1 (docs). README's flows section now documents the three outcomes, states that
reasons are verbatim and open-ended, and spells out that unknown is NOT
borrowed -- treat it as "assume uncommitted work was not seen".

2 (comment length). Trimmed the helper's XML doc from 16 lines to 5. Kept the
two facts the code cannot state itself: absent renders unknown and never
borrowed, and reasons are not enumerated anywhere.

19 workspace tests, 119/119 across the McpFlows suites, check-linear-ids clean.
…k claim

Two things caught by reading the PR's own diff.

The new helper was inserted BETWEEN AppendReviewerModelAudit's doc comment and
its method, so that doc silently became the summary for AppendWorkspace
Diagnostics and the audit helper was left undocumented. Moved the helper below
the audit method; both now carry their own summary.

The start tool description still said 'On fallback the reviewer read the last
commit' -- the exact claim just corrected in the renderer for
context_only_requested, where the reviewer reads the submitted context and no
repository at all. Same fix, same reason: say only what is true for every
fallback reason. It now reads 'your working tree was NOT borrowed'.

119/119 McpFlows, check-linear-ids clean.
Codex review of #380. 'the reviewer did not see uncommitted work' is not true
for context_only_requested: the working tree was not borrowed, but the caller
may have inlined an uncommitted diff into the submitted context, so the reviewer
may well have seen uncommitted work -- just not from the tree.

Two wordings have now been wrong in the same direction, both asserting more than
the CLI can know:
  'read a checkout at the last commit' -> false where no repository is read
  'did not see uncommitted work'       -> false where the caller inlined a diff

The only claim that holds for every reason is about the AUTOMATIC channel: the
tree was not borrowed, so nothing uncommitted arrived that way, and anything
uncommitted the reviewer saw came from the submitted context. Naming reasons
individually would fix each case and reintroduce the allowlist this feature
exists without.

The context-only test now also asserts the claim does NOT overreach the other
way, so a future edit cannot quietly restore the stronger phrasing.

19/19 workspace tests, check-linear-ids clean.
@realtonyyoung
realtonyyoung merged commit 7cde541 into main Jul 28, 2026
6 checks passed
@realtonyyoung
realtonyyoung deleted the tonyyoung/ai-1529-workspace-render branch July 28, 2026 17:58
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