diff --git a/.github/workflows/tend-ci-fix.yaml b/.github/workflows/tend-ci-fix.yaml index be674c5769ed..91a0572384b4 100644 --- a/.github/workflows/tend-ci-fix.yaml +++ b/.github/workflows/tend-ci-fix.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -32,7 +32,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-mention.yaml b/.github/workflows/tend-mention.yaml index 49d5620cae13..8b26a0ca0236 100644 --- a/.github/workflows/tend-mention.yaml +++ b/.github/workflows/tend-mention.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -253,7 +253,7 @@ jobs: env: EVENT_TS: ${{ github.event.comment.updated_at || github.event.review.submitted_at || github.event.issue.updated_at }} - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-nightly.yaml b/.github/workflows/tend-nightly.yaml index d08755dc058a..80c6ff5db3ff 100644 --- a/.github/workflows/tend-nightly.yaml +++ b/.github/workflows/tend-nightly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -32,7 +32,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-notifications.yaml b/.github/workflows/tend-notifications.yaml index efd9b28b7780..e3190aaf5d15 100644 --- a/.github/workflows/tend-notifications.yaml +++ b/.github/workflows/tend-notifications.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -26,7 +26,39 @@ jobs: - name: Check for unread notifications id: check run: | - COUNT=$(gh api notifications --jq 'length') + # Fetch notifications once, tolerating transient non-JSON responses. + # GitHub occasionally returns an HTML error page (even with a 200) + # during a brief API blip; under `bash -e` an untolerated `gh api` + # failure would abort the whole step red. A failed fetch just means + # this cycle can't enumerate — the next scheduled cycle picks up + # anything missed — so retry briefly, then skip cleanly. + # + # On every failed attempt, log what actually came back: the plain + # fetch hides the body behind `2>/dev/null`, and a transient blip + # that recovers on retry would otherwise leave no trace of the bad + # response. `gh api -i` re-fetches with the HTTP status line + + # headers ahead of the body, so a recurring failure shows the real + # status code and body — not just jq's parse complaint. It exits + # non-zero on an error status, hence `|| true`. The extra call runs + # only on the (rare) failing attempt; the happy path is one fetch. + NOTIFS="" + for attempt in 1 2 3; do + if NOTIFS=$(gh api notifications 2>/dev/null) && echo "$NOTIFS" | jq -e . >/dev/null 2>&1; then + break + fi + NOTIFS="" + echo "--- notifications fetch attempt $attempt failed; actual response (status + body head) ---" + gh api notifications -i 2>&1 | head -c 1000 || true + echo + [ "$attempt" -lt 3 ] && sleep "$attempt" + done + if [ -z "$NOTIFS" ]; then + echo "count=0" >> "$GITHUB_OUTPUT" + echo "notifications fetch failed after retries — skipping this cycle" + exit 0 + fi + + COUNT=$(echo "$NOTIFS" | jq 'length') if [ "$COUNT" = "0" ]; then echo "count=0" >> "$GITHUB_OUTPUT" echo "No unread notifications — skipping" @@ -42,7 +74,6 @@ jobs: RECENT_PRS=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs?created=>=$SINCE&per_page=50" --jq '[.workflow_runs[] | select(.name | test("^(tend-review|tend-mention|tend-triage|tend-ci-fix)$")) | .pull_requests[]?.number] | unique | .[]' || true) if [ -n "$RECENT_PRS" ]; then - NOTIFS=$(gh api notifications) for pr in $RECENT_PRS; do echo "$NOTIFS" | jq -r --arg repo "$GITHUB_REPOSITORY" --arg pr "$pr" '.[] | select(.subject.url == "https://api.github.com/repos/" + $repo + "/pulls/" + $pr or .subject.url == "https://api.github.com/repos/" + $repo + "/issues/" + $pr) | .id' | while read -r tid; do [ -n "$tid" ] || continue @@ -54,7 +85,8 @@ jobs: # --- Layer C: drop notifications on bot-authored closed PRs --- # The bot auto-subscribes to its own PRs. After merge/close, leftover # subscription notifications are pure noise — no action needed. - NOTIFS=$(gh api notifications) + # Reuses the snapshot fetched above; marking a thread read is + # idempotent, so a stale snapshot at worst re-PATCHes a read thread. echo "$NOTIFS" | jq -r --arg repo "$GITHUB_REPOSITORY" '.[] | select(.repository.full_name == $repo and .subject.type == "PullRequest") | .id' | while read -r tid; do [ -n "$tid" ] || continue PR_NUM=$(echo "$NOTIFS" | jq -r --arg tid "$tid" '.[] | select(.id == $tid) | .subject.url | split("/") | last') @@ -73,7 +105,12 @@ jobs: # Processing them now risks duplicating work. Cross-repo notifications # are exempt — no dedicated workflow handles them. CUTOFF=$(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ) - REMAINING=$(gh api notifications) + # Re-fetch so the count reflects threads marked read in Layers B/C. + # Tolerate a transient failure by falling back to the pre-mutation + # snapshot — an over-count at worst spends one agent run, never fails. + if ! REMAINING=$(gh api notifications 2>/dev/null) || ! echo "$REMAINING" | jq -e . >/dev/null 2>&1; then + REMAINING="$NOTIFS" + fi COUNT=$(echo "$REMAINING" | jq --arg repo "$GITHUB_REPOSITORY" --arg cutoff "$CUTOFF" '[.[] | select(.repository.full_name != $repo or .updated_at <= $cutoff)] | length') echo "count=$COUNT" >> "$GITHUB_OUTPUT" if [ "$COUNT" = "0" ]; then @@ -99,7 +136,7 @@ jobs: - uses: ./.github/actions/tend-setup if: steps.check.outputs.count != '0' || github.event_name == 'workflow_dispatch' - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 if: steps.check.outputs.count != '0' || github.event_name == 'workflow_dispatch' with: github_token: ${{ secrets.TEND_BOT_TOKEN }} diff --git a/.github/workflows/tend-review-runs.yaml b/.github/workflows/tend-review-runs.yaml index 3f5e99a03fef..02fc07e21ef6 100644 --- a/.github/workflows/tend-review-runs.yaml +++ b/.github/workflows/tend-review-runs.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -32,7 +32,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-review.yaml b/.github/workflows/tend-review.yaml index 3a408820476f..f197e0f8bfd3 100644 --- a/.github/workflows/tend-review.yaml +++ b/.github/workflows/tend-review.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -51,7 +51,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-triage.yaml b/.github/workflows/tend-triage.yaml index cad38c9b6224..b93ac0db34df 100644 --- a/.github/workflows/tend-triage.yaml +++ b/.github/workflows/tend-triage.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -35,7 +35,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-weekly.yaml b/.github/workflows/tend-weekly.yaml index c17cda907e76..3bf6680393e9 100644 --- a/.github/workflows/tend-weekly.yaml +++ b/.github/workflows/tend-weekly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.1.11. Regenerate with: uvx tend@latest init +# Generated by tend 0.1.12. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -32,7 +32,7 @@ jobs: - uses: ./.github/actions/tend-setup - - uses: max-sixty/tend/claude@0.1.11 + - uses: max-sixty/tend/claude@0.1.12 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}