agents: retro loop — "@pm retro" distills learnings into LEARNINGS.md
ci / lint (push) Skipped
ci / lint (pull_request) Successful in 11s
ci / lint (push) Skipped
ci / lint (pull_request) Successful in 11s
New learning step: ask @pm for a retrospective on any issue and the system turns what happened into prompt-visible rules for future runs. - run-agent.sh: @pm gains a RETRO marker (emit only when the maintainer asks); LEARNINGS.md (caller repo root, capped at 4KB) is injected into EVERY agent's prompt as "TEAM LEARNINGS" — the feedback loop that makes delegation more robust over time. - publish.sh: on @pm's RETRO marker, open a "retro: issue #N" issue pointing at the issue + its PR (state=all resolve, works after merge) and trigger @senior on it (has gitea-api to read both threads). The retro produces a LEARNINGS.md PR through the NORMAL choreography (senior → pm → qa), so retros are reviewed like any change. Strip the RETRO marker from visible replies. - publish.sh: bounce counter now counts only @qa-authored comments matching the exact trigger template — on PR #84 it jumped 1/3 → 3/3 because a qa review QUOTED our own "(fix attempt …)" template from the diff, halving the fix budget. Template + regex pinned together with a sync note. - README: document the retro loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
950c639b0a
commit
b40f547740
@@ -60,8 +60,8 @@ del_autopilot_label() {
|
||||
fi
|
||||
}
|
||||
|
||||
# drop machine-readable markers: DELEGATE / CLOSE_ISSUE / MERGE_PR / APPROVE / HALT / BOUNCE, and the
|
||||
# BEGIN_SUBTASKS..END_SUBTASKS and BEGIN_PR_DESCRIPTION..END_PR_DESCRIPTION blocks (the PR
|
||||
# drop machine-readable markers: DELEGATE / CLOSE_ISSUE / MERGE_PR / RETRO / APPROVE / HALT / BOUNCE,
|
||||
# and the BEGIN_SUBTASKS..END_SUBTASKS and BEGIN_PR_DESCRIPTION..END_PR_DESCRIPTION blocks (the PR
|
||||
# description is published separately).
|
||||
reply=$(awk '
|
||||
/^[[:space:]]*BEGIN_SUBTASKS/{s=1}
|
||||
@@ -69,6 +69,7 @@ reply=$(awk '
|
||||
/^[[:space:]]*DELEGATE:[[:space:]]*@/{next}
|
||||
/^[[:space:]]*CLOSE_ISSUE[[:space:]]*$/{next}
|
||||
/^[[:space:]]*MERGE_PR[[:space:]]*$/{next}
|
||||
/^[[:space:]]*RETRO[[:space:]]*$/{next}
|
||||
/^[[:space:]]*APPROVE[[:space:]]*$/{next}
|
||||
/^[[:space:]]*HALT([_ ]AUTOPILOT)?[[:space:]]*$/{next}
|
||||
/^[[:space:]]*BOUNCE:[[:space:]]*@/{next}
|
||||
@@ -113,13 +114,18 @@ if [ "$MODE" != "pr" ]; then
|
||||
[ -z "$dev" ] && [ -n "$PRN" ] && dev=$(curl -sS "${hdr[@]}" "$API/pulls/$PRN" | jq -r '.user.login // "junior"')
|
||||
dest="${PRN:-$NUM}"
|
||||
post_to "$dest" "$reply$activity" # recommendations, on the PR
|
||||
# Bounce budget: count prior "fix attempt" markers on the PR thread; stop after 3.
|
||||
prior=$(curl -sS "${hdr[@]}" "$API/issues/$dest/comments?limit=100" | jq -r 'if type=="array" then [.[]|select(.body|test("fix attempt"))]|length else 0 end' 2>/dev/null); prior=${prior:-0}
|
||||
# Bounce budget: count prior bounce TRIGGERS on the PR thread — only @qa-authored comments
|
||||
# matching the exact "(fix attempt N/3)" template. A loose substring match would also count
|
||||
# review text QUOTING our own templates (seen on PR #84: the counter jumped 1/3 → 3/3 because
|
||||
# a qa review quoted publish.sh lines containing the phrase), halving the fix budget.
|
||||
prior=$(curl -sS "${hdr[@]}" "$API/issues/$dest/comments?limit=100" | jq -r 'if type=="array" then [.[]|select(.user.login=="qa")|select(.body|test("^@[a-z]+ please address my review above and update PR #[0-9?]+ \\(fix attempt [0-9]+/3\\)\\.$"))]|length else 0 end' 2>/dev/null); prior=${prior:-0}
|
||||
if [ "$prior" -ge 3 ]; then
|
||||
[ "$AUTOPILOT" = "true" ] && del_autopilot_label "$ISSN"
|
||||
post_to "$ISSN" "🛑 Still not right after 3 fix attempts on PR #${PRN:-?} — handing to @ffaerber (details on the PR)."
|
||||
else
|
||||
n=$((prior + 1))
|
||||
# NOTE: this template and the counter regex above MUST stay in sync — if you reword one,
|
||||
# reword the other, or the count resets to 0 and the 3-round cap stops working.
|
||||
trig "$dest" "@${dev:-junior} please address my review above and update PR #${PRN:-?} (fix attempt $n/3)."
|
||||
fi
|
||||
elif grep -qiE '^[[:space:]]*HALT([_ ]AUTOPILOT)?[[:space:]]*$' /tmp/agent_out.md; then
|
||||
@@ -203,6 +209,27 @@ if [ "$MODE" != "pr" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- @pm RETRO: open a retrospective issue for this thread (maintainer asked for a retro) ---
|
||||
# Creates a retro issue pointing at this issue + its PR and triggers @senior on it (has gitea-api
|
||||
# to read both threads). The retro produces a LEARNINGS.md PR via the NORMAL choreography (senior →
|
||||
# pm → qa → merge), and run-agent.sh injects LEARNINGS.md into every future prompt — closing the loop.
|
||||
if [ "$NAME" = "pm" ] && grep -qiE '^[[:space:]]*RETRO[[:space:]]*$' /tmp/agent_out.md; then
|
||||
PRN=$(curl -sS "${hdr[@]}" "$API/pulls?state=all&limit=50" \
|
||||
| jq -r --arg br "ai/issue-$ISSN" 'if type=="array" then ([.[]|select(.head.ref==$br)] | sort_by(.number) | last | .number // empty) else empty end' 2>/dev/null)
|
||||
rbody=$(printf 'Retrospective for issue #%s%s.\n\nRead the FULL issue thread%s using the gitea-api skill (issue comments%s and the PR diff). Identify what went wrong, slow, or needed human correction — missed wiring, review misses, bounced rounds, unclear delegation, missing context.\n\nThen APPEND the distilled learnings to `LEARNINGS.md` at the repo root (create it with a short header if missing). Rules for entries:\n- 3 to 6 bullets max, each ONE line: `symptom -> rule for next time`.\n- Concrete and checkable (name the file/step/marker), not generic advice.\n- Do not repeat an existing bullet; refine it instead.\n- Do not rewrite unrelated parts of the file.\n\nThese learnings are injected into every future agent prompt, so quality over quantity.' \
|
||||
"$ISSN" "${PRN:+ / PR #$PRN}" "${PRN:+ and PR #$PRN thread}" "${PRN:+, PR comments}")
|
||||
rnum=$(curl -sS -X POST "${hdr[@]}" "$API/issues" \
|
||||
-d "$(jq -nc --arg t "retro: issue #$ISSN" --arg b "$rbody" '{title:$t,body:$b}')" | jq -r '.number // empty')
|
||||
if [ -n "$rnum" ]; then
|
||||
echo "opened retro issue #$rnum"
|
||||
post_to "$ISSN" "📝 Opened retro issue #$rnum."
|
||||
trig "$rnum" "@senior please run this retrospective per the issue body."
|
||||
else
|
||||
echo "retro issue creation failed"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- @pm delegation: hand the build to a dev, or hand the finished PR to @qa for review ---
|
||||
# Only an explicit 'DELEGATE: @<agent>' line acts (never a prose mention). Fires via the PAT (TTOK)
|
||||
# so a new run starts; the built-in token cannot. Everything posts on the ISSUE — @pm never touches
|
||||
|
||||
@@ -55,6 +55,10 @@ if [ "$MODE" = "comment" ]; then
|
||||
If anything is unclear or needs a decision at any phase, START your reply with '@ffaerber', ask
|
||||
specific questions, and do NOT emit a marker. Mentioning a teammate in prose does NOT act — only a
|
||||
DELEGATE line does.
|
||||
RETRO — when the maintainer asks for a retrospective on this issue (e.g. 'run a retro',
|
||||
'@pm retro'), briefly acknowledge and end your reply with EXACTLY one line: 'RETRO'. The
|
||||
automation opens a retro issue (read this issue + its PR, distill learnings into LEARNINGS.md)
|
||||
and assigns it. Emit RETRO only when explicitly asked.
|
||||
BREAKDOWN (a feature too big for one PR): in PHASE 1, propose a milestone name and the sub-task
|
||||
list, then ask '@ffaerber create these N sub-issues? reply yes.' ONLY after approval, end with:
|
||||
BEGIN_SUBTASKS
|
||||
@@ -102,10 +106,21 @@ else
|
||||
open pull requests yourself — that is automated for every branch you push.
|
||||
If the task is genuinely unclear, make NO changes and reply with specific questions instead."
|
||||
fi
|
||||
# TEAM LEARNINGS — distilled from past retros (see the RETRO flow in publish.sh). Lives at the
|
||||
# CALLER repo root as LEARNINGS.md, maintained by retro PRs. Injected into EVERY agent's prompt
|
||||
# (capped) so past mistakes actually change future behavior — this is the feedback loop.
|
||||
LEARN=""
|
||||
[ -s LEARNINGS.md ] && LEARN=$(head -c 4000 LEARNINGS.md)
|
||||
[ -n "$LEARN" ] && LEARN="
|
||||
TEAM LEARNINGS (distilled from past retros in this repo — APPLY them; they exist because a
|
||||
previous task went wrong without them):
|
||||
${LEARN}
|
||||
"
|
||||
PROMPT="You are @${NAME}, a member of an AI dev team working on this Gitea repository.
|
||||
YOUR ROLE: ${DESC}
|
||||
YOUR CAPABILITIES: model ${MODEL}. ${CAP}
|
||||
${NOTE}
|
||||
${LEARN}
|
||||
|
||||
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'
|
||||
|
||||
Reference in New Issue
Block a user