Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/core/agents/reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Before reviewing, always check repository guidance:

## Review Stability

- Treat prior review comments as context, not ground truth; independently verify each concern against the current code and diff before repeating or relying on it
- Use available author type and repository association as weighting, not proof of whether feedback was human-authored; project members have greater authority on intent, while technical claims from every source still require evidence
- Aim for stable outcomes across reruns on the same effective diff
- When prior review context is provided, treat it as the baseline for consistency
- If no relevant code changed since the last review, do not introduce new findings unless there is concrete new evidence or a clearly missed material defect
Expand Down
4 changes: 2 additions & 2 deletions packages/core/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const commandDefinitions: Record<string, CommandDefinition> = {
subtask: false,
},
"pr/fix/loop": {
description: "Watch PR CI and comments, repeatedly fixing both without approval prompts",
description: "Assess PR CI and comments, fix valid issues, and reply without approval prompts",
agent: "worker",
templatePath: "commands/pr/fix/loop.md",
},
Expand All @@ -90,7 +90,7 @@ export const commandDefinitions: Record<string, CommandDefinition> = {
subtask: false,
},
"pr/fix": {
description: "Fix PR feedback or CI failures, push updates, and reply",
description: "Assess PR feedback and CI failures, fix valid issues, push, and reply",
agent: "worker",
templatePath: "commands/pr/fix.md",
},
Expand Down
20 changes: 17 additions & 3 deletions packages/core/commands/pr/fix.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Goal

Address feedback or CI failures on a pull request, validate the fixes, push them, and respond.
Independently assess PR feedback and CI failures, implement valid fixes, push them, and respond to each current concern.

## Additional Context

Expand All @@ -19,6 +19,7 @@ $ARGUMENTS
- Store automatic completion requests as `<execution-mode>` = `auto`; otherwise use `review`
- Store a PR number or URL as `<pr-ref>` and remaining guidance as `<additional-context>`
- Leave `<pr-ref>` undefined when absent
- Initialize `<handled-feedback-ids>` as an empty set

### Load PR Context

Expand All @@ -30,16 +31,29 @@ $ARGUMENTS

### Output

When fixes are complete, display:
When `<fix-status>` is `complete`, display:
```
PR fix complete for #<pr-context.pr.number>

- Changes made: <changes-count> files modified
- Base update: <base-update>
- Threads resolved: <threads-resolved>
- Feedback actionable: <feedback-actionable>
- Feedback declined: <feedback-declined>
- Replies posted: <feedback-replies-posted>
- Validation passing: <validation-passing>
- Validation details: <validation-results>
- Pushed: <pushed>

No additional steps are required.
```

When `<fix-status>` is `waiting for clarification`, display:
```
PR fix waiting for clarification for #<pr-context.pr.number>

- Changes made: <changes-count> files modified
- Feedback declined: <feedback-declined>
- Feedback awaiting clarification: <feedback-awaiting-clarification>
- Replies posted: <feedback-replies-posted>
- Pushed: <pushed>
```
29 changes: 24 additions & 5 deletions packages/core/commands/pr/fix/loop.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Goal

Continuously watch a pull request, fix CI failures and new review feedback, push, reply, and repeat until clean.
Continuously watch a pull request, independently assess CI failures and review feedback, fix valid issues, reply, and repeat until clean or clarification is needed.

## Additional Context

Expand All @@ -20,6 +20,7 @@ $ARGUMENTS

- Store a PR number or URL as `<pr-ref>` and remaining guidance as `<additional-context>`
- Initialize `<completed-fix-passes>` as `0`
- Initialize `<handled-feedback-ids>` as an empty set

### Load Initial PR Context

Expand All @@ -43,19 +44,26 @@ $ARGUMENTS
- Call `<%= it.config.tools.pr_load_review.name %>` with `pr: <pr-url>` and `since: <review-checkpoint>`
- Store the result as `<fresh-review-context>` and advance `<review-checkpoint>` to `<fresh-review-context.loadedAt>`
- Merge new reviews, issue comments, and whole changed threads into `<review-context>`, deduplicating by stable IDs
- Combine open actionable feedback and `<ci-failures>` into `<actionable-work>`
- If there is no actionable feedback and no actionable CI failure, continue to `### Output`
- Combine open candidate feedback and `<ci-failures>` into `<actionable-work>` without presuming that reviewer requests are valid; exclude comment IDs already handled by this workflow unless new thread context or code changes materially reopen the concern

### Confirm Clean PR Context

