diff --git a/devloop b/devloop
index 3dc1432..e8d5b76 100755
--- a/devloop
+++ b/devloop
@@ -2160,7 +2160,7 @@ run_devloop() {
     if [ "$create_pr" = true ] && [ -n "$PASS_COMMIT" ]; then
       local pr_id="pull-request-$pass"
       event_step "$pr_id" "push branch and ensure draft PR"
-      if create_pull_request "$repo" "$FINAL_BRANCH" "$base"; then
+      if create_pull_request "$repo" "$FINAL_BRANCH" "$base" "$spec" "$criteria_file" "$FINAL_COMMIT"; then
         event_done "$pr_id" true "$PULL_REQUEST"
       else
         STATUS="pr-error"
@@ -2212,7 +2212,7 @@ run_devloop() {
     if [ "$create_pr" = true ] && [ -n "$PULL_REQUEST" ]; then
       local pr_comment_id="pr-review-comment-$pass"
       event_step "$pr_comment_id" "post round review to PR"
-      if post_pr_round_review "$repo" "$PULL_REQUEST" "$repo/$review" "$pass" "$slug"; then
+      if post_pr_round_review "$repo" "$PULL_REQUEST" "$repo/$review" "$pass"; then
         event_done "$pr_comment_id" true "posted"
       else
         STATUS="pr-error"
@@ -2261,7 +2261,7 @@ run_devloop() {
 
   if [ "$create_pr" = true ] && [ -n "$PULL_REQUEST" ] && [ "$STATUS" != "pr-error" ]; then
     event_step "pr-final-report" "post final report to PR"
-    if post_pr_final_report "$repo" "$PULL_REQUEST" "$slug" "$STATUS" "$PASSES" "$max" "$FINAL_BRANCH" "$REPORT" "$report_format"; then
+    if post_pr_final_report "$repo" "$PULL_REQUEST" "$slug" "$STATUS" "$PASSES" "$max" "$FINAL_BRANCH"; then
       event_done "pr-final-report" true "posted"
     else
       STATUS="pr-error"
@@ -3535,7 +3535,10 @@ create_pull_request() {
   local repo="$1"
   local branch="$2"
   local base="$3"
-  ensure_pull_request "$repo" "$branch" "$base"
+  local spec="${4:-}"
+  local criteria_file="${5:-}"
+  local commit="${6:-}"
+  ensure_pull_request "$repo" "$branch" "$base" "$spec" "$criteria_file" "$commit"
 }
 
 push_pull_request_branch() {
@@ -3561,16 +3564,101 @@ lookup_pull_request() {
   PULL_REQUEST="$(printf '%s\n' "$out" | sed '/^[[:space:]]*$/d' | head -n 1)"
 }
 
+spec_title() {
+  local spec="$1"
+  [ -f "$spec" ] || return 1
+  awk '
+    /^#[[:space:]]+/ {
+      sub(/^#[[:space:]]+/, "")
+      print
+      exit
+    }
+  ' "$spec"
+}
+
+spec_section() {
+  local spec="$1"
+  local name="$2"
+  [ -f "$spec" ] || return 1
+  awk -v want="$name" '
+    /^[[:space:]]*##[[:space:]]+/ {
+      heading = $0
+      sub(/^[[:space:]]*##[[:space:]]+/, "", heading)
+      sub(/[[:space:]]+$/, "", heading)
+      if (tolower(heading) == tolower(want)) { inside = 1; started = 0; next }
+      if (inside) exit
+    }
+    inside {
+      if (!started && $0 ~ /^[[:space:]]*$/) next
+      started = 1
+      print
+    }
+  ' "$spec"
+}
+
+draft_pull_request_body() {
+  local spec="$1"
+  local criteria_file="$2"
+  local commit="$3"
+  local title problem outcome
+  title="$(spec_title "$spec" 2>/dev/null || true)"
+  problem="$(spec_section "$spec" "Problem" 2>/dev/null || true)"
+  outcome="$(spec_section "$spec" "Outcome" 2>/dev/null || true)"
+  if [ -z "$title" ]; then title="Devloop change"; fi
+  if [ -z "$problem" ]; then problem="_No problem statement in the source spec._"; fi
+  if [ -z "$outcome" ]; then outcome="_No outcome described in the source spec._"; fi
+  if [ -z "$commit" ]; then commit="none"; fi
+  cat <<EOF
+# $title
+
+Generated by \`devloop --create-pr\`.
+
+## Problem
+
+$problem
+
+## Outcome
+
+$outcome
+
+## Acceptance Criteria
+
+$(if [ -n "$criteria_file" ] && [ -f "$criteria_file" ]; then criteria_block "$criteria_file"; else printf '%s\n' "No parsed acceptance criteria."; fi)
+
+## Review Trail
+
+Review rounds and the final report are posted as PR comments below.
+
+---
+
+Latest commit: $commit
+EOF
+}
+
 create_draft_pull_request() {
   local repo="$1"
   local branch="$2"
   local base="$3"
-  local out
-  if ! run_compact_command "$repo" "open draft pull request" gh pr create --draft --fill --base "$base" --head "$branch"; then
+  local spec="${4:-}"
+  local criteria_file="${5:-}"
+  local commit="${6:-}"
+  local body_file out
+  if ! body_file="$(mktemp "${TMPDIR:-/tmp}/devloop-pr-create.XXXXXX")"; then
+    PULL_REQUEST_ERROR="PR body file failed: mktemp failed"
+    return 1
+  fi
+  if ! draft_pull_request_body "$spec" "$criteria_file" "$commit" > "$body_file"; then
+    PULL_REQUEST_ERROR="PR body file failed"
+    rm -f "$body_file"
+    return 1
+  fi
+  if ! run_compact_command "$repo" "open draft pull request" gh pr create --draft --fill --base "$base" --head "$branch" --body-file "$body_file"; then
     out="$RUN_OUTPUT"
     PULL_REQUEST_ERROR="PR creation failed: $(gh_error_detail "$out")"
+    rm -f "$body_file"
     return 1
   fi
+  rm -f "$body_file"
   out="$RUN_OUTPUT"
   PULL_REQUEST="$(printf '%s\n' "$out" | awk '/^https?:\/\// { print; exit }')"
   if [ -z "$PULL_REQUEST" ]; then PULL_REQUEST="$(printf '%s\n' "$out" | tail -n 1)"; fi
@@ -3580,17 +3668,19 @@ ensure_pull_request() {
   local repo="$1"
   local branch="$2"
   local base="$3"
+  local spec="${4:-}"
+  local criteria_file="${5:-}"
+  local commit="${6:-}"
   PULL_REQUEST_ERROR=""
   if ! push_pull_request_branch "$repo" "$branch"; then return 1; fi
   if ! lookup_pull_request "$repo" "$branch"; then return 1; fi
   if [ -n "$PULL_REQUEST" ]; then return 0; fi
-  create_draft_pull_request "$repo" "$branch" "$base"
+  create_draft_pull_request "$repo" "$branch" "$base" "$spec" "$criteria_file" "$commit"
 }
 
 round_review_comment_body() {
   local review_file="$1"
   local pass="$2"
-  local slug="$3"
   local verdict
   verdict="$(parse_verdict "$review_file")"
   if [ -z "$verdict" ]; then verdict="UNCLEAR"; fi
@@ -3598,7 +3688,6 @@ round_review_comment_body() {
 # Devloop Review Round $pass
 
 - Verdict: $verdict
-- Local cache: \`.codex/reviews/$slug-r$pass.md\`
 
 $(cat "$review_file")
 EOF
@@ -3609,10 +3698,9 @@ post_pr_round_review() {
   local pr="$2"
   local review_file="$3"
   local pass="$4"
-  local slug="$5"
   local body_file out
   body_file="$(mktemp "${TMPDIR:-/tmp}/devloop-pr-review.XXXXXX")"
-  round_review_comment_body "$review_file" "$pass" "$slug" > "$body_file"
+  round_review_comment_body "$review_file" "$pass" > "$body_file"
   if ! out="$(cd "$repo" >/dev/null 2>&1 && gh pr comment "$pr" --body-file "$body_file" 2>&1)"; then
     PULL_REQUEST_ERROR="PR comment failed: $(gh_error_detail "$out")"
     rm -f "$body_file"
@@ -3653,10 +3741,8 @@ final_pr_report_body() {
   local max="$5"
   local pr="$6"
   local branch="$7"
-  local report="$8"
-  local report_format="$9"
   local latest_review="$repo/.codex/reviews/$slug-r$pass.md"
-  local final_verdict acceptance engineering html_path risk
+  local final_verdict acceptance engineering risk
   final_verdict=""
   if [ -f "$latest_review" ]; then final_verdict="$(parse_verdict "$latest_review")"; fi
   if [ -z "$final_verdict" ]; then final_verdict="none"; fi
@@ -3664,10 +3750,6 @@ final_pr_report_body() {
   engineering="$(review_section "$latest_review" "Engineering quality matrix" || true)"
   if [ -z "$acceptance" ]; then acceptance="- Not available."; fi
   if [ -z "$engineering" ]; then engineering="- Not available."; fi
-  html_path="Not generated."
-  if [ "$report_format" = "html" ] && [ -f "$repo/$report" ]; then
-    html_path="$repo/$report"
-  fi
   case "$status" in
     accepted) risk="No blocking residual risk was recorded by the final review." ;;
     *) risk="The run ended as $status. Inspect the latest findings and missing tests before merging." ;;
@@ -3709,10 +3791,6 @@ $(commit_lines)
 ## Residual Risk
 
 - $risk
-
-## Local Report
-
-- HTML report path: $html_path
 EOF
 }
 
@@ -3724,11 +3802,9 @@ post_pr_final_report() {
   local pass="$5"
   local max="$6"
   local branch="$7"
-  local report="$8"
-  local report_format="$9"
   local body_file out
   body_file="$(mktemp "${TMPDIR:-/tmp}/devloop-pr-final.XXXXXX")"
-  final_pr_report_body "$repo" "$slug" "$status" "$pass" "$max" "$pr" "$branch" "$report" "$report_format" > "$body_file"
+  final_pr_report_body "$repo" "$slug" "$status" "$pass" "$max" "$pr" "$branch" > "$body_file"
   if ! out="$(cd "$repo" >/dev/null 2>&1 && gh pr comment "$pr" --body-file "$body_file" 2>&1)"; then
     PULL_REQUEST_ERROR="PR final report comment failed: $(gh_error_detail "$out")"
     rm -f "$body_file"
diff --git a/tests/devloop_test.sh b/tests/devloop_test.sh
index 85af3aa..b5afe14 100755
--- a/tests/devloop_test.sh
+++ b/tests/devloop_test.sh
@@ -603,12 +603,17 @@ case "${1:-}" in
           exit 1
         fi
         head=""
+        body_file=""
         while [ "$#" -gt 0 ]; do
           case "$1" in
             --head)
               shift
               head="${1:-}"
               ;;
+            --body-file)
+              shift
+              body_file="${1:-}"
+              ;;
           esac
           shift || true
         done
@@ -616,6 +621,9 @@ case "${1:-}" in
           printf 'head branch was not pushed before PR creation: %s\n' "$head" >&2
           exit 1
         fi
+        if [ -n "$body_file" ]; then
+          cp "$body_file" "$state/pr-body.md"
+        fi
         url="${DEVLOOP_GH_PR_URL:-https://github.com/satyaborg/devloop/pull/123}"
         printf '%s\n' "$url" > "$state/pr_url"
         printf '%s\n' "$url"
@@ -877,6 +885,14 @@ pr: null
 
 # $title
 
+## Problem
+
+The result file is never written during the loop.
+
+## Outcome
+
+The loop writes the result file on accept.
+
 ## Acceptance criteria
 1. Write the result file.
 MARKDOWN
@@ -1114,6 +1130,18 @@ contains "$pr_accept_output" "pr:" "PR accept loop"
 create_line="$(grep -n 'gh pr create' "$pr_log" | cut -d: -f1 | head -n 1)"
 review_line="$(grep -n 'agent reviewer 1' "$pr_log" | cut -d: -f1 | head -n 1)"
 [[ -n "$create_line" && -n "$review_line" && "$create_line" -lt "$review_line" ]] || fail "PR was not created before reviewer pass 1"
+contains "$(cat "$pr_log")" "--body-file" "PR create body flag"
+[[ -s "$pr_state/pr-body.md" ]] || fail "created PR body missing"
+pr_body="$(cat "$pr_state/pr-body.md")"
+contains "$pr_body" "E2E PR Accept" "created PR body"
+contains "$pr_body" "## Problem" "created PR body"
+contains "$pr_body" "The result file is never written during the loop." "created PR body"
+contains "$pr_body" "## Outcome" "created PR body"
+contains "$pr_body" "The loop writes the result file on accept." "created PR body"
+contains "$pr_body" "Latest commit" "created PR body"
+if ! printf '%s\n' "$pr_body" | grep -Eq 'Latest commit:[[:space:]]*[0-9a-f]{7,}'; then fail "created PR body missing commit hash"; fi
+contains "$pr_body" "Write the result file." "created PR body"
+if printf '%s\n' "$pr_body" | grep -q '/Users/'; then fail "created PR body leaked absolute local path"; fi
 equals "$(find "$pr_state/comments" -name 'round-*.md' | wc -l | tr -d ' ')" "1" "one round PR comment"
 equals "$(find "$pr_state/comments" -name 'final-*.md' | wc -l | tr -d ' ')" "1" "one final PR comment"
 round_body="$(cat "$pr_state/comments/round-1.md")"
@@ -1141,7 +1169,8 @@ contains "$final_body" "Residual Risk" "final PR comment"
 contains "$final_body" "PR URL" "final PR comment"
 contains "$final_body" "Branch" "final PR comment"
 contains "$final_body" "Commit References" "final PR comment"
-contains "$final_body" ".codex/reports/e2e-pr-accept.html" "final PR comment"
+if printf '%s\n' "$final_body" | grep -q '/Users/'; then fail "final PR comment leaked absolute local path"; fi
+if printf '%s\n' "$round_body" | grep -q 'Local cache'; then fail "round PR comment leaked local cache path"; fi
 if printf '%s\n' "$final_body" | grep -Eq '<(html|script|style)'; then fail "final PR comment embedded standalone HTML"; fi
 unset DEVLOOP_GH_STATE DEVLOOP_GH_LOG DEVLOOP_AGENT_LOG
 ok "PR-backed accept comments"
