Skip to content

Track actionable review follow-ups#816

Merged
ralphbean merged 6 commits into
fullsend-ai:mainfrom
dhshah13:fix/789-review-followup-issues
May 13, 2026
Merged

Track actionable review follow-ups#816
ralphbean merged 6 commits into
fullsend-ai:mainfrom
dhshah13:fix/789-review-followup-issues

Conversation

@dhshah13
Copy link
Copy Markdown
Contributor

Summary

Closes #789.

This teaches fullsend post-review to preserve actionable low/info review findings after an approval by creating GitHub follow-up issues and posting a sticky PR summary.

What Changed

  • Adds structured review findings parsing with an actionable flag.
  • Creates follow-up issues only for approved reviews with actionable low or info findings.
  • Uses hidden deterministic markers to dedupe follow-up issues across reruns and across multiple PRs.
  • Posts/updates a sticky PR comment listing created or existing follow-up issues.
  • Extends Forge/GitHub clients and fakes for issue creation/listing with labels and bodies.
  • Updates review schema and review-agent instructions so actionable non-blocking work is explicit.

Validation

  • go test ./internal/cli ./internal/forge/...
  • bash internal/scaffold/fullsend-repo/scripts/validate-output-schema-test.sh

Live Smoke Test

Verified against real GitHub data in dhshah13/fullsend:

Notes

This is always-on behavior, matching #789. It only creates follow-up issues for approved reviews, and only when findings are both low/info and explicitly marked actionable: true.

@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 11, 2026

Site preview

Preview: https://b384940e-site.fullsend-ai.workers.dev

Commit: 7ee8f9af77c952c219fd879a5d588b7232c2b022

Copy link
Copy Markdown
Contributor

@ralphbean ralphbean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

This is a well-structured feature that directly addresses #789. The dedup strategy (content-addressed SHA-256 markers), the forge abstraction, and the test coverage for core flows are all solid. Three items need attention before merge; three more are noted for follow-up.

Request changes

  1. truncate() slices on byte offsets — will produce invalid UTF-8 on multi-byte characters in issue titles.
  2. ListOpenIssues fetches all open issues in the repo for dedup. At minimum, filter by the type/chore label to reduce API calls and payload size.
  3. The 422 retry in CreateIssue catches all 422 errors, not just label validation. Should either inspect the error response or document the intentional broadness.

Notes (deferred)

  1. Follow-up issue creation failure causes post-review to exit non-zero even though the formal review already succeeded. Consider making this a soft failure.
  2. The dedup loop recomputes SHA-256 markers in O(N×M) — precomputing markers before the loop is an easy optimization.
  3. Test coverage gaps: no unit tests for truncate/compactWhitespace helpers, no golden-value test for marker hash stability, no error-path tests for ListOpenIssues.

type reviewFollowupIssue struct {
finding ReviewFinding
issue *forge.Issue
created bool
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important] truncate() uses len(s) and s[:max-3] which operate on byte offsets. If a multi-byte UTF-8 character straddles the cut point, this produces invalid UTF-8 in the GitHub issue title. Should use []rune conversion:

func truncate(s string, max int) string {
	runes := []rune(s)
	if len(runes) <= max {
		return s
	}
	if max <= 3 {
		return string(runes[:max])
	}
	return strings.TrimSpace(string(runes[:max-3])) + "..."
}

return nil
}

func labelNames(labels []struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important] This fetches all open issues in the repo (up to 10,000) every time post-review runs with actionable findings. For active repos this burns rate-limit quota and adds latency.

Since all follow-up issues are created with the type/chore label, adding &labels=type/chore to the query string would significantly reduce the result set and API calls. Even better long-term would be using GitHub's search API to search for the marker prefix directly.

