Skip to content
Open
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
9 changes: 5 additions & 4 deletions agents/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ the review agent — if the triage was wrong, your code will fail review.
## Structured output

You MUST produce a JSON file at `$FULLSEND_OUTPUT_DIR/code-result.json`
that documents the target branch for PR creation. The `code-implementation`
skill describes the schema and the exact step where you write it. The
post-script reads this file to determine which branch to target the PR
against. Without this file, the validation loop rejects the run and retries.
with `target_branch` (required) and optionally `pr_body` for the PR
description. The `code-implementation` skill describes the schema and
the exact steps where you write each field. The post-script reads this
file to determine the PR target branch and description. Without this
file, the validation loop rejects the run and retries.

After writing the file, validate it before exiting:

Expand Down
7 changes: 6 additions & 1 deletion schemas/code-result.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "code-result.schema.json",
"title": "Code Agent Result",
"description": "Structured output from the code agent documenting the target branch for PR creation.",
"description": "Structured output from the code agent documenting the target branch and PR body for PR creation.",
"type": "object",
"required": ["target_branch"],
"additionalProperties": false,
Expand All @@ -11,6 +11,11 @@
"type": "string",
"description": "Branch the PR should target. Determined by the agent from issue context.",
"pattern": "^[a-zA-Z0-9._/-]+$"
},
"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."
}
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
}
}
153 changes: 148 additions & 5 deletions scripts/post-code-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,35 @@ build_pr_body() {
local issue_number="$2"
local branch="$3"
local scan_range="$4"
local pr_body_from_result="${5:-}" # optional: agent-provided pr_body

local description=""
if [ -n "${pr_body_from_result}" ]; then
Comment thread
rh-hemartin marked this conversation as resolved.
# Agent provided pr_body — strip trailing footer lines only.
description="$(printf '%s\n' "${pr_body_from_result}" | awk '
{ lines[NR] = $0 }
END {
end = NR
while (end > 0) {
l = lines[end]
if (l == "" || l ~ /^Signed-off-by:/ || l ~ /^[Cc]lose[sd]? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/ || l ~ /^[Ff]ix(e[sd])? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/ || l ~ /^[Rr]esolve[sd]? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/)
end--
else
break
}
for (i = 1; i <= end; i++)
print lines[i]
}
')"
fi

local description
if [ -z "${commit_body}" ]; then
description="Automated implementation for issue #${issue_number}."
else
description="${commit_body}"
# Fall back if pr_body was absent or stripped to empty
if [ -z "${description}" ]; then
if [ -z "${commit_body}" ]; then
description="Automated implementation for issue #${issue_number}."
else
description="${commit_body}"
fi
fi

echo "${description}
Expand Down Expand Up @@ -250,6 +273,126 @@ count_closes_test "single-closes-with-body" \
count_closes_test "single-closes-empty-body" \
"" "99"

# Verify pr_body path strips Closes lines (agent may include them)
count_closes_pr_body_test() {
local test_name="$1"
local pr_body="$2"
local issue_number="$3"

local actual
actual="$(build_pr_body "" "${issue_number}" "agent/${issue_number}-fix" "abc123..def456" "${pr_body}")"

local count
count=$(echo "${actual}" | grep -c "Closes #${issue_number}" || true)

if [ "${count}" -ne 1 ]; then
echo "FAIL: ${test_name}"
echo " expected exactly 1 'Closes #${issue_number}', found ${count}"
echo " in body:"
echo "${actual}" | sed 's/^/ /'
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

count_closes_pr_body_test "single-closes-pr-body-with-closes" \
Comment thread
rh-hemartin marked this conversation as resolved.
"## Summary

Implemented widget rendering.

Closes #42" "42"

count_closes_pr_body_test "single-closes-pr-body-with-cross-repo-closes" \
"## Summary

Implemented widget rendering.

Closes fullsend-ai/agents#42" "42"

# --- pr_body path test cases ---

# Helper for pr_body tests (fifth arg is pr_body from result file)
run_pr_body_test() {
local test_name="$1"
local pr_body="$2"
local issue_number="$3"
local branch="$4"
local check_pattern="$5"
local expect_present="$6" # "yes" or "no"

local actual
actual="$(build_pr_body "" "${issue_number}" "${branch}" "abc123..def456" "${pr_body}")"

if [ "${expect_present}" = "yes" ]; then
if ! echo "${actual}" | grep -qF "${check_pattern}"; then
echo "FAIL: ${test_name}"
echo " expected to find: '${check_pattern}'"
echo " in body:"
echo "${actual}" | sed 's/^/ /'
FAILURES=$((FAILURES + 1))
return
fi
else
if echo "${actual}" | grep -qF "${check_pattern}"; then
echo "FAIL: ${test_name}"
echo " expected NOT to find: '${check_pattern}'"
echo " in body:"
echo "${actual}" | sed 's/^/ /'
FAILURES=$((FAILURES + 1))
return
fi
fi

echo "PASS: ${test_name}"
}

# pr_body provided by agent should appear in final PR body
run_pr_body_test "pr-body-from-result" \
$'## Summary\n\nAdded widget rendering.\n\n## Testing\n\nManual test.' \
"42" "agent/42-widget" \
"Added widget rendering." "yes"

# pr_body should NOT be word-wrapped (it's verbatim)
run_pr_body_test "pr-body-verbatim" \
$'## Summary\n\nThis is a very long line that would normally be word-wrapped by the legacy commit-body awk logic but should remain intact when coming from pr_body.' \
"42" "agent/42-widget" \
"This is a very long line that would normally be word-wrapped by the legacy commit-body awk logic but should remain intact when coming from pr_body." "yes"

# pr_body that strips to empty should fall back to automated description
run_pr_body_test "pr-body-strips-to-empty" \
$'Closes #42' \
"42" "agent/42-widget" \
"Automated implementation for issue #42." "yes"

# Cross-repo Closes in trailing footer should be stripped
run_pr_body_test "pr-body-cross-repo-closes" \
$'## Summary\n\nImplemented widget rendering.\n\nCloses fullsend-ai/agents#42' \
"42" "agent/42-widget" \
"Closes fullsend-ai/agents#42" "no"

# Closes-like line in body content (not footer) should be preserved
run_pr_body_test "pr-body-closes-in-content-preserved" \
$'## Summary\n\nThis fixes the issue where Closes #99 was not handled.\n\n## Testing\n\nManual test.' \
"42" "agent/42-widget" \
"Closes #99 was not handled" "yes"

# Multiple trailing blank lines before footer should all be stripped
count_closes_pr_body_test "pr-body-trailing-blanks-before-footer" \
$'## Summary\n\nDid the thing.\n\n\n\nCloses #42' "42"

# GitHub auto-close keyword variants should be stripped from footer
run_pr_body_test "pr-body-fixes-keyword-stripped" \
$'## Summary\n\nFixed the bug.\n\nFixes #42' \
"42" "agent/42-widget" \
"Fixes #42" "no"

run_pr_body_test "pr-body-resolves-keyword-stripped" \
$'## Summary\n\nResolved the issue.\n\nResolves #42' \
"42" "agent/42-widget" \
"Resolves #42" "no"

# ---------------------------------------------------------------------------
# Test helper — reimplements the no-op detection logic from post-code.sh
# so we can test it without a git repo or network access.
Expand Down
49 changes: 40 additions & 9 deletions scripts/post-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -446,15 +446,46 @@ fi
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
if ! PR_BODY_FROM_RESULT="$(jq -r '.pr_body // empty' "${RESULT_FILE}" 2>/dev/null)"; then
echo "::notice::pr_body not found in result file; using commit body"
PR_BODY_FROM_RESULT=""
fi
fi

if [ -n "${PR_BODY_FROM_RESULT}" ]; then
# Agent provided pr_body — strip trailing footer lines (Signed-off-by,
# GitHub auto-close keywords) so the script appends them once.
# Only strips from the end to preserve matching lines in body content.
COMMIT_BODY="$(printf '%s\n' "${PR_BODY_FROM_RESULT}" | awk '
{ lines[NR] = $0 }
END {
end = NR
while (end > 0) {
l = lines[end]
if (l == "" || l ~ /^Signed-off-by:/ || l ~ /^[Cc]lose[sd]? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/ || l ~ /^[Ff]ix(e[sd])? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/ || l ~ /^[Rr]esolve[sd]? (#|[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#)/)
end--
else
break
}
for (i = 1; i <= end; i++)
print lines[i]
}
')"
else
Comment thread
rh-hemartin marked this conversation as resolved.
Comment thread
rh-hemartin marked this conversation as resolved.
# Fall back to unwrapped commit body (legacy path)
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 }
')"
fi

# ---------------------------------------------------------------------------
# Ensure PR title includes an issue reference.
Expand Down
Loading
Loading