Skip to content

fix(selfhost): only escalate the final AI-provider retry attempt to Sentry (#5046) - #5048

Merged
JSONbored merged 1 commit into
mainfrom
fix/5046-ai-provider-attempt-logging
Jul 11, 2026
Merged

fix(selfhost): only escalate the final AI-provider retry attempt to Sentry (#5046)#5048
JSONbored merged 1 commit into
mainfrom
fix/5046-ai-provider-attempt-logging

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Fixes fix(selfhost): AI-provider per-attempt logging amplifies one retry loop into up to 6 Sentry errors #5046, filed from live re-triage of the Sentry dashboard after this session's earlier 13-bug batch: GITTENSORY-K (selfhost_ai_provider_failed: claude_stalled_no_output) is still climbing (2077+ events) despite Fix fix(review): claude-code subscription-CLI hangs/times out repeatedly (4,030 Sentry events, ongoing) #4994/PR fix(review): add a stalled-no-output fast-fail deadline for claude-code #5013 already shipping and deploying — confirmed the deployed self-host release (gittensory-selfhost@6b52b207) is a descendant of that fix commit, so this is not a deploy-lag artifact.
  • Root cause: pulled raw events and found several sharing the exact same jobId/trace_id, with attempt: 0, attempt: 1, etc. — one single review job, retrying internally, with every individual attempt independently reaching Sentry as its own console.error. runWorkersOpinion (src/services/ai-review.ts) retries up to 3× per model × up to 2 models (6 attempts max) — and its own per-attempt log (ai_review_provider_attempt_failed) is deliberately console.warn, with an explicit comment citing feat(agent): serve stale decision packs with freshness marker and background rebuild #26: "Per-attempt logs are warn (noisy retries, skipped by the central Sentry forwarder); the exhausted summary is error." But logSelfHostAiProviderFailed (src/selfhost/ai.ts), called from inside createClaudeCodeAi/createCodexAi's own catch blocks on every throw, ignored that policy entirely and escalated every attempt — the two layers disagreed, and the lower one always wins since Sentry sees it either way. Fix fix(review): claude-code subscription-CLI hangs/times out repeatedly (4,030 Sentry events, ongoing) #4994 made each individual attempt fail faster (good, real, needed) but never touched this separate over-escalation.
  • Checked whether a blind fix (always downgrade to warn) was safe: it isn't. src/review/planner.ts and src/services/ai-slop.ts have their own retry loops (2× and 3× respectively) with zero logging of their own — they rely entirely on this same low-level log for any Sentry visibility into a persistent failure. A blanket downgrade would have made them silently blind.
  • Fix: thread finalAttempt?: boolean through AiRunOptions, mirroring the existing attempt/jobId correlation fields (#codex-timeout-fields — same optional, purely-observational pattern). logSelfHostAiProviderFailed logs at warn only when finalAttempt === false; unset (any single-shot caller) or true stays error, so nothing loses visibility by default. runWorkersOpinion, the dual-AI tie-break judge call, runPlannerModel, and runWorkersSlopOpinion all compute it as attempt === maxAttempts - 1 && modelIndex === models.length - 1 and pass it through.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (Closes #5046).

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • test:coverage (full unsharded): not run end-to-end — ran scoped vitest --coverage for the five touched test files (453 tests total: selfhost-ai.test.ts, ai-review.test.ts, ai-review-advisory.test.ts, planner.test.ts, ai-slop.test.ts) and confirmed via lcov that every changed line and both branches of every new finalAttempt expression are covered — the ai-review.ts/planner.ts/ai-slop.ts boolean expressions were already naturally exercised (both truthy and falsy) by the existing retry/exhaustion test suites; three new dedicated regression tests in selfhost-ai.test.ts pin the actual behavior change (warn vs error vs unset-stays-loud).
  • actionlint / test:workers / build:mcp / test:mcp-pack / ui:openapi:check / ui:lint / ui:typecheck / ui:build / npm audit: not run — this change touches only existing internal AI-provider plumbing (no new API/schema/binding/dependency surface) and its tests; no workflow, MCP, UI, or dependency-manifest surface changed.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. (N/A — no auth surface touched.)
  • API/OpenAPI/MCP behavior is updated and tested where needed. (N/A.)
  • UI changes use live API data or real empty/error/loading states. (N/A.)
  • Visible UI changes include a UI Evidence section. (N/A.)
  • Public docs/changelogs are updated where needed. (N/A — internal engine behavior; changelog is not edited in a normal PR.)

Notes

Follow-up from this session's earlier 13-bug Sentry triage batch (#4994#5006) — found while re-checking the live dashboard after that batch shipped. The claude_stalled_no_output events themselves are expected/correct (Fix #4994 working as designed: fast-detecting a real, occasional external CLI hang); this PR addresses the separate, over-eager escalation of every retry within that detection.

…entry (#5046)

logSelfHostAiProviderFailed fired console.error on every subscription-
CLI attempt, not just the final one -- contradicting ai-review.ts's
own documented "per-attempt=warn, exhausted=error" policy (#26).
GITTENSORY-K (claude_stalled_no_output) hit 2077+ events: pulled raw
events sharing the same jobId/trace_id at attempt 0, 1, 2, confirming
one review's 3x-retry-per-model loop was independently escalating
each attempt to Sentry instead of just the exhausted outcome.

Thread finalAttempt through AiRunOptions (mirroring the existing
attempt/jobId correlation fields) so the provider's own log can tell
a retried attempt (quiet) from a genuinely exhausted one (loud).
runWorkersOpinion, the dual-AI tie-break judge, the issue planner, and
the AI slop advisory all compute it as "last attempt of the last
model" and pass it through. Two of those callers (planner.ts,
ai-slop.ts) have no logging of their own -- their retry loops rely
entirely on this log for visibility, so a blanket downgrade would have
silenced them; unset stays loud by default for exactly that reason.
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.16%. Comparing base (8f10e83) to head (f67375a).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5048   +/-   ##
=======================================
  Coverage   94.16%   94.16%           
=======================================
  Files         463      463           
  Lines       39429    39435    +6     
  Branches    14384    14390    +6     
=======================================
+ Hits        37128    37134    +6     
  Misses       1646     1646           
  Partials      655      655           
Flag Coverage Δ
shard-1 46.59% <30.00%> (-0.02%) ⬇️
shard-2 33.21% <70.00%> (-0.11%) ⬇️
shard-3 31.22% <0.00%> (-0.19%) ⬇️
shard-4 32.48% <40.00%> (-0.27%) ⬇️
shard-5 34.37% <20.00%> (+0.93%) ⬆️
shard-6 45.22% <30.00%> (+0.21%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/review/planner.ts 100.00% <100.00%> (ø)
src/selfhost/ai.ts 98.05% <100.00%> (+<0.01%) ⬆️
src/services/ai-review.ts 96.72% <100.00%> (+0.01%) ⬆️
src/services/ai-slop.ts 93.42% <100.00%> (+0.08%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored JSONbored self-assigned this Jul 11, 2026
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 11, 2026
@loopover-orb

loopover-orb Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-11 10:47:49 UTC

5 files · no blockers · readiness 100/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • AI review already in progress for this PR head: Another Gittensory pass is already running the AI review for this exact PR head. This pass is skipping to avoid a duplicate LLM call.

Review summary
AI review is already running for this PR head in another Gittensory pass. Gittensory is holding this PR for manual review until that pass completes.

Nits — 2 non-blocking
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • AI review already in progress for this PR head — The gate is held for a human reviewer rather than passed automatically; it re-evaluates once the in-flight review completes or on the next update.
Signal Result Evidence
Code review ✅ No blockers No AI review summary
Linked issue ✅ Linked #5046, #4994
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (2 linked issues).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 47 registered-repo PR(s), 39 merged, 428 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 47 PR(s), 428 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Improvement ✅ Minor risk: clean · value: minor — Code changes are accompanied by test evidence.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, JavaScript, MDX, Shell, Solidity
  • Official Gittensor activity: 47 PR(s), 428 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 11, 2026
@JSONbored
JSONbored merged commit af46a90 into main Jul 11, 2026
19 checks passed
@JSONbored
JSONbored deleted the fix/5046-ai-provider-attempt-logging branch July 11, 2026 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

1 participant