if len(labels) > 0 {
payload["labels"] = labels
}
resp, err := c.post(ctx, fmt.Sprintf("/repos/%s/%s/issues", owner, repo), payload)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important] The 422 retry is intentionally broad — it catches any StatusUnprocessableEntity, not just label validation errors. A 422 for body-too-long or title validation would trigger a retry without labels that will likely fail again with a confusing double error.

Consider either:

  1. Inspecting the GitHub error response errors array for field: "labels" before retrying, or
  2. Adding an inline comment explaining why the broad catch is acceptable (labels are best-effort, the only label used is type/chore).

return submitFormalReview(cmd.Context(), client, owner, repoName, pr, parsed.Action, parsed.HeadSHA, commentURL, dryRun, printer)
if err := submitFormalReview(cmd.Context(), client, owner, repoName, pr, parsed.Action, parsed.HeadSHA, commentURL, dryRun, printer); err != nil {
return err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] (deferred) If postApprovedFollowUpIssues fails, the entire post-review command exits non-zero even though submitFormalReview already succeeded. Since follow-up issue creation is supplementary to the review, consider logging a warning and returning nil instead of propagating the error.

marker := reviewFollowupIssueMarker(owner, repo, finding)
if strings.Contains(issue.Body, marker) {
existingByMarker[marker] = issue
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] (deferred) The nested loop recomputes reviewFollowupIssueMarker for each finding on every issue (O(N×M) hash computations). Precomputing markers into a map before the loop is straightforward:

markers := make(map[string]ReviewFinding, len(actionable))
for _, f := range actionable {
    markers[reviewFollowupIssueMarker(owner, repo, f)] = f
}
for _, issue := range openIssues {
    for marker := range markers {
        if strings.Contains(issue.Body, marker) {
            existingByMarker[marker] = issue
        }
    }
}

@@ -558,3 +567,177 @@ func TestSubmitFormalReview_ListErrorSkipsCleanup(t *testing.T) {
assert.Empty(t, fc.MinimizedComments)
require.Len(t, fc.CreatedReviews, 1, "review should still be created despite list error")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] (deferred) Good coverage for the core flows. A few edge-case gaps to consider for follow-up:

  • Unit tests for truncate() boundary cases (empty string, max=0, multi-byte chars)
  • A golden-value test for reviewFollowupIssueMarker to guard against accidental hash changes
  • Error-path test for ListOpenIssues failures

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catches on the test gaps. A few additional edge cases to consider:

  • Non-approve actions: No test verifying that request-changes, comment, or failure actions don't create follow-up issues (the action != "approve" gate at postreview.go:310)
  • CreateIssue error path: Missing test for when CreateIssue fails (rate limit, permissions, GitHub outage)
  • High/critical with actionable: No test confirming that high/critical severity findings are skipped even when actionable: true
  • Mixed scenario: No test for the common case where some findings are new and others already have issues (tests both "Created" and "Existing" sections of the summary)

@ralphbean
Copy link
Copy Markdown
Contributor

@ggallen — you filed #789 originally. Does this implementation match what you had in mind? Specifically:

  • Follow-up issues are created only for actionable: true findings at low/info severity on approved PRs
  • Dedup uses content-addressed SHA-256 markers in issue bodies (cross-run and cross-PR)
  • A sticky PR comment lists created/existing follow-up issues
  • Always-on behavior, no per-repo config

Any concerns or gaps vs. the original intent?

@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

@dhshah13
Copy link
Copy Markdown
Contributor Author

dhshah13 commented May 11, 2026

Thanks for the careful review. I pushed a follow-up commit addressing the requested changes:

  • Made truncate() UTF-8 safe.
  • Filtered duplicate-detection issue scans to type/chore.
  • Narrowed the 422 retry so it only retries without labels when GitHub reports a labels validation error.
  • Precomputed follow-up markers before scanning issues.
  • Added tests for truncation, whitespace compaction, marker hash stability, ListOpenIssues error handling, label filtering, pagination, and non-label 422 behavior.

