fix: escalation notifications should @mention human on Matrix (#624)

Add MATRIX_MENTION_USER config to project TOML and include a Matrix
mention pill in escalation notify_ctx calls so humans get notified
even in muted rooms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-24 19:21:46 +00:00
parent e58e60fe7c
commit 32c336f3df
4 changed files with 16 additions and 8 deletions

View file

@ -410,9 +410,11 @@ Write PHASE:awaiting_review to the phase file, then stop and wait for review fee
_ci_pipeline_url="${WOODPECKER_SERVER}/repos/${WOODPECKER_REPO_ID}/pipeline/${PIPELINE_NUM:-0}" _ci_pipeline_url="${WOODPECKER_SERVER}/repos/${WOODPECKER_REPO_ID}/pipeline/${PIPELINE_NUM:-0}"
if [ "$CI_FIX_COUNT" -gt "$MAX_CI_FIXES" ]; then if [ "$CI_FIX_COUNT" -gt "$MAX_CI_FIXES" ]; then
log "CI failure not recoverable after ${CI_FIX_COUNT} fix attempts — escalating" log "CI failure not recoverable after ${CI_FIX_COUNT} fix attempts — escalating"
local _mention_html=""
[ -n "${MATRIX_MENTION_USER:-}" ] && _mention_html="<a href='https://matrix.to/#/${MATRIX_MENTION_USER}'>${MATRIX_MENTION_USER}</a> "
notify_ctx \ notify_ctx \
"CI exhausted after ${CI_FIX_COUNT} attempts — escalating for human help" \ "CI exhausted after ${CI_FIX_COUNT} attempts — escalating for human help" \
"CI exhausted after ${CI_FIX_COUNT} attempts on PR <a href='${PR_URL:-${FORGE_WEB}/pulls/${PR_NUMBER}}'>#${PR_NUMBER}</a> | <a href='${_ci_pipeline_url}'>Pipeline</a><br>Step: <code>${FAILED_STEP:-unknown}</code> — escalating for human help" "${_mention_html}CI exhausted after ${CI_FIX_COUNT} attempts on PR <a href='${PR_URL:-${FORGE_WEB}/pulls/${PR_NUMBER}}'>#${PR_NUMBER}</a> | <a href='${_ci_pipeline_url}'>Pipeline</a><br>Step: <code>${FAILED_STEP:-unknown}</code> — escalating for human help"
printf 'PHASE:escalate\nReason: ci_exhausted after %d attempts (step: %s)\n' "$CI_FIX_COUNT" "${FAILED_STEP:-unknown}" > "$PHASE_FILE" printf 'PHASE:escalate\nReason: ci_exhausted after %d attempts (step: %s)\n' "$CI_FIX_COUNT" "${FAILED_STEP:-unknown}" > "$PHASE_FILE"
# Do NOT update LAST_PHASE_MTIME here — let the main loop detect PHASE:escalate # Do NOT update LAST_PHASE_MTIME here — let the main loop detect PHASE:escalate
return 0 return 0
@ -646,9 +648,11 @@ Instructions:
_issue_url="${FORGE_WEB}/issues/${ISSUE}" _issue_url="${FORGE_WEB}/issues/${ISSUE}"
_pr_link="" _pr_link=""
[ -n "${PR_NUMBER:-}" ] && _pr_link=" | PR <a href='${FORGE_WEB}/pulls/${PR_NUMBER}'>#${PR_NUMBER}</a>" [ -n "${PR_NUMBER:-}" ] && _pr_link=" | PR <a href='${FORGE_WEB}/pulls/${PR_NUMBER}'>#${PR_NUMBER}</a>"
local _mention_html=""
[ -n "${MATRIX_MENTION_USER:-}" ] && _mention_html="<a href='https://matrix.to/#/${MATRIX_MENTION_USER}'>${MATRIX_MENTION_USER}</a> "
notify_ctx \ notify_ctx \
"⚠️ Issue #${ISSUE} (PR #${PR_NUMBER:-none}) escalated — needs human input.${ESCALATE_REASON:+ Reason: ${ESCALATE_REASON}}" \ "⚠️ Issue #${ISSUE} (PR #${PR_NUMBER:-none}) escalated — needs human input.${ESCALATE_REASON:+ Reason: ${ESCALATE_REASON}}" \
"⚠️ <a href='${_issue_url}'>Issue #${ISSUE}</a>${_pr_link} escalated — needs human input.${ESCALATE_REASON:+ Reason: ${ESCALATE_REASON}}<br>Reply in this thread to send guidance to the agent." "${_mention_html}⚠️ <a href='${_issue_url}'>Issue #${ISSUE}</a>${_pr_link} escalated — needs human input.${ESCALATE_REASON:+ Reason: ${ESCALATE_REASON}}<br>Reply in this thread to send guidance to the agent."
log "phase: escalate — notified via Matrix, session stays alive waiting for reply" log "phase: escalate — notified via Matrix, session stays alive waiting for reply"
# Session stays alive — matrix_listener injects human reply directly # Session stays alive — matrix_listener injects human reply directly

View file

@ -66,6 +66,8 @@ if 'bot_user' in mx:
emit('MATRIX_BOT_USER', mx['bot_user']) emit('MATRIX_BOT_USER', mx['bot_user'])
if 'token_env' in mx: if 'token_env' in mx:
emit('MATRIX_TOKEN_ENV', mx['token_env']) emit('MATRIX_TOKEN_ENV', mx['token_env'])
if 'mention_user' in mx:
emit('MATRIX_MENTION_USER', mx['mention_user'])
# [monitoring] section # [monitoring] section
mon = cfg.get('monitoring', {}) mon = cfg.get('monitoring', {})

View file

@ -17,9 +17,10 @@ stale_minutes = 60
containers = [] containers = []
[matrix] [matrix]
room_id = "!your_room_id:matrix.example.org" room_id = "!your_room_id:matrix.example.org"
bot_user = "@disinto-factory:matrix.example.org" bot_user = "@disinto-factory:matrix.example.org"
token_env = "DISINTO_MATRIX_TOKEN" token_env = "DISINTO_MATRIX_TOKEN"
mention_user = "@johba:matrix.allf.in" # Matrix user to @mention on escalations
[monitoring] [monitoring]
check_prs = true check_prs = true

View file

@ -17,9 +17,10 @@ stale_minutes = 60
containers = ["ponder"] containers = ["ponder"]
[matrix] [matrix]
room_id = "!your_room_id:matrix.example.org" room_id = "!your_room_id:matrix.example.org"
bot_user = "@harb-factory:matrix.example.org" bot_user = "@harb-factory:matrix.example.org"
token_env = "MATRIX_TOKEN" token_env = "MATRIX_TOKEN"
mention_user = "@johba:matrix.allf.in" # Matrix user to @mention on escalations
[monitoring] [monitoring]
check_prs = true check_prs = true