Skip to content

Pass a loose end's subject and flag one already closed - #1286

Merged
realtonyyoung merged 2 commits into
mainfrom
tonyyoung/ai-3416-loose-end-subject
Oct 2, 2026
Merged

realtonyyoung merged 2 commits into
mainfrom
tonyyoung/ai-3416-loose-end-subject

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

What & why

The server half, kurrent-io/kcap-server#2215, lets POST /api/loose-ends/declare take an optional subject (an issue key: PROJ-123, #123, owner/repo#123 or a GitHub issue URL) and returns the recorded subject and a subject_state (open/settled/unknown). This exposes subject on declare_loose_end and, when the server reports the subject already settled, appends an instruction telling the agent to check the remote before treating the loose end as unfinished.

Verification

Unit tests in McpWorkItemsServerTests:

  • BuildDeclareLooseEndBody copies an optional subject into the request body, the same way it already copies text.
  • FormatDeclareLooseEndResult leaves the server's JSON unchanged for open/absent subject_state, and appends the "already closed" guidance only when it is settled.
  • The declare_loose_end tool schema pin now includes subject alongside text/session_id.

Ran dotnet run --no-build --project test/Capacitor.Cli.Tests.Unit/Capacitor.Cli.Tests.Unit.csproj -- --treenode-filter "/*/*/McpWorkItemsServerTests/*": 102 passed, 0 failed.

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Oct 2, 2026

Copy link
Copy Markdown

AI-3416

The response is forwarded unchanged (declaration_id parsers see the
same JSON) except for a trailing instruction appended only when
subject_state is "settled", so the agent checks the remote before
treating a closed issue as unfinished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@realtonyyoung realtonyyoung changed the title [AI-3416] Pass a loose end's subject and flag one already closed Pass a loose end's subject and flag one already closed Oct 2, 2026
@realtonyyoung
realtonyyoung force-pushed the tonyyoung/ai-3416-loose-end-subject branch from 022ef50 to 89d88d5 Compare October 2, 2026 03:18
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Pass loose-end subjects and flag already-closed issues

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Allow agents to associate a loose end with an optional issue subject.
• Prompt agents to check the remote when the server reports the subject settled.
• Document the behavior and test request forwarding, response formatting, and tool schema.
Diagram

graph TD
  A["Agent"] --> B["MCP tool"] --> C["Declare API"] --> D{"Subject settled?"}
  D -->|No| E["Unchanged JSON"]
  D -->|Yes| F["Append guidance"] --> G["Check remote"]
Loading
High-Level Assessment

Keep the MCP tool as a thin client of the server's subject-state check. A separate client-side tracker lookup would duplicate server behavior; appending guidance only for settled subjects preserves other responses.

Files changed (5) +62 / -7

Enhancement (1) +26 / -2
McpWorkItemsServer.csForward subjects and flag settled declare results +26/-2

Forward subjects and flag settled declare results

• Adds optional subject to the tool schema and declare request. For successful declarations, preserves the server response except for appended remote-check guidance when subject_state is settled.

src/Capacitor.Cli/Commands/McpWorkItemsServer.cs

Tests (1) +26 / -1
McpWorkItemsServerTests.csCover subject forwarding and response guidance +26/-1

Cover subject forwarding and response guidance

• Tests optional subject forwarding, settled-subject guidance, unchanged open or absent states, and the updated tool schema.

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

Documentation (3) +10 / -4
README.mdDocument optional loose-end subjects +1/-1

Document optional loose-end subjects

• Explains accepted subject forms and the guidance returned when the server finds an issue already closed.

README.md

SKILL.mdTeach agents when to supply a loose-end subject +5/-1

Teach agents when to supply a loose-end subject

• Adds subject guidance and describes the remote check prompted by a closed issue. Updates the tool reference to list the optional argument.

kcap/skills/work-items/SKILL.md

help-mcp.txtDescribe subject support in CLI help +4/-2