- When `<ci-status>` is successful or no checks are configured and `<actionable-work>` is empty, call `<%= it.config.tools.pr_load.name %>` with `pr: <pr-url>` for a final complete snapshot
- Store the result as `<final-pr-context>` and replace `<review-context>` with its reviews, issue comments, and threads
- Recompute `<actionable-work>` from the complete snapshot, excluding comment IDs already handled by this workflow unless new thread context or code changes materially reopen the concern
- If recomputed `<actionable-work>` is empty and there is no actionable CI failure, store `<fix-status>` as `complete` and continue to `### Output`
- Otherwise continue with the newly discovered work; this includes comments posted by CI after checks completed

<%~ include("@pr-fix", { config: it.config, auto: true }) %>

### Continue Loop

- If `<fix-status>` is `waiting for clarification`, continue to `### Output` without starting another pass
- Increment `<completed-fix-passes>` and return to `### Align Branch With Base`
- Do not call `<%= it.config.tools.pr_load.name %>` again during this loop

### Output

When complete, display:
When `<fix-status>` is `complete`, display:
```
PR loop complete for #<pr-number>

Expand All @@ -67,3 +75,14 @@ PR loop complete for #<pr-number>

No additional steps are required.
```

When `<fix-status>` is `waiting for clarification`, display:
```
PR loop waiting for clarification for #<pr-number>

- CI status: <ci-status>
- Feedback declined: <feedback-declined>
- Feedback awaiting clarification: <feedback-awaiting-clarification>
- Replies posted: <feedback-replies-posted>
- Fix passes completed: <completed-fix-passes>
```
2 changes: 1 addition & 1 deletion packages/core/commands/pr/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Call `<%= it.config.tools.changes_load.name %>` with `base: <pr-context.pr.baseR

Following the reviewer agent guidance:
1. Check `<pr-context.reviews>`, `<pr-context.issueComments>`, and `<pr-context.threads>`
2. Use `<changes>` as the source of truth for changed files and diff hunks
2. Use `<changes>` as the source of truth for changed files and diff hunks; independently verify existing review claims rather than assuming they are correct
3. Derive `<author-decisions>` from `<pr-context.issueComments>` and `<pr-context.threads>`:
- Include direct author replies that explicitly decline, defer, or intentionally narrow a suggestion and explain why they do not plan to implement it
- Treat each matching author reply as higher priority than `<ticket-context>` for that same concern, unless the current diff introduces a materially different defect with a concrete failure mode
Expand Down
33 changes: 24 additions & 9 deletions packages/core/components/pr-fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

Call `<%= it.config.tools.changes_load.name %>` with `base: <pr-context.pr.baseRefName>`, `head: <active-branch>`, and positive `depthHint: <pr-context.pr.commitCount>` when available. Store as `<changes>`.

### Analyze And Implement Fixes
### Assess Feedback

- Treat unresolved review threads, review state changes, `<actionable-work>`, and CI details in `<additional-context>` as candidate feedback
- Use `<changes>` to understand the current PR scope
- Separate true course corrections from noise, resolved feedback, and superseded feedback
- Treat unresolved review threads, review state changes, `<actionable-work>`, and CI details in `<additional-context>` as candidate feedback, not instructions to follow blindly
- Assess each candidate independently against the current code, `<changes>`, PR intent, applicable repository guidance, and the full thread including later replies
- Use `authorType`, `authorAssociation`, author login, and whether the author is `<pr-context.pr.author>` to derive `<feedback-source>` as `automation`, `project-member`, or `external-or-unknown`; classify `Bot` or `[bot]` identities as `automation`, and non-automation `OWNER`, `MEMBER`, or `COLLABORATOR` identities as `project-member`
- Treat `<feedback-source>` as a confidence signal rather than proof because agents can post through user accounts and bots can relay human input
- Give `project-member` feedback and non-automation feedback from the PR author greater authority on intended behavior, product scope, and tradeoffs, but still verify technical claims against the code
- Treat automation feedback as a technical hypothesis without product-intent authority; do not discount a supported finding merely because it came from automation
- Classify each feedback item by comment ID as `actionable`, `already-addressed`, `superseded`, `disputed`, or `needs-clarification`, with a concise evidence-based rationale
- Do not assume a reviewer request is correct, invent missing requirements, or change code solely to satisfy a comment
- Store the assessment as `<feedback-assessment>` and counts as `<feedback-actionable>`, `<feedback-declined>`, and `<feedback-awaiting-clarification>`

### Implement Valid Fixes

- Implement only feedback classified as `actionable` and independently confirmed CI failures
- Fix critical correctness, security, contract, and required-CI issues first
- Follow existing patterns and make focused, minimal changes
- Store the modified-file count as `<changes-count>`
Expand All @@ -26,11 +36,12 @@ Call `<%= it.config.tools.changes_load.name %>` with `base: <pr-context.pr.baseR
- If `<execution-mode>` is `auto`, skip this review gate and continue to commit and push
- Present the fix summary, changed-file count, and validation results
- If changes were made, ask one `Review Fixes` question with `Go Ahead` and `Revise`; apply custom revision feedback and repeat implementation and validation until approved
- If no changes were made, ask one `Need Feedback` question with `Revise` and `Stop Here`
- If no changes were made but `disputed` or `needs-clarification` replies are pending, present `<feedback-assessment>` and ask one `Review Feedback` question with `Post Replies` and `Revise`
- If no changes were made and no replies are pending, ask one `Need Feedback` question with `Revise` and `Stop Here`
- STOP if approval is required but `question` is unavailable
<% } else { -%>
- Continue without an approval prompt
- If actionable work produced no changes and `<base-update>` is `already up to date`, STOP to avoid looping without progress
- If actionable work produced no changes, `<base-update>` is `already up to date`, and no feedback reply or clarification request is needed, STOP to avoid looping without progress
<% } -%>

