Skip to content

feat(code-agent): use PR template for pr_body in structured output#56

Open
rh-hemartin wants to merge 1 commit into
mainfrom
feat/pr-body
Open

feat(code-agent): use PR template for pr_body in structured output#56
rh-hemartin wants to merge 1 commit into
mainfrom
feat/pr-body

Conversation

@rh-hemartin

@rh-hemartin rh-hemartin commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

  • Migrate PR template / pr_body support from feat(code-agent): uses PR template if found fullsend#2979 to the agents repo
  • Agent discovers repo PR templates and writes a template-structured pr_body field in code-result.json
  • Post-script uses pr_body verbatim as PR description, falling back to commit body when absent
  • Includes all review feedback fixes: printf over echo, tightened sed patterns, maxLength constraint, jq -n examples, if/else clarity

Test plan

  • post-code-test.sh passes all 62 tests including new pr_body cases
  • Verify schema validation accepts pr_body field in CI
  • End-to-end: code agent run on a repo with a PR template produces template-structured PR description

Migrated from fullsend-ai/fullsend#2979. Closes fullsend-ai/fullsend#1575

🤖 Generated with Claude Code

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Use repo PR templates to populate structured pr_body for PR creation

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Add optional pr_body field to code-agent result schema for template-structured PR descriptions.
• Update post-code script to prefer pr_body verbatim, falling back to commit body.
• Extend docs/tests to cover PR template discovery and pr_body behavior (Closes stripping).
Diagram