Describe subject support in CLI help

• Notes the optional subject and the closed-subject signal in the declare_loose_end help entry.

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

@qodo-code-review

qodo-code-review Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Closed subjects break declaration parsers ✓ Resolved
Description
FormatDeclareLooseEndResult appends an instruction after the server's JSON when subject_state is
settled, making the returned text invalid JSON. MCP clients that parse the declaration response to
read declaration_id fail for settled subjects, while open-subject responses remain parseable.
Code

src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[R830-831]

+        return body + "\n\nThe subject issue is already closed in the tracker. Check the remote (fetch origin) "
+            + "before treating this as unfinished; if it is done, close the loose end with close_loose_end.";
Relevance

●●● Strong

Machine-readable MCP responses must remain parseable; accepted precedents preserve untouched
response bodies and JSON-RPC contracts.

PR-#480
PR-#224

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Successful declarations are newly routed through the formatter, which returns the original body
except when it appends prose for a settled subject. The MCP result exposes the response as text, and
the implementation itself identifies callers that parse its JSON and declaration_id.

src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[265-272]
src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[818-831]
src/Capacitor.Cli/Commands/McpReviewServer.cs[446-447]

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

## Issue description
Settled-subject guidance is appended to the JSON text returned by `declare_loose_end`, so consumers can no longer parse the declaration response.
## Fix Focus Areas
- src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[270-270]
- src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[822-831]
## Recommended Fix
Preserve the server response as a complete JSON text content item and deliver the guidance in a separate MCP text content item for settled subjects. Test that the JSON item can still be parsed to extract `declaration_id`.

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



Remediation recommended

2. Malformed subjects return internal errors ✓ Resolved
Description
BuildDeclareLooseEndBody calls GetValue<string>() on a supplied subject without checking its
JSON type. When an MCP caller supplies a number or object, the failure bypasses the argument-error
handler and the outer dispatcher returns a generic internal error instead of identifying the invalid
field.
Code

src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[813]

+        if (args?["subject"]?.GetValue<string>() is { Length: > 0 } subject) body["subject"] = subject;
Relevance

●●● Strong

Wrong-typed MCP arguments must produce field errors; closely matching precedents accepted defensive
JSON parsing.

PR-#800
PR-#344

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new subject access does not use the existing supplied-string shape validator. The tool-call
handler turns ArgumentException into a field error, while unexpected exceptions reach the outer
dispatcher and become a generic internal error.

src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[810-815]
src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[881-889]
src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[288-298]
src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[59-70]

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

## Issue description
A non-string `subject` throws outside the tool's argument-error path, hiding the field error behind a generic internal-error response.
## Fix Focus Areas
- src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[810-815]
- src/Capacitor.Cli/Commands/McpWorkItemsServer.cs[881-889]
## Recommended Fix
Check the type of a supplied subject and throw an `ArgumentException` naming `subject` when it is not a string. Add a malformed-argument test through the tool-call handler.

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


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
  Explored: repo: kurrent-io/kcap-server (branch: claude-tyoung/agent-duplicate-suggestions-ccef78, sha: fa7aa56f) — View relationship
Review mode: ⚖️ Balanced: This is a localized but behavioral MCP/API-facing change that alters request construction, response handling, and tool schema, so it warrants a careful single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.Cli/Commands/McpWorkItemsServer.cs Outdated
Comment thread src/Capacitor.Cli/Commands/McpWorkItemsServer.cs Outdated
Appending prose after the JSON broke clients parsing declaration_id;
guidance now lands as a field instead. subject now goes through
McpToolArguments.OptionalString so a wrong-typed value is a field error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@realtonyyoung
realtonyyoung merged commit b4fe603 into main Oct 2, 2026
8 checks passed
@realtonyyoung
realtonyyoung deleted the tonyyoung/ai-3416-loose-end-subject branch October 2, 2026 12: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