Validation:

  • go test ./internal/cli ./internal/forge/...
  • bash internal/scaffold/fullsend-repo/scripts/validate-output-schema-test.sh

I left the soft-failure suggestion unchanged for now because it changes the reliability semantics: if the formal approval succeeds but follow-up issue creation fails, failing the workflow makes the missing follow-up visible instead of silently approving without tracking the actionable finding. Happy to switch that to warning-only if you prefer.

@fullsend-ai-review
Copy link
Copy Markdown

fullsend-ai-review Bot commented May 11, 2026

Review: #816

Head SHA: 7ca8bdf
Timestamp: 2026-05-12T00:00:00Z
Outcome: approve

Summary

This PR delivers exactly what #789 requests: automatic GitHub issue creation for actionable low/info findings when the review agent approves a PR, with deterministic dedup markers, a sticky PR summary comment, and best-effort label application with retry logic. The implementation is clean, well-structured, follows existing patterns (sticky comments, fake client, forge interface), and has comprehensive test coverage including creation, filtering, dedup within and across PRs, duplicate-marker warnings, dry-run, and error propagation. The interface change (CreateIssue variadic labels, new ListOpenIssues) is backward-compatible with the sole existing caller. No correctness, security, or injection concerns found.

Findings

Info

  • [correctness] internal/cli/postreview.go:228 — The reviewFollowupIssueMarker normalizes severity to lowercase but does not normalize category. If the review agent inconsistently cases category names across runs (e.g., missing-test vs Missing-Test), a duplicate issue could be created for the same finding. In practice this is unlikely since the review agent produces structured output with consistent casing.

  • [correctness] internal/forge/github/github.go:1213ListOpenIssues caps pagination at 100 pages (10,000 issues). For repos with more than 10,000 open issues matching the label filter, some duplicates could slip through. This is an extreme edge case and the cap prevents runaway pagination.

Footer

Outcome: approve
This review applies to SHA 7ca8bdf0bf3c5a6c778d7ea6ef9714804332bdc4. Any push to the PR head clears this review and requires a new evaluation.

Previous run

Review: #816

Head SHA: b1a7911
Timestamp: 2026-05-12T00:00:00Z
Outcome: approve

Summary

This PR cleanly implements the feature requested in #789: creating follow-up GitHub issues for actionable low/info review findings when the review agent approves a PR. The code is well-structured, the interface changes are backward-compatible (variadic labels parameter), error handling is thorough (including graceful retry when labels are rejected by GitHub), and duplicate detection via deterministic SHA-256 markers is sound. Test coverage is comprehensive, covering creation, deduplication (same-PR and cross-PR), non-actionable filtering, severity gating, dry-run, and API error propagation. The CreateIssue label-fallback retry, ListOpenIssues pagination with PR filtering, and the APIError enrichment with validation details are all correctly implemented. The agent instruction and schema updates are consistent with the Go implementation.

Findings

Info

  • [correctness] internal/cli/postreview.go:224 — The dedup marker hash includes the Line field, so the same conceptual finding at a different line number (e.g., after a refactor shifts code) will produce a different hash and create a duplicate issue. This is a reasonable trade-off for determinism and is documented by the test coverage, but worth noting for future consideration if duplicate issues become noisy.

  • [correctness] internal/forge/github/github.go:1161ListOpenIssues fetches all open issues with the type/chore label for marker scanning. For repositories with thousands of type/chore issues this could be slow, but the label filter keeps the set manageable in practice and the 100-page cap prevents runaway pagination.

Footer

Outcome: approve
This review applies to SHA b1a79118e1c2ce85bec1c4af4162a6a9dd786d92. Any push to the PR head clears this review and requires a new evaluation.

Previous run (2)

Review: #816

Head SHA: 76a7b30
Timestamp: 2026-05-11T00:00:00Z
Outcome: approve

Summary

