From c6bae1be818f22525b8e2311b57694d0080e1e7c Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 7 Oct 2026 09:42:07 +0800 Subject: [PATCH 1/2] fix: keep follow-up reviews from repeating open findings Each CI review starts from an empty database and the review guidance never asked the agent to read the PR's existing review threads, so a new commit received inline comments that repeated still-open findings. Read existing threads before publishing, comment inline only on new findings, refer to still-applicable ones by link, and keep resolved threads settled unless new evidence changes them. Also wrap the long GitHub guidance strings with implicit concatenation; their rendered text is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/guides/github.md | 2 +- src/landing/adapters/github.py | 48 ++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/docs/guides/github.md b/docs/guides/github.md index 5fd1d9a..90a9405 100644 --- a/docs/guides/github.md +++ b/docs/guides/github.md @@ -30,7 +30,7 @@ In Actions, Landing supplies the trigger, target, and relevant revisions. The ag ## Publish native results -PR review publishes a native [GitHub Review](https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request) for the requested commit, with findings attached to the affected lines. A clean review needs no code comments. Reviews comment by default; approvals and change requests require explicit authorization. The gate recommendation remains separate. +PR review publishes a native [GitHub Review](https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request) for the requested commit, with findings attached to the affected lines. A clean review needs no code comments. Follow-up reviews on new commits read the PR's existing review threads: findings that still apply stay in their original threads and are referenced from the review body rather than posted again, and resolved threads stay settled unless new evidence changes their conclusion. Reviews comment by default; approvals and change requests require explicit authorization. The gate recommendation remains separate. Explicit delegations require a reply to their selected issue or PR; inline follow-ups reply in the original review thread. Landing verifies that the prepared identity published to the requested destination before reporting completion. Include `confirm_reply` when restricting tools for inline follow-ups. diff --git a/src/landing/adapters/github.py b/src/landing/adapters/github.py index f6d504b..35bc97d 100644 --- a/src/landing/adapters/github.py +++ b/src/landing/adapters/github.py @@ -23,17 +23,49 @@ from landing.prompts import render from landing.runtime import Runtime -REPOSITORY_GUIDANCE = "GitHub repository: $repository. Use the prepared gh CLI. In GitHub conversations use #number or owner/repo#number outside code spans; elsewhere use explicit links. Read contribution templates from the checkout's standard GitHub locations when needed." - -PUBLICATION_GUIDANCE = "When publishing, include $stamp at the start of the body to identify this delivery. For a body file, use gh pr/issue comment --body-file FILE or gh api -F body=@FILE; -f body=@FILE sends the literal path. Use --input FILE for a JSON payload. Refresh the current PR head before publishing. Supplemental evidence may be linked from the required reply or review; a separate evidence comment cannot replace that publication. The context records the candidate head separately from the actual CI checkout revision." - -THREAD_GUIDANCE = "Reply in the original thread with POST repos/$repository/pulls/$number/comments/$thread/replies, rather than a new review. Include the delivery marker, or call confirm_reply with the returned comment ID." - -REVIEW_GUIDANCE = "Publish a native GitHub COMMENT review on PR #$number; APPROVE and REQUEST_CHANGES require separate explicit authorization. Use the reviews API with commit_id, body and inline comments containing path, line and side; ranges also use start_line and start_side. Verify locations against the inspected diff. Put the verdict in the review body and findings in inline comments; use the body for findings with no suitable diff location. Use suggestion blocks for verified replacements within the commented range, preserving the surrounding syntax, and
for longer reproductions or evidence. Reply to existing findings through the original review thread. Native check jobs are independent of Landing feedback; do not wait for this feedback job or the enclosing workflow to complete." +REPOSITORY_GUIDANCE = ( + "GitHub repository: $repository. Use the prepared gh CLI. In GitHub conversations use #number or owner/repo#number " + "outside code spans; elsewhere use explicit links. Read contribution templates from the checkout's standard GitHub " + "locations when needed." +) + +PUBLICATION_GUIDANCE = ( + "When publishing, include $stamp at the start of the body to identify this delivery. " + "For a body file, use gh pr/issue comment --body-file FILE or gh api -F body=@FILE; " + "-f body=@FILE sends the literal path. Use --input FILE for a JSON payload. " + "Refresh the current PR head before publishing. Supplemental evidence may be linked from the required reply or " + "review; a separate evidence comment cannot replace that publication. " + "The context records the candidate head separately from the actual CI checkout revision." +) + +THREAD_GUIDANCE = ( + "Reply in the original thread with POST repos/$repository/pulls/$number/comments/$thread/replies, rather than a new " + "review. Include the delivery marker, or call confirm_reply with the returned comment ID." +) + +REVIEW_GUIDANCE = ( + "Publish a native GitHub COMMENT review on PR #$number; APPROVE and REQUEST_CHANGES require separate explicit " + "authorization. Use the reviews API with commit_id, body and inline comments containing path, line and side; " + "ranges also use start_line and start_side. Verify locations against the inspected diff. " + "Put the verdict in the review body and findings in inline comments; use the body for findings with no suitable " + "diff location. Use suggestion blocks for verified replacements within the commented range, preserving the " + "surrounding syntax, and
for longer reproductions or evidence. " + "Before publishing, read the PR's existing review threads, including their resolved and outdated state, through the " + "GraphQL reviewThreads connection. Add inline comments only for findings that no existing thread raises. " + "Leave a still-applicable open finding in its thread and refer to it from the review body by link; " + "reply in that thread only when the candidate changes its conclusion or affected location. " + "Resolved threads stay settled unless new evidence changes their conclusion. " + "Native check jobs are independent of Landing feedback; do not wait for this feedback job or the enclosing workflow " + "to complete." +) AUTOMATIC_GUIDANCE = "This is automatic follow-up; no_update is available when there is no useful change." -CONVERSATION_GUIDANCE = "Answer the delegation in issue or PR #$number. Link a repair or supporting evidence when needed; do not repeat an explanation already published in the linked issue, PR or review. Call confirm_reply with the returned conversation comment ID to read back the published body." +CONVERSATION_GUIDANCE = ( + "Answer the delegation in issue or PR #$number. Link a repair or supporting evidence when needed; " + "do not repeat an explanation already published in the linked issue, PR or review. " + "Call confirm_reply with the returned conversation comment ID to read back the published body." +) class GitHubEnvironment(BaseSettings): From 745d79aa00451afec9424060490cbb2905241e5e Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 7 Oct 2026 10:24:41 +0800 Subject: [PATCH 2/2] test: wait for the complete MCP receipt before reading it The MCP fixture server opens the receipt before writing its record, so the cancellation test could see the file while it was still empty and fail with JSONDecodeError. Wait until the record's line is complete. Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/test_mcp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_mcp.py b/tests/test_mcp.py index c4462e9..1dab447 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -168,8 +168,9 @@ async def run(): consumer = asyncio.create_task( output(await landing.run_stream(session_id="pending", prompt="Record evidence.")) ) + # The server creates the receipt before writing its line; wait for the complete record. async with asyncio.timeout(15): - while not receipt.exists(): + while not receipt.exists() or not receipt.read_text().endswith("\n"): await asyncio.sleep(0.01) pid = json.loads(receipt.read_text())["pid"] consumer.cancel()