graph TD
  T["PR templates"] --> A["Code agent"] --> R["code-result.json"] --> P["post-code.sh"] --> G{{"GitHub PR"}}
  S[("code-result.schema.json")] --> R
  D["SKILL.md guidance"] --> A

  subgraph Legend
    direction LR
    _file["File"] ~~~ _proc["Process"] ~~~ _ext{{"External"}} ~~~ _schema[("Schema")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Have post-code.sh discover templates and assemble PR body
  • ➕ Keeps behavior centralized in the post-script (single source of truth)
  • ➕ Doesn’t require agent-side template parsing heuristics
  • ➖ Makes post-code.sh significantly more complex (template selection, comment stripping, section prompting)
  • ➖ Harder to tailor content to actual changes without agent context
2. Introduce structured PR-body fields (e.g., summary/testing/notes) instead of free-form markdown
  • ➕ Enables stronger validation and consistent formatting
  • ➕ Easier to enforce required sections across repos
  • ➖ Requires ongoing schema evolution and mapping to arbitrary repo templates
  • ➖ More work for agents and post-script to render to template-specific markdown
3. Continue using commit-body unwrapping only (no pr_body)
  • ➕ No new schema surface area
  • ➕ Simpler pipeline end-to-end
  • ➖ Cannot reliably match repo PR templates
  • ➖ Still constrained by gitlint body line-length rules and unwrapping heuristics

Recommendation: Current approach (agent writes template-structured pr_body, post-script uses it verbatim) is the best trade-off: the agent has the richest context to populate template prompts, while the post-script remains a thin consumer with a clear fallback. The main thing to watch is the line-stripping logic (Signed-off-by/Closes) to avoid accidentally deleting legitimate content; the tightened sed patterns and added tests address this risk.

Files changed (4) +164 / -31

Enhancement (1) +22 / -9
post-code.shPrefer pr_body from result file when creating PR +22/-9

Prefer pr_body from result file when creating PR

• Reads '.pr_body' from the agent result JSON (via 'jq') and, when present, uses it as the PR description verbatim after stripping Signed-off-by/Closes lines. Retains the legacy fallback path that unwraps the commit body via awk when 'pr_body' is absent.

scripts/post-code.sh

Tests (1) +86 / -1
post-code-test.shExtend post-code tests for agent-provided pr_body path +86/-1

Extend post-code tests for agent-provided pr_body path

• Updates 'build_pr_body' to accept an optional agent-provided 'pr_body' and strip Signed-off-by/Closes lines before appending canonical metadata once. Adds new test helpers and cases to ensure 'pr_body' is used verbatim (no wrapping) and that only a single 'Closes #<n>' is present.

scripts/post-code-test.sh

Documentation (1) +50 / -20
SKILL.mdDocument PR template discovery and safe jq -n pr_body writing +50/-20

Document PR template discovery and safe jq -n pr_body writing

• Adds explicit guidance to check for PR templates and structure the agent-produced 'pr_body' to match visible template sections (skipping HTML comments). Replaces heredoc examples with 'jq -n --arg' patterns to avoid shell expansion issues and clarifies that 'pr_body' is not subject to gitlint body line-length limits.

skills/code-implementation/SKILL.md

Other (1) +6 / -1
code-result.schema.jsonAdd optional pr_body to code-result schema +6/-1

Add optional pr_body to code-result schema

• Extends the schema description and adds an optional 'pr_body' string field with 'maxLength: 65536'. This enables the agent to provide a full PR description independently of the commit body and gitlint wrapping constraints.

schemas/code-result.schema.json

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 8, 2026

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure · Started 7:24 AM UTC · Completed 7:38 AM UTC
Commit: f828ec7 · View workflow run →

@rh-hemartin rh-hemartin self-assigned this Jul 8, 2026
@qodo-code-review

qodo-code-review Bot commented Jul 8, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (1)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Action required

1. Protected paths modified (scripts/, skills/) 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths (scripts/ and skills/), which require
explicit human review and must not be auto-approved. Ensure the PR has explicit
justification/authorization and is routed for human approval.
Code

scripts/post-code.sh[R446-460]

echo "Creating PR..."

COMMIT_SUBJECT="$(git log -1 --format='%s' HEAD)"
-COMMIT_BODY_RAW="$(git log -1 --format='%b' HEAD | sed '/^Signed-off-by:/d' | sed '/^Closes #/d' | sed -e :a -e '/^\n*$/{ $d; N; ba; }')"
-
-COMMIT_BODY="$(echo "${COMMIT_BODY_RAW}" | awk '
-  /^$/           { if (buf) print buf; print; buf=""; next }
-  /^[-*#>]|^  /  { if (buf) print buf; buf=""; print; next }
-  /^Closes /     { if (buf) print buf; buf=""; print; next }
-                 { buf = (buf ? buf " " $0 : $0) }
-  END            { if (buf) print buf }
-')"
+
+# Read pr_body from agent output. Fall back to commit body if absent.
+PR_BODY_FROM_RESULT=""
+if [ -n "${RESULT_FILE}" ]; then
+  PR_BODY_FROM_RESULT="$(jq -r '.pr_body // empty' "${RESULT_FILE}" 2>/dev/null || true)"
+fi
+
+if [ -n "${PR_BODY_FROM_RESULT}" ]; then
+  # Agent provided pr_body (template-aware or best-effort).
+  # Strip Signed-off-by and Closes lines so the script appends them once.
+  COMMIT_BODY="$(printf '%s\n' "${PR_BODY_FROM_RESULT}" | sed '/^Signed-off-by:/d' | sed '/^Closes #/d; /^Closes [a-zA-Z0-9_.-]*\/[a-zA-Z0-9_.-]*#/d' | sed -e :a -e '/^\n*$/{ $d; N; ba; }')"
+else
Relevance

⭐⭐⭐ High

Repo added CODEOWNERS/ruleset enforcement and treats governance paths as requiring explicit human
review/justification.

PR-#27
PR-#29

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The compliance rule requires a finding whenever protected paths are modified. The diff shows changes
under scripts/ and skills/, triggering the protected-path requirement.

scripts/post-code.sh[446-470]
scripts/post-code-test.sh[127-157]
skills/code-implementation/SKILL.md[167-176]
Skill: pr-review

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

## Issue description
Protected paths were modified in this PR; per policy this must not be auto-approved and needs explicit justification/authorization plus human review.

## Issue Context
The repository treats changes under `scripts/` and `skills/` as protected governance/infrastructure modifications.

## Fix Focus Areas
- scripts/post-code.sh[446-470]
- scripts/post-code-test.sh[127-157]
- skills/code-implementation/SKILL.md[167-176]

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


2. jq failure ignored for pr_body ✓ Resolved 📜 Skill insight ☼ Reliability
Description
scripts/post-code.sh suppresses JSON parse/read failures when extracting .pr_body by using
2>/dev/null || true, causing a silent fallthrough to the commit-body path. This violates the
requirement that inter-component/guard failure paths be handled explicitly rather than ignored.
Code

scripts/post-code.sh[453]

+  PR_BODY_FROM_RESULT="$(jq -r '.pr_body // empty' "${RESULT_FILE}" 2>/dev/null || true)"
Relevance

⭐⭐ Medium

No prior accepted/rejected guidance found on handling jq parse failures vs silent fallback in
scripts.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The rule forbids silently ignoring failure paths for guards/contracts. The added .pr_body consumer
swallows jq errors and proceeds without warning, making contract failures non-obvious at runtime.

scripts/post-code.sh[450-454]
Skill: pr-review

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

## Issue description
`jq` failures while reading `.pr_body` from the agent result are silenced (`2>/dev/null || true`), which can hide a broken producer/consumer contract and silently change runtime behavior.

## Issue Context
This is an inter-component contract: the agent produces `code-result.json`, and `post-code.sh` consumes it to build the PR body.

## Fix Focus Areas
- scripts/post-code.sh[450-454]

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



Remediation recommended

3. Docs contradict pr_body support ✓ Resolved 📜 Skill insight ⚙ Maintainability
Description
Repository documentation about code-result.json is stale and internally contradictory: it still
describes structured output as containing only target_branch (and even “exactly one field”)
despite the schema and scripts now supporting an optional pr_body. This mismatch creates
misleading guidance for agents and reviewers about whether pr_body is permitted and how validation
behaves.
Code

schemas/code-result.schema.json[R15-19]

+    "pr_body": {
+      "type": "string",
+      "maxLength": 65536,
+      "description": "PR description used as PR body instead of commit body. Supports markdown formatting and template-compliant structure without gitlint line-length constraints."
    }
Relevance

⭐⭐⭐ High

Team recently tightened structured-output docs/validation; likely will fix contradictory docs around
code-result.json fields.

PR-#24

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The schemas/code-result.schema.json has been extended to include an optional pr_body, and the
consumer behavior (including post-code.sh) reads .pr_body when present, but the documentation
has not been updated consistently: skills/code-implementation/SKILL.md step 3 instructs agents to
emit {target_branch, pr_body} while the later “Validate structured output” step 11 still claims
the JSON must contain exactly one field and that only target_branch is allowed (extra fields fail
validation). Additionally, agents/code.md discusses structured output only in terms of
target-branch selection, reinforcing outdated guidance; together these citations show the docs
contradict the current schema/behavior.

schemas/code-result.schema.json[15-19]
skills/code-implementation/SKILL.md[810-820]
agents/code.md[95-101]
skills/code-implementation/SKILL.md[199-226]
schemas/code-result.schema.json[7-19]
Skill: docs-review
Skill: pr-review

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

## Issue description
Documentation describing structured output for `code-result.json` is now stale and contradictory because the schema (and consumer behavior) supports an optional `pr_body`, yet parts of the docs still claim the output must contain exactly one field and/or only `target_branch` is allowed.

## Issue Context
- The harness schema (`schemas/code-result.schema.json`) has been extended to allow optional `pr_body` alongside `target_branch`.
- `skills/code-implementation/SKILL.md` is internally inconsistent: step 3 instructs writing `{target_branch, pr_body}`, but the later “Validate structured output” step 11 still says the JSON must contain exactly one field and that any extra fields fail validation.
- `agents/code.md` still describes structured output primarily/only in terms of target-branch selection, which is now incomplete.
- Goal: align docs with the current schema and validation rules (including that `additionalProperties: false` still applies, but both keys are allowed and `pr_body` is optional).

## Fix Focus Areas
- schemas/code-result.schema.json[15-19]
- skills/code-implementation/SKILL.md[810-820]
- agents/code.md[95-101]

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


4. Over-broad pr_body stripping ✓ Resolved 🐞 Bug ☼ Reliability
Description
When pr_body is present, scripts/post-code.sh deletes any line that starts at column 0 with
Signed-off-by: or Closes ... before using it as the PR description. This can unintentionally
remove legitimate visible content (e.g., a template prompt/example or a code block line) that
happens to begin with those exact prefixes.
Code

scripts/post-code.sh[R450-460]

+# Read pr_body from agent output. Fall back to commit body if absent.
+PR_BODY_FROM_RESULT=""
+if [ -n "${RESULT_FILE}" ]; then
+  PR_BODY_FROM_RESULT="$(jq -r '.pr_body // empty' "${RESULT_FILE}" 2>/dev/null || true)"
+fi
+
+if [ -n "${PR_BODY_FROM_RESULT}" ]; then
+  # Agent provided pr_body (template-aware or best-effort).
+  # Strip Signed-off-by and Closes lines so the script appends them once.
+  COMMIT_BODY="$(printf '%s\n' "${PR_BODY_FROM_RESULT}" | sed '/^Signed-off-by:/d' | sed '/^Closes #/d; /^Closes [a-zA-Z0-9_.-]*\/[a-zA-Z0-9_.-]*#/d' | sed -e :a -e '/^\n*$/{ $d; N; ba; }')"
+else
Relevance

⭐⭐ Medium

No historical evidence on whether they consider sed-based stripping too broad for pr_body content.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR body is piped through sed delete commands before being used; those commands match at
start-of-line and will delete any such line regardless of whether it’s intended as an auto-close
footer or legitimate template/content text.

scripts/post-code.sh[450-470]
scripts/post-code-test.sh[127-157]

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 `pr_body` path is meant to be used largely verbatim, but the current `sed '/^Signed-off-by:/d'` and `sed '/^Closes .../d'` deletes matching lines anywhere in the PR body. If a repo PR template contains a visible “Closes #…” prompt/example, or a markdown code block includes a line that begins with `Closes #`, that line will be silently removed.

### Issue Context
- `post-code.sh` appends its own `Closes #<ISSUE_NUMBER>` footer, so it’s reasonable to remove *footer* `Closes` lines from agent-provided text.
- The current implementation removes these lines regardless of position.

### Fix Focus Areas
- scripts/post-code.sh[450-470]
- scripts/post-code-test.sh[127-157]

### What to change
- Change sanitization to only remove `Signed-off-by:` / `Closes ...` lines when they are part of the trailing footer block (e.g., strip only contiguous matching lines at the end, after trimming trailing blank lines).
- Update `post-code-test.sh`’s `build_pr_body` helper to match the production logic.
- Add a test where `pr_body` contains a code block or example line starting with `Closes #...` *not at the end* and assert it remains present.

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


Grey Divider

Qodo Logo

Comment thread scripts/post-code.sh
Comment thread scripts/post-code.sh Outdated
Comment thread schemas/code-result.schema.json
Comment thread scripts/post-code.sh
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [logic-error] skills/code-implementation/SKILL.md — The read -r -d '' pr_body <<'PRBODY' pattern in step 10d always exits with status 1 because the heredoc never contains a NUL byte (the delimiter specified by -d ''). While $pr_body is correctly populated, the non-zero exit code will cause the subsequent jq command to never execute if the agent's shell has set -e active or chains commands with &&. This could silently omit pr_body from the result file.
    Remediation: add || true to the read command, or use pr_body=$(cat <<'PRBODY' ... PRBODY) instead.

  • [protected-path] agents/code.md, scripts/post-code.sh, scripts/post-code-test.sh, skills/code-implementation/SKILL.md — This PR modifies files under protected paths (agents/, scripts/, skills/). The PR references feat(code-agent): uses PR template if found fullsend#2979 as the migration source and explains the rationale for each change. Human approval is always required for protected-path changes regardless of context.

Low

  • [test-coverage] scripts/post-code-test.sh — No test for the jq extraction path edge cases in post-code.sh (malformed JSON in RESULT_FILE, pr_body as null vs absent vs empty string). The production code handles these gracefully via 2>/dev/null and exit code checking with fallback to empty, but an explicit test would lock in the contract.

  • [test-coverage] scripts/post-code-test.sh — No test for the case where pr_body is provided AND commit_body is also non-empty AND pr_body does NOT strip to empty. The pr_body_fallback_test covers the strips-to-empty case (confirming commit body is used), but an explicit test with both non-empty would directly document and lock in the precedence: pr_body wins over commit_body.

  • [prompt-injection-surface] skills/code-implementation/SKILL.md — Step 3 item 5 instructs the code agent to read PR template files from target repos (.github/pull_request_template.md, etc.). A malicious template could contain adversarial instructions targeting the agent. Risk is bounded by defense-in-depth (sandbox isolation, post-script secret scanning and pre-commit hooks, review agent) and is consistent with the existing threat surface from reading CLAUDE.md and CONTRIBUTING.md from untrusted repos.

Prior review resolution

# Prior finding Status
1 [medium] logic-error: read -r -d '' exit code in step 10d ➡ Unchanged — remediation not applied
2 [medium] test-production-mismatch: missing gitleaks checklist line ✅ Resolved — both production and test now include the line
3 [medium] protected-path: protected files modified ➡ Unchanged — still requires human approval
4 [low] test-coverage: jq extraction edge cases ➡ Unchanged
5 [low] test-coverage: pr_body + commit_body precedence ➡ Partially addressed — pr_body_fallback_test covers strips-to-empty case
6 [low] prompt-injection-surface: PR template reading ➡ Unchanged — risk bounded by defense-in-depth

Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • agents/code.md
  • scripts/post-code-test.sh
  • scripts/post-code.sh
  • skills/code-implementation/SKILL.md
Previous run

Review

Findings

Medium

  • [logic-error] skills/code-implementation/SKILL.md — The read -r -d '' pr_body <<'PRBODY' pattern in new step 10d always exits with status 1 because the heredoc never contains a NUL byte (the delimiter specified by -d ''). While $pr_body is correctly populated, the non-zero exit code will cause the subsequent jq command to never execute if the agent's shell has set -e active or chains commands with &&. This could silently omit pr_body from the result file.
    Remediation: add || true to the read command.

  • [test-production-mismatch] scripts/post-code-test.sh — The test's build_pr_body footer template is missing the new - [x] PR body secret scan passed (gitleaks — no-git) checklist line that the production code in post-code.sh adds. No current test asserts on this line, but the mismatch means the test function no longer faithfully reproduces production output.

  • [protected-path] agents/code.md, scripts/post-code.sh, scripts/post-code-test.sh, skills/code-implementation/SKILL.md — This PR modifies files under protected paths (agents/, scripts/, skills/). The PR references feat(code-agent): uses PR template if found fullsend#2979 as the migration source and explains the rationale for each change. Human approval is always required for protected-path changes regardless of context.

Low

  • [test-coverage] scripts/post-code-test.sh — No test for the jq extraction path edge cases in post-code.sh (malformed JSON in RESULT_FILE, pr_body as null vs absent vs empty string). The production code handles these gracefully via 2>/dev/null and exit code checking with fallback to empty, but an explicit test would lock in the contract.

  • [test-coverage] scripts/post-code-test.sh — No test for the case where pr_body is provided AND commit_body is also non-empty AND pr_body does NOT strip to empty. An explicit test with both non-empty would directly document and lock in the precedence: pr_body wins over commit_body.

  • [prompt-injection-surface] skills/code-implementation/SKILL.md — Step 3 item 5 instructs the code agent to read PR template files from target repos (.github/pull_request_template.md, etc.). A malicious template could contain adversarial instructions targeting the agent. Risk is bounded by defense-in-depth (sandbox isolation, post-script secret scanning and pre-commit hooks, review agent) and is consistent with the existing threat surface from reading CLAUDE.md and CONTRIBUTING.md from untrusted repos.

Prior review resolution

# Prior finding Status
1 [medium] protected-path: protected files modified ➡ Unchanged — still requires human approval
2 [low] test-coverage: jq extraction edge cases ➡ Unchanged
3 [low] test-coverage: pr_body + commit_body precedence ➡ Partially addressed — pr_body_fallback_test covers strips-to-empty case
4 [low] prompt-injection-surface: PR template reading ➡ Unchanged — risk bounded by defense-in-depth

Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • agents/code.md
  • scripts/post-code-test.sh
  • scripts/post-code.sh
  • skills/code-implementation/SKILL.md
Previous run (2)

Review

Findings

Medium

  • [protected-path] agents/code.md, scripts/post-code.sh, scripts/post-code-test.sh, skills/code-implementation/SKILL.md — This PR modifies files under protected paths (agents/, scripts/, skills/). The PR references feat(code-agent): uses PR template if found fullsend#2979 as the migration source and explains the rationale for each change. Human approval is always required for protected-path changes regardless of context.

Low

  • [test-coverage] scripts/post-code-test.sh — No test for the jq extraction path edge cases in post-code.sh (malformed JSON in RESULT_FILE, pr_body as null vs absent vs empty string). The production code handles these gracefully via 2>/dev/null and exit code checking with fallback to empty, but an explicit test would lock in the contract.

  • [test-coverage] scripts/post-code-test.sh — No test for the case where pr_body is provided AND commit_body is also non-empty. The code correctly prioritizes pr_body (checked first via the if-branch), but an explicit test would document and lock in the precedence behavior.

  • [prompt-injection-surface] skills/code-implementation/SKILL.md:169 — Step 3 item 5 instructs the code agent to read PR template files from target repos (.github/pull_request_template.md, etc.). A malicious template could contain adversarial instructions targeting the agent. Risk is bounded by defense-in-depth (sandbox isolation, post-script secret scanning and pre-commit hooks, review agent) and is consistent with the existing threat surface from reading CLAUDE.md and CONTRIBUTING.md from untrusted repos.

Prior review resolution

# Prior finding Status
1 [low] test-coverage: trailing blank lines before footer ✅ Resolved — pr-body-trailing-blanks-before-footer test added
2 [low] prompt-injection-surface: PR template reading ➡ Unchanged — risk bounded by defense-in-depth

Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • agents/code.md
  • scripts/post-code-test.sh
  • scripts/post-code.sh
  • skills/code-implementation/SKILL.md
Previous run (3)

Review

Verdict: approve · 2 low findings

Clean feature addition. The prior medium-severity finding (step 11 in SKILL.md contradicting the new pr_body field by stating "exactly one field" and "only target_branch allowed") has been resolved — the updated diff now correctly says "target_branch (required) and optionally pr_body" and "Only target_branch and pr_body are allowed."

The architecture follows the established structured-output pattern cleanly: agent writes pr_body to code-result.json, post-script reads it via jq, strips trailing footer lines with a well-constructed awk pipeline, and falls back to commit body when absent. The schema change is additive-only (optional field, no required-array change), so existing outputs continue to validate. Shell handling is safe throughout — printf '%s\n' prevents backslash interpretation, double-quoting prevents word splitting, jq --arg avoids shell expansion when writing, and the data flows to gh pr create --body as a single quoted argument with no secondary shell interpretation.

The awk footer-stripping logic is more comprehensive than the legacy path: it handles Closes, Closed, Close, Fixes, Fixed, Fix, Resolves, Resolved, Resolve (case-flexible), and cross-repo references (org/repo#N), while only stripping from the end to preserve matching lines in body content. The legacy path continues unchanged for backward compatibility.

Test coverage is solid: 8 new test cases cover the happy path, verbatim pass-through (no unwrapping), strip-to-empty fallback, cross-repo Closes stripping, content-embedded Closes preservation, and Fixes/Resolves keyword variants.

Findings

1. [low] [test-coverage] scripts/post-code-test.sh

No test for pr_body with multiple consecutive trailing blank lines before a footer line (e.g., "Summary\n\n\n\nCloses #42"). The awk logic handles this correctly (blank lines are in the strip set and the while loop processes them), but an explicit test would lock in the behavior and catch regressions if the awk pattern changes.

2. [low] [prompt-injection-surface] skills/code-implementation/SKILL.md · step 3 item 5

The new step instructs the agent to read PR template files from target repos (.github/pull_request_template.md, etc.). A malicious template could contain adversarial instructions targeting the agent. Risk is bounded: the agent operates in a sandbox, the instruction to "skip HTML comments" partially mitigates, and the post-script applies defense-in-depth gates (secret scan, pre-commit, branch validation, review agent). This is consistent with the existing threat surface from reading CLAUDE.md and CONTRIBUTING.md from untrusted repos.

Prior review resolution

# Prior finding Status
1 [medium] Step 11 stale instructions ✅ Resolved — text updated to reflect both fields
2 [low] agents/code.md incomplete description ✅ Resolved — structured output section updated
3 [low] Test coverage gaps ✅ Mostly resolved — Fixes/Resolves/cross-repo tests added; one minor gap remains (finding 1 above)
4 [low] Test reimplements production logic ➡ Unchanged — established pattern in this file

Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • agents/code.md
  • scripts/post-code-test.sh
  • scripts/post-code.sh
  • skills/code-implementation/SKILL.md
Previous run (4)

Review

Verdict: comment · 1 medium, 3 low findings

Clean feature migration. The architecture (agent writes pr_body to code-result.json, post-script reads it verbatim, falls back to commit body) fits the existing structured-output pattern. Schema change is backward compatible (additive optional field), shell handling is safe (printf '%s\n' + sed pipeline), and the test additions follow the established test-helper reimplementation pattern in post-code-test.sh. The jq error-suppression idiom matches the existing AGENT_TARGET extraction on line 80.

Findings

1. [medium] [stale-instructions] skills/code-implementation/SKILL.md · lines 780–790

Step 11 text contradicts the new pr_body field. Step 11 currently states: "The file must be valid JSON with exactly one field" and "Only the target_branch field is allowed. Any extra fields will cause validation to fail." Since this PR adds pr_body as a valid optional field in the schema, those statements are now factually wrong. An agent following step 11 literally may remove pr_body from the output before running fullsend-check-output, silently defeating the feature.

The risk is mitigated by the fact that step 3 (newly updated) explicitly instructs the agent to write pr_body, and fullsend-check-output will pass with pr_body present. However, the contradictory text creates unnecessary ambiguity.

Remediation: Update step 11 to reflect the new schema — change "exactly one field" to "the required field (target_branch) and optionally pr_body", update the JSON example to show both fields, and revise the compliance note.

2. [low] [stale-documentation] agents/code.md · lines 97–100

The structured output section says the file "documents the target branch for PR creation" and the post-script "reads this file to determine which branch to target the PR against." Now that the file also carries pr_body, this description is incomplete. The schema and SKILL.md are the authoritative references, so impact is minimal.

3. [low] [test-coverage] scripts/post-code-test.sh

The new pr_body tests cover the happy path (content present, verbatim pass-through, Closes deduplication) but omit several edge cases: (a) pr_body that becomes empty after Signed-off-by / Closes stripping — should fall back to the automated description via the existing if [ -z "${COMMIT_BODY}" ] guard; (b) pr_body with cross-repo closes (Closes org/repo#N); (c) pr_body with trailing blank lines. These are handled by existing fallback logic, so the risk is low.

4. [low] [code-organization] scripts/post-code-test.sh · line 127

build_pr_body() in the test file reimplements the production inline logic from post-code.sh. The sed pipelines currently match exactly. This follows the established pattern throughout post-code-test.sh (8+ helpers reimplement production logic for isolation testing), so it is consistent — but the duplication remains a future maintenance consideration.

Notes

  • The Closes-stripping sed patterns only handle the Closes keyword (not Fixes, Resolves, Fix, etc.). This is a pre-existing limitation shared with the legacy commit-body path and is not introduced by this PR. The PR actually improves coverage by adding cross-repo Closes stripping in the new path.
  • The asymmetry between the legacy path (sed '/^Closes #/d') and the new path (which adds cross-repo pattern) is an intentional improvement, not a regression.
  • The schema change is fully backward compatible: pr_body is optional, not in the required array, and additionalProperties: false is maintained. Existing outputs with only target_branch continue to validate.
  • Shell safety is sound: printf '%s\n' prevents backslash interpretation, double-quoting prevents word splitting, and sed reads from stdin. No injection vector exists in the new data path.

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-squad pass (5 agents: Claude, Gemini, Codex) on top of the existing qodo-code-review findings. Posted 6 inline comments for medium+ issues not already covered by qodo's 4 comments (protected-paths, jq-failure-silencing, docs-contradiction, over-broad-stripping).

One additional MEDIUM finding couldn't be attached inline because it's on an unchanged line outside the diff:

[MEDIUM] Step 6's "Label-gated" early exit has no fullsend-check-output call (skills/code-implementation/SKILL.md, the "Label-gated" bullet under step 6, "Identify the task type"). The prior step 3 had an explicit instruction that any early exit (steps 4, 6, or 7) must run fullsend-check-output before exiting; this PR's diff removes that block. Steps 4, 7, 9c, and 11 each have their own inline call, and the "Already-fixed" bullet routes through step 7's call — but "Label-gated" has no call of its own and previously relied on the now-removed step 3 guidance. Suggest adding an explicit fullsend-check-output "${FULLSEND_OUTPUT_DIR}/code-result.json" call to that bullet.

No shell/command-injection issues found — verified gh pr create --body handling is safe against $()/backtick payloads in pr_body, and pr_body never reaches GITHUB_OUTPUT.

Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread scripts/post-code-test.sh
Comment thread scripts/post-code.sh Outdated
Comment thread scripts/post-code-test.sh
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:00 AM UTC · Completed 7:08 AM UTC
Commit: 0c74ec6 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Jul 10, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:17 AM UTC · Completed 7:33 AM UTC
Commit: ee18489 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 10, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-squad pass (3 agents: Claude, Claude, Gemini) on top of the existing qodo-code-review and fullsend-ai-review bot history. All prior findings were re-verified against the live file content before this pass (three from the prior ad hoc review-squad round turned out to be false positives — ${FULLSEND_OUTPUT_FILE} undefined, ${pr_template} undefined, and the docs-say-"exactly one field" claim — none reproduce against the current file). Posting the unique medium-and-above findings from this round below as inline comments.

Most notable: a CRITICAL, empirically-reproduced command-injection bug in the step 10a jq --arg merge example, which slipped past three prior review rounds — including this repo's own deployed fullsend-ai-review bot explicitly asserting twice that "jq --arg avoids shell expansion." I verified this live: substituting a backtick-quoted command into the documented pattern causes bash to execute it before jq ever sees the string.

Also new: a HIGH finding that pr_body never passes through gitleaks/secret scanning (it lives under $FULLSEND_OUTPUT_DIR, outside the git tree gitleaks detect --source . scans), and a HIGH finding that pr_body's maxLength (65536) exactly matches GitHub's hard body-length ceiling with no headroom for the footer post-code.sh always appends.

Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread scripts/post-code.sh
Comment thread schemas/code-result.schema.json Outdated
Comment thread scripts/post-code.sh Outdated
Comment thread scripts/post-code.sh Outdated
Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread scripts/post-code-test.sh
Comment thread scripts/post-code-test.sh Outdated
Comment thread skills/code-implementation/SKILL.md Outdated
Comment thread skills/code-implementation/SKILL.md

@ralphbean ralphbean left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. One minor note inline. I agree with @waynesun09's findings on the jq --arg safety claim and the maxLength headroom — thumbs-upped those.

Comment thread scripts/post-code.sh Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 6:46 AM UTC · Ended 6:52 AM UTC
Commit: 5cd495a · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:52 AM UTC · Completed 7:13 AM UTC
Commit: bdafc8a · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 15, 2026
Add pr_body to code-result schema so agents can write PR descriptions
matching repo templates. Post-script reads, secret-scans, and sanitizes
pr_body before creating the PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Hector Martinez <hemartin@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:43 AM UTC · Completed 8:00 AM UTC
Commit: a108cb8 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 15, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-squad pass (3 agents: Claude, Claude, Grok). Posted 4 inline comments for the unique medium findings not already covered by prior review rounds.

All prior CRITICAL/HIGH findings verified as resolved (jq --arg injection fixed, maxLength reduced to 60000, pr_body secret scan added, footer regex end-anchored). Two approvals (ascerra, ralphbean) remain valid.

Assisted-by: Claude (review), Grok (review)

expansion, then pass it to `jq --arg`:

```bash
read -r -d '' pr_body <<'PRBODY'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] read -r -d '' returns exit 1, breaks under set -e

read -r -d '' reads until a null byte. Heredocs never contain null bytes, so read hits EOF and returns exit code 1 — even though $pr_body is correctly populated. If an agent wraps this in a script with set -e or set -euo pipefail, the shell terminates before the jq line executes and pr_body is never written to the result file.

Without set -e it works (the non-zero exit doesn't prevent the next command from running), but the pattern is fragile and will silently break if the execution environment changes.

Suggestion: Append || true:

read -r -d '' pr_body <<'PRBODY' || true
<pr_body content>
PRBODY

Or replace with cat command substitution (returns 0, also strips trailing newlines which is generally desirable for PR body content):

pr_body=$(cat <<'PRBODY'
<pr_body content>
PRBODY
)

§ Consensus — flagged independently by 3/3 review agents.

Comment thread scripts/post-code.sh

- [x] Branch is not main/master (\`${BRANCH}\`)
- [x] Secret scan passed (gitleaks — \`${SCAN_RANGE}\`)
- [x] PR body secret scan passed (gitleaks — no-git)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] Unconditional "PR body secret scan passed" checklist item is inaccurate

This line is hardcoded and always rendered, regardless of what actually happened. Three scenarios produce misleading audit claims:

  1. No pr_body exists (legacy commit-body path) — no scan ran, but checklist says it passed
  2. gitleaks detected a secret — script correctly fell back to commit body, but checklist still says "passed"
  3. gitleaks itself errored (corrupt binary, permission denied) — same misleading "passed" message

The security behavior is correct in all cases (no secrets leak), but the audit trail is inaccurate. The same unconditional line appears in the test helper at post-code-test.sh:176.

Suggestion: Make the line conditional on what actually happened:

if [ -n "${PR_BODY_SCANNED:-}" ]; then
  PR_BODY_SCAN_LINE="- [x] PR body secret scan passed (gitleaks — no-git)"
else
  PR_BODY_SCAN_LINE="- [x] PR body secret scan: N/A (commit body path)"
fi

Set PR_BODY_SCANNED=1 after a successful gitleaks run. For the "blocked" case, use "- [x] PR body secret scan: blocked, fell back to commit body".

§ Consensus — flagged independently by 2/3 review agents.

Comment thread scripts/post-code.sh
Comment on lines 553 to +560

if [ -z "${COMMIT_BODY}" ]; then
# pr_body stripped to empty — fall back to commit body before placeholder
COMMIT_BODY_RAW="$(git log -1 --format='%b' HEAD | sed '/^Signed-off-by:/d' | sed '/^Closes #/d' | sed -e :a -e '/^\n*$/{ $d; N; ba; }')"
COMMIT_BODY="$(echo "${COMMIT_BODY_RAW}" | awk '
/^$/ { if (buf) print buf; print; buf=""; next }
/^[-*#>]|^ / { if (buf) print buf; buf=""; print; next }
/^Closes / { if (buf) print buf; buf=""; print; next }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] Intra-file duplication of commit-body extraction pipeline

This 7-line git log | sed | sed | sed + awk pipeline is identical to the one at lines 521–529 (the legacy else branch). A future change to the extraction logic must be applied to both copies, and divergence between them would be silent.

The prior review flagged cross-file duplication (test vs production) — author noted "multi-scripts not supported." This is intra-file duplication within the same script, fixable with a local function:

extract_commit_body() {
  local raw
  raw="$(git log -1 --format='%b' HEAD | sed '/^Signed-off-by:/d' | sed '/^Closes #/d' | sed -e :a -e '/^\n*$/{ $d; N; ba; }')"
  echo "${raw}" | awk '
    /^$/           { if (buf) print buf; print; buf=""; next }
    /^[-*#>]|^  /  { if (buf) print buf; buf=""; print; next }
    /^Closes /     { if (buf) print buf; buf=""; print; next }
                   { buf = (buf ? buf " " $0 : $0) }
    END            { if (buf) print buf }
  '
}

Then both call sites become COMMIT_BODY="$(extract_commit_body)".

§ Consensus — flagged independently by 2/3 review agents.

Comment thread scripts/post-code.sh
if [ -n "${PR_BODY_FROM_RESULT}" ]; then
PR_BODY_TMP="$(mktemp)"
printf '%s\n' "${PR_BODY_FROM_RESULT}" > "${PR_BODY_TMP}"
if ! gitleaks detect --source "${PR_BODY_TMP}" --no-git --redact 2>/dev/null; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] gitleaks stderr suppressed — runtime errors misdiagnosed as "secret detected"

2>/dev/null discards all stderr. If gitleaks fails due to a config error, missing binary, permission denied, or corrupt installation, the non-zero exit code triggers "BLOCKED — secret detected in pr_body" when no secret was actually found. The conservative fallback (using commit body) is correct, but the diagnostic misleads anyone debugging a workflow failure — they'll chase a phantom secret instead of a broken tool.

Suggestion: Capture the exit code and distinguish detection from error:

gitleaks detect --source "${PR_BODY_TMP}" --no-git --redact 2>/dev/null
GL_RC=$?
if [ "${GL_RC}" -eq 1 ]; then
  echo "::warning::BLOCKED — secret detected in pr_body; falling back to commit body"
  PR_BODY_FROM_RESULT=""
elif [ "${GL_RC}" -ne 0 ]; then
  echo "::warning::gitleaks scan failed (exit ${GL_RC}); falling back to commit body"
  PR_BODY_FROM_RESULT=""
fi

Or at minimum, redirect stderr to ::debug:: instead of /dev/null so CI logs retain the diagnostic when needed.

§ Consensus — flagged independently by 2/3 review agents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code agent should follow the target repo's PR template

4 participants