This PR implements follow-up issue creation for actionable low/info review findings when a PR is approved, directly addressing #789. The implementation is well-structured: postApprovedFollowUpIssues correctly gates on approve-only action and actionable+low/info severity, uses deterministic SHA-256 markers for deduplication across PRs and reruns, and posts a sticky summary comment. The CreateIssue label retry fallback is a pragmatic touch for repos that lack the type/chore label. The forge interface, fake client, and GitHub live client changes are consistent and well-tested. Schema, agent instructions, and skill docs are updated coherently. Test coverage is thorough — happy path, skip non-actionable, skip medium, duplicate detection, cross-PR dedup, dry-run, error propagation, label retry, non-label validation errors, pagination, and golden marker hash.

Findings

Medium / Low / Info

No critical, high, or medium findings.

  • [info] Style: The whitespace realignment in TestHexSHAValidation and CreateOrgSecret payload map is cosmetic churn unrelated to the feature. Not blocking but adds noise to the diff.

Footer

Outcome: approve
This review applies to SHA 76a7b305e26727caad030cb11d9a9c53a580e729. Any push to the PR head clears this review and requires a new evaluation.

@dhshah13 dhshah13 force-pushed the fix/789-review-followup-issues branch from 76a7b30 to b1a7911 Compare May 12, 2026 13:31
@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

@dhshah13
Copy link
Copy Markdown
Contributor Author

dhshah13 commented May 12, 2026

resolved merge conflicts.

Copy link
Copy Markdown
Contributor

@ralphbean ralphbean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR cleanly implements the feature requested in #789: creating follow-up GitHub issues for actionable low/info review findings when approving PRs. The implementation is well-structured with comprehensive test coverage, deterministic deduplication via SHA-256 markers, and thoughtful error handling (label retry fallback, graceful degradation).

@dhshah13 addressed the previous review feedback effectively:

  • ✅ UTF-8-safe truncation
  • ✅ Label filtering for issue queries
  • ✅ Narrow 422 retry to label validation errors
  • ✅ Precomputed markers for O(N+M) scanning
  • ✅ Additional test coverage

The strategic direction is sound and aligns with the project's autonomous development goals. A few minor edge cases noted inline for follow-up work, but nothing blocking.

Approving with deferred notes for future improvements.

for marker := range markers {
if strings.Contains(issue.Body, marker) {
existingByMarker[marker] = issue
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] (deferred) The collision handling silently overwrites when multiple issues contain the same marker:

existingByMarker[marker] = issue  // Overwrites if duplicate found

While marker collisions are extremely unlikely (SHA-256 hash), if data corruption occurs or issues are manually edited, this could cause a finding to be marked as "tracked" when it's actually lost. Consider logging a warning when collision is detected, or document this as expected behavior.

Not blocking for this PR, but worth considering for robustness.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I went ahead and addressed this.

Duplicate markers no longer silently overwrite the prior entry during duplicate detection. The scan keeps the first matching issue deterministically, emits a warning with both issue numbers, and skips replacing the existing marker mapping.

I also added TestPostApprovedFollowUpIssues_WarnsOnDuplicateMarkers to cover the manual/corrupted duplicate-marker case and verify the PR summary continues to reuse the first tracked issue instead of drifting silently.

Validation:

  • go test ./internal/cli -run TestPostApprovedFollowUpIssues_WarnsOnDuplicateMarkers
  • go test ./internal/cli ./internal/forge/...

@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

Copy link
Copy Markdown
Contributor

@ralphbean ralphbean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This is a well-designed feature that directly implements the requirements from #789. The code quality is high with comprehensive test coverage and thoughtful deduplication logic.

Strategic Assessment

Good idea — Solves real workflow problem of tracking actionable findings after PR approval
Fits scope — Core to fullsend's autonomous development mission
Implements #789 — All requirements addressed (actionable flag, deduplication, sticky summary)

Code Quality

  • ✅ Only creates follow-up issues for approved reviews with actionable low/info findings
  • ✅ Content-based hashing prevents duplicates across runs and PRs
  • ✅ Graceful label creation fallback
  • ✅ Comprehensive test coverage with edge cases
  • ✅ Handles UTF-8 correctly in title truncation
  • ✅ Duplicate marker warning (commit 7ca8bdf) is a good addition

Findings

I've noted a few areas for potential future improvement in inline comments below (all deferred, non-blocking):

  • Race condition in duplicate detection (difficult to solve without GitHub API atomic operations)
  • Field validation before hashing (schema should enforce this)
  • Schema-code alignment for actionable flag (minor documentation improvement)

I also have one minor suggestion about the reviewActionToEvent function accepting both "request-changes" and "request_changes" variants when the schema only defines the hyphenated version. I'll post that as a separate comment since the function signature wasn't modified in this PR.

Recommendation: Approve. The deferred items are nice-to-haves that don't block merging this excellent feature.

}

