diff --git a/internal/scaffold/fullsend-repo/scripts/post-code-test.sh b/internal/scaffold/fullsend-repo/scripts/post-code-test.sh
index f2121e0420..8fd6769abf 100644
--- a/internal/scaffold/fullsend-repo/scripts/post-code-test.sh
+++ b/internal/scaffold/fullsend-repo/scripts/post-code-test.sh
@@ -539,6 +539,138 @@ run_error_comment_test "error-comment-non-org-mode-fallback" \
"https://github.com/my-org/my-repo/actions/runs/67890" "yes" \
""
+# ---------------------------------------------------------------------------
+# Test helper — reimplements the pre-commit failure comment builder from
+# post-code.sh. Verifies the comment body distinguishes "agent wrote code,
+# PR blocked by pre-commit" from generic push/PR failure messages.
+# ---------------------------------------------------------------------------
+build_precommit_failure_comment() {
+ local branch="$1"
+ local repo_full_name="$2"
+ local run_id="$3"
+ local hook_output="$4"
+ local github_repository="${5:-}" # GITHUB_REPOSITORY override (org-mode)
+
+ local run_repo="${github_repository:-${repo_full_name}}"
+ local run_url="https://github.com/${run_repo}/actions/runs/${run_id}"
+ local hook_excerpt
+ hook_excerpt="$(echo "${hook_output}" | tail -n 30)"
+
+ echo "⚠️ **Agent wrote code but PR was blocked by pre-commit hooks**
+
+The code agent completed and committed changes on branch \`${branch}\`, \
+but the post-code authoritative pre-commit check failed. No PR was created.
+
+
+Hook output (last 30 lines)
+
+\`\`\`
+${hook_excerpt}
+\`\`\`
+
+
+
+**Workflow run:** ${run_url}
+
+**Next steps:**
+- Retry with \`/fs-code\` (the agent may fix the issues on the next attempt)
+- Or fix the pre-commit issues locally and push a PR manually"
+}
+
+run_precommit_comment_test() {
+ local test_name="$1"
+ local branch="$2"
+ local repo="$3"
+ local run_id="$4"
+ local hook_output="$5"
+ local check_pattern="$6"
+ local expect_present="$7"
+ local github_repository="${8:-}"
+
+ local actual
+ actual="$(build_precommit_failure_comment "${branch}" "${repo}" "${run_id}" "${hook_output}" "${github_repository}")"
+
+ 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}"
+}
+
+# --- Pre-commit failure comment test cases ---
+
+# Comment should mention pre-commit hooks (not generic push/PR failure)
+run_precommit_comment_test "precommit-comment-mentions-hooks" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "pre-commit hooks" "yes"
+
+# Comment should NOT use generic push/PR failure message
+run_precommit_comment_test "precommit-comment-not-generic-push-failure" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "pushing the branch or creating the PR" "no"
+
+# Comment should include branch name
+run_precommit_comment_test "precommit-comment-has-branch" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "agent/224-fix-slog" "yes"
+
+# Comment should include workflow run URL
+run_precommit_comment_test "precommit-comment-has-run-url" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "https://github.com/my-org/my-repo/actions/runs/12345" "yes"
+
+# Comment should include retry hint
+run_precommit_comment_test "precommit-comment-has-retry-hint" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "/fs-code" "yes"
+
+# Comment should include hook output excerpt
+run_precommit_comment_test "precommit-comment-has-hook-output" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "golangci-lint failed" "yes"
+
+# Comment should say "No PR was created" to distinguish from other failures
+run_precommit_comment_test "precommit-comment-says-no-pr" \
+ "agent/224-fix-slog" "my-org/my-repo" "12345" \
+ "golangci-lint failed" \
+ "No PR was created" "yes"
+
+# Org-mode: URL should use dispatch repo (GITHUB_REPOSITORY)
+run_precommit_comment_test "precommit-comment-org-mode-url" \
+ "agent/224-fix-slog" "test-org/my-app" "12345" \
+ "hook failed" \
+ "https://github.com/test-org/.fullsend/actions/runs/12345" "yes" \
+ "test-org/.fullsend"
+
+# Org-mode: URL should NOT use source repo
+run_precommit_comment_test "precommit-comment-org-mode-not-source" \
+ "agent/224-fix-slog" "test-org/my-app" "12345" \
+ "hook failed" \
+ "https://github.com/test-org/my-app/actions/runs/12345" "no" \
+ "test-org/.fullsend"
+
# --- Summary ---
echo ""
diff --git a/internal/scaffold/fullsend-repo/scripts/post-code.sh b/internal/scaffold/fullsend-repo/scripts/post-code.sh
index 1d7e8a4bc4..7e16e6acbd 100755
--- a/internal/scaffold/fullsend-repo/scripts/post-code.sh
+++ b/internal/scaffold/fullsend-repo/scripts/post-code.sh
@@ -206,14 +206,51 @@ if [ -f .pre-commit-config.yaml ]; then
if command -v pre-commit >/dev/null 2>&1; then
mapfile -t changed_array <<< "${CHANGED_FILES}"
- if pre-commit run --files "${changed_array[@]}"; then
+ PRECOMMIT_OUTPUT="$(mktemp)"
+ if pre-commit run --files "${changed_array[@]}" 2>&1 | tee "${PRECOMMIT_OUTPUT}"; then
echo "Pre-commit passed — all hooks clean"
else
echo "::error::BLOCKED — pre-commit hooks failed on agent's changes"
echo "::error::The agent's code does not pass the repo's pre-commit hooks."
echo "::error::Fix the issues and re-run, or update the pre-commit config."
+
+ # Post a failure comment on the source issue so the human gets
+ # actionable feedback without digging into workflow logs.
+ if [ -z "${GH_TOKEN:-}" ]; then
+ export GH_TOKEN="${PUSH_TOKEN}"
+ fi
+ local_run_url="${GITHUB_SERVER_URL:-https://github.com}/${GITHUB_REPOSITORY:-${REPO_FULL_NAME}}/actions/runs/${GITHUB_RUN_ID:-unknown}"
+ hook_excerpt="$(tail -n 30 "${PRECOMMIT_OUTPUT}")"
+ precommit_comment="⚠️ **Agent wrote code but PR was blocked by pre-commit hooks**
+
+The code agent completed and committed changes on branch \`${BRANCH}\`, \
+but the post-code authoritative pre-commit check failed. No PR was created.
+
+
+Hook output (last 30 lines)
+
+\`\`\`
+${hook_excerpt}
+\`\`\`
+
+
+
+**Workflow run:** ${local_run_url}
+
+**Next steps:**
+- Retry with \`/fs-code\` (the agent may fix the issues on the next attempt)
+- Or fix the pre-commit issues locally and push a PR manually"
+
+ echo "::warning::Posting pre-commit failure comment to issue #${ISSUE_NUMBER}..."
+ gh issue comment "${ISSUE_NUMBER}" \
+ --repo "${REPO_FULL_NAME}" \
+ --body "${precommit_comment}" 2>/dev/null || \
+ echo "::warning::Failed to post pre-commit failure comment to issue #${ISSUE_NUMBER}"
+
+ rm -f "${PRECOMMIT_OUTPUT}"
exit 1
fi
+ rm -f "${PRECOMMIT_OUTPUT}"
else
echo "::warning::pre-commit not available on runner — skipping authoritative check"
echo "::warning::CI pre-commit will still run on the PR"