From 4c05abac63b5ba27827c3fa219197627ec3ea3a9 Mon Sep 17 00:00:00 2001 From: Felix Faerber Date: Sun, 5 Jul 2026 18:00:58 +0300 Subject: [PATCH] agents: drop redundant agent-name headers from comments (+ fix self-trigger loop) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea already attributes every comment/PR to its author, so the "๐Ÿค– **@name**" / "๐Ÿ”จ **@name**" header at the top of agent comments was redundant noise. Remove it everywhere and, in the process, close a self-trigger loop. - publish.sh: post()/prpost() now append a hidden `` marker instead of each message carrying a visible "๐Ÿค– **@name**" header. The gate keys on the '๐Ÿค–' char to skip agent replies, so the marker preserves loop-prevention while being invisible. All reply/status/autopilot strings drop the name header. - route.sh: the "building on branch" notice is posted with the agent's PAT and previously had NO ๐Ÿค– marker + an "@name" mention, so it re-triggered the agent (the observed loop on issue #139). Reword without the self-name and add the hidden marker so it can't fire a new run. - publish.sh: broaden the leading self-header stripper to drop any leading line referencing the agent's own @handle (e.g. "## ๐Ÿ”จ @senior โ€” โ€ฆ"), not just bare "**@name**" lines. Legit headings like "## Summary" are preserved. - run-agent.sh: tell the model not to begin its reply with its own name/handle. - Trigger comments (delegation / autopilot / bounce) stay marker-free by design, so they still fire the next agent. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/scripts/publish.sh | 44 ++++++++++++++++----------- .gitea/workflows/scripts/route.sh | 4 ++- .gitea/workflows/scripts/run-agent.sh | 4 +++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/scripts/publish.sh b/.gitea/workflows/scripts/publish.sh index bb568e9..89bf1bb 100755 --- a/.gitea/workflows/scripts/publish.sh +++ b/.gitea/workflows/scripts/publish.sh @@ -23,8 +23,15 @@ git config user.name "$NAME" git config user.email "$NAME@ffaerber.duckdns.org" API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" hdr=(-H "Authorization: token $TOK" -H "Content-Type: application/json") +# Hidden loop-prevention marker appended to every agent REPLY/STATUS comment. Gitea already shows +# who authored a comment, so we don't repeat the agent's name in the body; but the trigger gate keys +# on the '๐Ÿค–' character to know "this is an agent's own comment, don't fire a new run". An HTML +# comment renders as nothing, so the marker is invisible while still tripping the gate's guard. +# NOTE: trigger comments (delegation / autopilot / bounce) are posted with inline curl, NOT post()/ +# prpost(), so they never get this marker and therefore DO fire the next run โ€” that is intended. +MARK=$'\n\n' post() { curl -sS -w 'comment -> HTTP %{http_code}\n' -X POST "${hdr[@]}" \ - "$API/issues/$NUM/comments" -d "$(jq -nc --arg b "$1" '{body:$b}')"; } + "$API/issues/$NUM/comments" -d "$(jq -nc --arg b "$1$MARK" '{body:$b}')"; } # Remove the 'autopilot' label from an issue by resolving its ID first (Gitea's DELETE label # endpoint is by ID, not name). Arg $1 = issue number. Used as the autopilot kill switch. del_autopilot_label() { @@ -54,12 +61,15 @@ reply=$(awk ' p{ if(/^[[:space:]]*END_PR_DESCRIPTION/){p=0}; next } {print} ' /tmp/agent_out.md 2>/dev/null) -# Strip a leading self-header the model sometimes emits ("๐Ÿค– **@pm**" on its own line) so we don't -# double it when we prepend our own. Removes a leading run of such header lines and blank lines. -reply=$(printf '%s' "$reply" | awk ' +# Strip a leading self-identification header the model sometimes emits, e.g. "๐Ÿค– **@pm**", +# "๐Ÿ”จ **@senior**", or a heading like "## ๐Ÿ”จ @senior โ€” ". Gitea already attributes the comment +# to its author, so we drop any leading line that references the agent's OWN @handle โ€” or a bare +# "**@name**" line โ€” together with surrounding blank lines, up to the first real content line. +reply=$(printf '%s' "$reply" | awk -v me="@$NAME" ' BEGIN{s=1} - s && /^[^A-Za-z0-9]*\*\*@[A-Za-z]+\*\*[[:space:]]*$/ {next} s && /^[[:space:]]*$/ {next} + s && index($0, me) {next} + s && /^[^A-Za-z0-9]*\*\*@[A-Za-z]+\*\*[[:space:]]*$/ {next} {s=0; print} ') [ -z "$reply" ] && reply="_(Made changes without a text summary โ€” see the diff below.)_" @@ -113,9 +123,9 @@ if [ "$MODE" != "pr" ]; then echo "created sub-issue #${n:-?}: $title" [ -n "$n" ] && links="$links\n- #$n โ€” $title" done < /tmp/subtasks.txt - subtext=$(printf '\n\n---\n๐Ÿค– **@%s** โ€” created sub-issues%s (mention an agent on each when ready):%b' "$NAME" "${ms:+ under milestone **$ms**}" "$links") + subtext=$(printf '\n\n---\nCreated sub-issues%s (mention an agent on each when ready):%b' "${ms:+ under milestone **$ms**}" "$links") fi - post "$(printf '๐Ÿค– **@%s**\n\n%s%s' "$NAME" "$msg" "$subtext")" + post "$(printf '%s%s' "$msg" "$subtext")" # --- AUTOPILOT: @qa's narrow, label-gated merge / halt authority --- # Only @qa, only when 'autopilot' is set, and only on a PR thread. The MERGE_PR / HALT_AUTOPILOT @@ -141,13 +151,13 @@ if [ "$MODE" != "pr" ]; then echo "closing origin issue #${ISSNUM:-$NUM}" curl -sS -X PATCH "${hdr[@]}" "$API/issues/${ISSNUM:-$NUM}" \ -d '{"state":"closed"}' -w '\nclose -> HTTP %{http_code}\n' || true - post "$(printf '๐Ÿค– **@qa** โ€” โœ… verified & merged PR #%s (autopilot). Closed issue #%s.' "$NUM" "${ISSNUM:-$NUM}")" + post "$(printf 'โœ… Verified & merged PR #%s (autopilot). Closed issue #%s.' "$NUM" "${ISSNUM:-$NUM}")" ;; *) # Merge failed (checks not green, conflicts, or TOKEN_QA lacks merge scope) โ€” do NOT # silently proceed: drop the label so it reverts to human control and report. del_autopilot_label "${ISSNUM:-$NUM}" - post "$(printf '๐Ÿค– **@qa** โ€” โš ๏ธ tried to merge PR #%s but the API returned HTTP %s (checks not green, a conflict, or missing merge permission on TOKEN_QA). Removed the `autopilot` label โ€” @ffaerber please take a look.' "$NUM" "$mc")" + post "$(printf 'โš ๏ธ Tried to merge PR #%s but the API returned HTTP %s (checks not green, a conflict, or missing merge permission on TOKEN_QA). Removed the `autopilot` label โ€” @ffaerber please take a look.' "$NUM" "$mc")" ;; esac fi @@ -167,7 +177,7 @@ if [ "$MODE" != "pr" ]; then if [ "$prior" -ge 3 ]; then echo "@qa autopilot: 3 bounces already โ€” halting" del_autopilot_label "${ISSNUM:-$NUM}" - post "$(printf '๐Ÿค– **@qa** โ€” ๐Ÿ›‘ still not right after 3 fix attempts. Stopping autopilot (removed the `autopilot` label). @ffaerber please take over โ€” details in the comments above.')" + post "$(printf '๐Ÿ›‘ Still not right after 3 fix attempts. Stopping autopilot (removed the `autopilot` label). @ffaerber please take over โ€” details in the comments above.')" else n=$((prior + 1)) echo "@qa autopilot: bounce $n/3 -> @$target" @@ -180,7 +190,7 @@ if [ "$MODE" != "pr" ]; then elif grep -qiE '^[[:space:]]*HALT_AUTOPILOT[[:space:]]*$' /tmp/agent_out.md; then echo "@qa autopilot: HALT โ€” removing 'autopilot' label from #${ISSNUM:-$NUM}" del_autopilot_label "${ISSNUM:-$NUM}" - post "$(printf '๐Ÿค– **@qa** โ€” ๐Ÿ›‘ this needs a human decision (not a dev fix). Removed the `autopilot` label (back to human control). @ffaerber please decide next steps (details above).')" + post "$(printf '๐Ÿ›‘ This needs a human decision (not a dev fix). Removed the `autopilot` label (back to human control). @ffaerber please decide next steps (details above).')" fi exit 0 fi @@ -228,7 +238,7 @@ fi git push origin "HEAD:$BRANCH" || true git fetch -q origin 2>/dev/null || true -prbody=$(printf '%s\n\n---\nResolves #%s ยท ๐Ÿค– @%s' "$prdesc" "$NUM" "$NAME") +prbody=$(printf '%s\n\n---\nResolves #%s' "$prdesc" "$NUM") owner=${GITHUB_REPOSITORY%%/*} # Post the agent's activity trail (tool calls + reasoning) inline in the same comment so @@ -248,7 +258,7 @@ br="$BRANCH" ahead=$(git rev-list --count "origin/main..origin/$br" 2>/dev/null || echo 0) if [ "${ahead:-0}" -eq 0 ]; then # No changes on this branch โ€” a plan / questions / analysis only. - post "$(printf '๐Ÿค– **@%s**\n\n%s%s' "$NAME" "$reply" "$activity")" + post "$(printf '%s%s' "$reply" "$activity")" exit 0 fi @@ -266,7 +276,7 @@ if [ -z "$url" ]; then url=$(printf '%s' "$resp" | jq -r '.html_url // empty' 2>/dev/null) prnum=$(printf '%s' "$resp" | jq -r '.number // empty' 2>/dev/null) fi -[ -z "$url" ] && { echo "PR open/lookup failed for $br โ€” posting reply on issue instead"; post "$(printf '๐Ÿค– **@%s**\n\n%s%s' "$NAME" "$reply" "$activity")"; exit 0; } +[ -z "$url" ] && { echo "PR open/lookup failed for $br โ€” posting reply on issue instead"; post "$(printf '%s%s' "$reply" "$activity")"; exit 0; } # Posts to the PR thread when we have a PR number, else to the origin issue ($NUM). prpost() { @@ -274,11 +284,11 @@ prpost() { [ -n "$n" ] && [ "$n" != "$NUM" ] && t="$n" echo "posting to #$t" curl -sS -w 'comment -> HTTP %{http_code}\n' -X POST "${hdr[@]}" \ - "$API/issues/$t/comments" -d "$(jq -nc --arg b "$1" '{body:$b}')" + "$API/issues/$t/comments" -d "$(jq -nc --arg b "$1$MARK" '{body:$b}')" } if [ "$NEW" = "true" ]; then - prpost "$prnum" "$(printf '๐Ÿค– **@%s** โ€” โœ… PR ready for review โ€” @ffaerber please review & merge:\n- %s%s' "$NAME" "$url" "$activity")" + prpost "$prnum" "$(printf 'โœ… PR ready for review โ€” @ffaerber please review & merge:\n- %s%s' "$url" "$activity")" # AUTOPILOT: hand the fresh PR to @qa automatically (via TOK, so it fires a new run). # @qa then verifies and โ€” if green โ€” merges + closes via its MERGE_PR marker. The comment lands # on the PR thread ($prnum) so the next run resolves the origin issue's label from the branch @@ -293,7 +303,7 @@ if [ "$NEW" = "true" ]; then else # Resume: just link the PR โ€” its body and the diff already carry the description, so we don't # repeat the full write-up in the comment (the reasoning trail below shows what this run did). - prpost "$prnum" "$(printf '๐Ÿค– **@%s** โ€” pushed an update to the PR:\n- %s%s' "$NAME" "$url" "$activity")" + prpost "$prnum" "$(printf 'Pushed an update to the PR:\n- %s%s' "$url" "$activity")" # AUTOPILOT: after a dev pushes a fix (e.g. following a @qa bounce), hand back to @qa to re-verify. if [ "$AUTOPILOT" = "true" ] && [ -n "$TTOK" ] && [ -n "$prnum" ]; then case "$NAME" in diff --git a/.gitea/workflows/scripts/route.sh b/.gitea/workflows/scripts/route.sh index c595cea..8d1fe15 100755 --- a/.gitea/workflows/scripts/route.sh +++ b/.gitea/workflows/scripts/route.sh @@ -71,7 +71,9 @@ else # comment on an issue, no branch ye git push -u origin "HEAD:ai/issue-$NUM" || true url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/ai/issue-$NUM" curl -sS -X POST "${hdr[@]}" "$API/issues/$NUM/comments" \ - -d "$(jq -nc --arg b "๐Ÿ”จ **@$name** is on it โ€” building on branch [\`ai/issue-$NUM\`]($url). I'll open a PR when it's ready." '{body:$b}')" >/dev/null || true + -d "$(jq -nc --arg b "๐Ÿ”จ Building on branch [\`ai/issue-$NUM\`]($url) โ€” I'll open a PR when it's ready. + +<!-- ๐Ÿค– agent reply โ€” do not trigger -->" '{body:$b}')" >/dev/null || true fi fi diff --git a/.gitea/workflows/scripts/run-agent.sh b/.gitea/workflows/scripts/run-agent.sh index 7abfc45..d7895b9 100755 --- a/.gitea/workflows/scripts/run-agent.sh +++ b/.gitea/workflows/scripts/run-agent.sh @@ -102,6 +102,10 @@ PROMPT="You are @${NAME}, a member of an AI dev team working on this Gitea repos YOUR CAPABILITIES: model ${MODEL}. ${CAP} ${NOTE} + Your reply is posted as a comment already attributed to you (@${NAME}) โ€” your name and avatar are + shown by Gitea. Do NOT begin your reply with your own name, an '@${NAME}' header, or a '๐Ÿค–/๐Ÿ”จ @you' + line; just write the content directly. + TEAM ROSTER (who does what โ€” hand off if a task isn't yours): ${ROSTER} -- 2.54.0