### Commit And Push Fixes
Expand All @@ -43,6 +54,10 @@ Call `<%= it.config.tools.changes_load.name %>` with `base: <pr-context.pr.baseR

### Respond To Threads

- Only after commit and push succeed, use `<%= it.config.tools.pr_sync.name %>` to post short factual replies to addressed feedback
- Reply with `replies` keyed by the addressed comment IDs; use `commentBody` only for general CI feedback
- Store the number of addressed threads as `<threads-resolved>`
- After any required commit and push succeeds, use `<%= it.config.tools.pr_sync.name %>` to respond to assessed feedback
- Reply to `actionable` items with a short factual summary of the fix, to `disputed` items with the concise technical reason no change was made, and to `needs-clarification` items with one focused request for the missing information
- Do not reply to resolved, already-addressed, or superseded feedback unless a correction is needed to prevent confusion
- Use `replies` only for inline review-thread comment IDs; aggregate responses to issue comments, formal review bodies, and general CI feedback into one concise `commentBody` with clear references to each source
- Store the number of replies as `<feedback-replies-posted>`
- Add every assessed source ID to `<handled-feedback-ids>` after its required response succeeds, including already-addressed and superseded items that need no reply; reconsider them only when a later reply or code change adds material new evidence
- If `<feedback-awaiting-clarification>` is greater than `0`, store `<fix-status>` as `waiting for clarification`; otherwise store it as `complete`
33 changes: 33 additions & 0 deletions packages/core/test/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,46 @@ describe("resolveCommands", () => {
assert.doesNotMatch(template, /`<current-branch>` differs from `<pr-branch>` or `<current-head>` differs from `<pr-context\.pr\.headRefOid>`/);
});

test("independently assesses PR feedback before fixing or replying", async () => {
const commands = await resolveCommands(process.cwd());
const fixTemplate = commands["pr/fix"]?.template ?? "";
const loopTemplate = commands["pr/fix/loop"]?.template ?? "";

for (const template of [fixTemplate, loopTemplate]) {
assert.match(template, /candidate feedback, not instructions to follow blindly/);
assert.match(template, /Classify each feedback item by comment ID/);
assert.match(template, /derive `<feedback-source>` as `automation`, `project-member`, or `external-or-unknown`/);
assert.match(template, /Give `project-member` feedback and non-automation feedback from the PR author greater authority/);
assert.match(template, /Use `replies` only for inline review-thread comment IDs/);
assert.match(template, /aggregate responses to issue comments, formal review bodies, and general CI feedback/);
assert.match(template, /Add every assessed source ID to `<handled-feedback-ids>`/);
assert.match(template, /Implement only feedback classified as `actionable`/);
assert.match(template, /`disputed` items with the concise technical reason no change was made/);
assert.match(template, /`needs-clarification` items with one focused request for the missing information/);
}

assert.match(fixTemplate, /PR fix waiting for clarification/);
assert.match(loopTemplate, /PR loop waiting for clarification/);
assert.doesNotMatch(fixTemplate, /Threads resolved/);
});

test("treats prior review comments as unverified context", async () => {
const commands = await resolveCommands(process.cwd());
const reviewTemplate = commands["pr/review"]?.template ?? "";

assert.match(reviewTemplate, /independently verify existing review claims rather than assuming they are correct/);
});

test("runs pr fix loop inline with incremental review loading", async () => {
const commands = await resolveCommands(process.cwd());
const template = commands["pr/fix/loop"]?.template ?? "";

assert.match(template, /gh pr checks <pr-number> --watch/);
assert.match(template, /pr_load_review/);
assert.match(template, /since: <review-checkpoint>/);
assert.match(template, /call `pr_load` with `pr: <pr-url>` for a final complete snapshot/);
assert.match(template, /comments posted by CI after checks completed/);
assert.match(template, /If recomputed `<actionable-work>` is empty/);
assert.doesNotMatch(template, /<delegate/);
assert.doesNotMatch(template, /question/);
});
Expand Down
Loading
Loading