printer.StepStart("Checking for existing review follow-up issues")
openIssues, err := client.ListOpenIssues(ctx, owner, repo, "type/chore")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important] (deferred) The duplicate detection has a potential race condition: if two post-review runs execute concurrently for different PRs with identical findings, both could list issues, find no duplicates, and both create duplicate issues.

While the warning at line 347 helps detect this after the fact, there's no prevention. This is difficult to solve without database-level locking or GitHub's atomic operations.

Suggestion: Document this known limitation in a code comment so future maintainers understand the tradeoff. For example:

// Note: Duplicate detection is subject to race conditions if concurrent
// post-review runs create issues for the same finding. The warning below
// helps detect this case, but prevention would require atomic operations
// not available through the GitHub API.

Not blocking for this PR, but worth documenting.

return b.String()
}

func reviewFollowupIssueMarker(owner, repo string, finding ReviewFinding) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important] (deferred) The marker hash is computed without validating that required fields are non-empty. If finding.File, finding.Category, or other fields are empty strings, the hash still computes but may lead to unexpected collisions or confusing markers.

Consider adding defensive validation in postApprovedFollowUpIssues before calling this function, or add a comment documenting the assumption that findings are pre-validated by the JSON schema.

Not blocking for this PR since the schema should enforce required fields, but defensive programming would make this more robust.

"description": { "type": "string", "minLength": 1 },
"remediation": { "type": "string" }
"remediation": { "type": "string" },
"actionable": {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] (deferred) The schema allows actionable on findings of any severity, but the code in postreview.go:actionableApprovedFindings() only processes findings with low or info severity. An agent could mark a medium finding as actionable: true, but it would be silently ignored.

Consider updating the description to clarify that actionable only applies to low/info findings:

"actionable": {
  "type": "boolean",
  "description": "True when this low/info severity finding should be tracked as a follow-up issue if the review approves. Only applies to 'low' and 'info' severity findings."
}

Not blocking, but helps align schema with implementation.

@ralphbean
Copy link
Copy Markdown
Contributor

[minor] Additional note on reviewActionToEvent (line 171):

The function accepts both "request-changes" and "request_changes" (hyphen and underscore variants), but the schema only defines "request-changes" with a hyphen. While flexibility is nice, this creates inconsistency.

Consider either:

  1. Remove the underscore variant and strictly enforce the schema format, or
  2. Add a comment documenting why both variants are accepted (backward compatibility? agent flexibility?)

Prefer option 1 for clarity unless there's a specific reason to support both.

@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

@dhshah13
Copy link
Copy Markdown
Contributor Author

Agreed. I checked the surrounding contract and there is no compatibility reason to keep the underscore variant: the schema, docs, and agent instructions all define request-changes.

I removed request_changes from reviewActionToEvent and updated TestReviewActionToEvent to assert that only the schema spelling is accepted.

Validation:

  • go test ./internal/cli -run TestReviewActionToEvent
  • go test ./internal/cli ./internal/forge/...

Copy link
Copy Markdown
Contributor

@ralphbean ralphbean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Well-designed feature that directly implements #789. The deduplication strategy (content-addressed SHA-256 markers), forge abstraction, and test coverage are all solid. The author addressed all prior blocking feedback (UTF-8 truncation, label filtering, narrow 422 retry, precomputed markers, duplicate marker warnings).

Strategic assessment

  • Good idea — solves a real workflow gap where non-blocking findings vanish after merge
  • In scope — core to fullsend's autonomous development mission
  • Implements #789 — all requirements addressed

One deferred note on issue creation volume; otherwise LGTM.

Approving.

printer.StepDone("Duplicate check complete")

results := make([]reviewFollowupIssue, 0, len(actionable))
for _, finding := range actionable {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] (deferred) No cap on follow-up issues per review run. If a review agent emits many actionable low/info findings (e.g., 50+), this loop creates all of them as GitHub issues in a single run with no rate limiting or cap. This could be noisy for maintainers and could hit GitHub API rate limits.

Consider adding a configurable cap (e.g., max 10 per run) with a warning when hit. Not blocking for this PR.

Copy link
Copy Markdown
Contributor Author

@dhshah13 dhshah13 May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I added a cap, using 3 as the stricter limit rather than the example 10 so one review run cannot flood maintainers.

Details:

  • --max-follow-up-issues now defaults to 3.
  • Allowed range is 0..3; 0 disables follow-up issue creation and values above 3 are rejected.
  • Existing duplicate issues do not consume the creation cap.
  • When the cap is hit, the sticky follow-up summary includes a warning with the skipped count.
  • Added tests for the default, validation, cap behavior, and the existing-issues-do-not-consume-cap case.

Validation:

  • go test ./internal/cli -run 'Test(NewPostReviewCmd_MaxFollowUpIssuesDefault|ValidateMaxReviewFollowUpIssues|PostApprovedFollowUpIssues|ReviewActionToEvent|ParseReviewResult_Findings)'
  • bash internal/scaffold/fullsend-repo/scripts/validate-output-schema-test.sh

@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

…up-issues

# Conflicts:
#	internal/cli/postreview.go
#	internal/scaffold/fullsend-repo/agents/review.md
#	internal/scaffold/fullsend-repo/skills/code-review/SKILL.md
#	internal/scaffold/fullsend-repo/skills/pr-review/SKILL.md
@github-actions
Copy link
Copy Markdown

fullsend review is working on this — view logs

Copy link
Copy Markdown
Contributor

@ralphbean ralphbean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Well-designed feature that directly implements #789. The dedup strategy (content-addressed SHA-256 markers), forge abstraction, test coverage, and error handling are all solid. The author addressed all prior blocking feedback across 4 review rounds.

Strategic assessment

  • Good idea — solves a real workflow gap where non-blocking findings vanish after merge
  • In scope — core to fullsend's autonomous development mission
  • Implements #789 — all requirements addressed (actionable flag, dedup, sticky summary, always-on)

Notes (deferred, non-blocking)

One deferred note on markdown injection risk in agent-sourced content flowing into issue bodies. See inline comment.

Approving.

return fmt.Sprintf("Follow-up from PR #%d: %s", pr, truncate(summary, 88))
}

func reviewFollowupIssueBody(owner, repo string, pr int, finding ReviewFinding, marker string) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate, deferred] finding.Description and finding.Remediation are rendered as raw markdown in the issue body. A compromised agent could inject misleading links or formatting. Consider sanitizing or escaping markdown-active characters in agent-sourced content before writing to GitHub issues. Low risk today since the source is a controlled agent, but worth hardening given the project's threat model prioritizes external injection.

@ralphbean ralphbean added this pull request to the merge queue May 13, 2026
Merged via the queue into fullsend-ai:main with commit 3bcce6e May 13, 2026
37 checks passed
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.

Review agent should create follow-up issues for actionable low/info findings when approving

2 participants