Skip to content

docs(ops): add runbooks for rate-limit, cache, Qdrant, Orb, and AI provider recovery#2638

Merged
JSONbored merged 2 commits into
mainfrom
docs/selfhost-observability-runbooks
Jul 2, 2026
Merged

docs(ops): add runbooks for rate-limit, cache, Qdrant, Orb, and AI provider recovery#2638
JSONbored merged 2 commits into
mainfrom
docs/selfhost-observability-runbooks

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Advances #1943

Investigation

Before implementing, I audited the current state of every deliverable category in the issue (Grafana dashboards, alert rules, emitted metrics, existing ops/troubleshooting docs) against the actual code, rather than assuming the issue's framing was still accurate:

  • Alert rules: already comprehensive — 13 groups, ~21 rules, covering queue backlog, dead-letter growth, GitHub rate-limits, Postgres, backups, Qdrant, Orb export, HTTP latency/5xx, webhook enqueue failures, and (as of this session's fix(selfhost): validate AI reviewer-provider configuration and add a failure circuit breaker #2540) AI provider circuit-breaker/inconclusive-review alerts.
  • Dashboards: 6 dashboards already cover queue, jobs, GitHub cache/rate-limits, Postgres, HTTP, and AI usage.
  • Metrics: well-instrumented (24 counters, 3 gauges, 1 histogram), no significant drift between docs and actual incr()/gauge()/observe() call sites.
  • Docs: docs.self-hosting-operations.tsx and docs.self-hosting-troubleshooting.tsx cover the basics (health endpoints, log events, alerting setup, routine checks) but had no runbook for five specific failure modes an operator can actually hit.

Given the infrastructure was already substantially complete, this PR is scoped to the genuine gap: five new troubleshooting runbooks.

Changes

Adds to docs.self-hosting-troubleshooting.tsx, each grounded in metric names/labels/alert names verified directly against the source (not inferred):

  • GitHub rate-limit responses and admission deferrals — the kind/key_scope/job_type label vocabulary, PromQL to break a spike down by token pool, and how to distinguish an expected brief admission hold from a sustained problem.
  • Low GitHub response-cache hit rate — the result/class labels and a hit-rate PromQL query.
  • Qdrant/vector-store errors — the op label, the embedding-model-change dimension-mismatch scenario, and how to recreate a collection.
  • Orb export/relay reconciliation — pointing at the export loop's Sentry cron monitor as the fastest first check before digging into error counters.
  • AI provider circuit breaker (shipped this session in fix(selfhost): validate AI reviewer-provider configuration and add a failure circuit breaker #2540) — what it does, that it self-heals in 60s with no manual reset, and how to tell a transient trip from a persistent credential/reachability problem.

Test plan

  • npm run ui:lint — 0 errors (pre-existing warnings in other files unrelated to this change)
  • npm run ui:typecheck clean
  • npm run ui:test — 33/33 passing
  • npm run ui:build succeeds
  • Root npm run typecheck clean
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • Verified in a live preview: navigated to /docs/self-hosting-troubleshooting, confirmed all 5 new sections render with correct heading text, code blocks, and TOC entries

…ovider recovery

A prior investigation found the dashboards, alert rules (13 groups, ~21
rules), and metrics for #1943's requested categories were already
substantially complete -- the genuine gap was runbook coverage for five
specific failure modes an operator can hit but had no documented recovery
path for.

Adds five new troubleshooting sections, each grounded in the exact metric
names, label vocabularies, and alert names the code actually emits (cross-
checked against src/selfhost/metrics.ts call sites and prometheus/rules/
alerts.yml, not inferred from the issue text):

- GitHub rate-limit responses and admission deferrals, with PromQL to
  break a spike down by kind/key_scope/job_type and distinguish an
  expected brief admission hold from a sustained problem.
- Low GitHub response-cache hit rate, with a PromQL hit-rate query by
  endpoint class.
- Qdrant/vector-store errors, including the dimension-mismatch-on-
  embedding-model-change scenario and how to recreate a collection.
- Orb export/relay reconciliation, pointing at the export loop's Sentry
  cron monitor as the fastest first check.
- The AI provider circuit breaker (#2540): what it does, that it
  self-heals in 60s with no manual reset, and how to tell a transient trip
  from a persistent credential/reachability problem.
@dosubot dosubot Bot added the size:L label Jul 2, 2026
@loopover-orb

loopover-orb Bot commented Jul 2, 2026

Copy link
Copy Markdown

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-02 23:26:23 UTC

3 files · 1 AI reviewer · no blockers · readiness 91/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR adds concrete self-host troubleshooting runbooks and updates the docs sidebar to keep the larger self-hosting section navigable. The changed React/route code is structurally sound, and the new drift test provides a useful guard that referenced metric and alert names still exist. The main concern is maintainability/scope: a runbook-only PR also changes the exported docs navigation shape, which is fine inside this file but worth keeping tightly justified because downstream consumers may have assumed flat groups.

Nits — 5 non-blocking
  • nit: apps/gittensory-ui/src/components/site/docs-nav.tsx:6 adds a long policy comment about information architecture that reads more like PR rationale than code documentation; trim it to the structural invariant or move the reasoning to docs/PR notes.
  • nit: apps/gittensory-ui/src/routes/docs.self-hosting-troubleshooting.tsx:219 shows a Qdrant delete command without the optional API-key header even though the preceding bullet tells operators to verify QDRANT_API_KEY, so authenticated deployments need an adjusted command.
  • nit: test/unit/docs-selfhost-troubleshooting-metric-names.test.ts:16 hard-codes the metric source file list, so a future metric moved to a new emitter file could make the docs guard fail even though the metric still exists in source.
  • apps/gittensory-ui/src/routes/docs.self-hosting-troubleshooting.tsx:219: include the authenticated Qdrant command variant, e.g. `curl -H "api-key: $QDRANT_API_KEY" ...`, or explicitly say the shown command is for unauthenticated local Qdrant.
  • test/unit/docs-selfhost-troubleshooting-metric-names.test.ts:16: consider deriving metric sources with a repository glob over `src/**/*.ts` or centralizing the emitter list so this drift guard does not become its own stale inventory.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
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 (size label size:L; no linked issue context).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 64 registered-repo PR(s), 55 merged, 522 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 64 PR(s), 522 issue(s).
Gate result ✅ Passing No configured blocker found.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: not available
  • Official Gittensor activity: 64 PR(s), 522 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Explain no-issue PR.
  • No action.
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gittensory-ui cc404f7 Commit Preview URL

Branch Preview URL
Jul 02 2026, 11:13 PM

Gate review round on PR #2638 flagged three real content gaps, all fixed:
- Route meta description didn't mention the 5 new runbook topics.
- Only the REST cache hit-rate PromQL was shown despite documenting both
  REST and GraphQL cache metrics -- added the GraphQL equivalent.
- The Qdrant collection-drop guidance didn't name the fixed collection
  ("gittensory", hard-coded, not env-configurable) or warn that dropping
  it temporarily removes ALL indexed RAG context until reindexing
  completes.

Also adds a drift-guard test (mirrors #2556's check-openapi-settings-
parity.mjs pattern): every gittensory_*_total metric name and Gittensory*
alert name referenced in the troubleshooting doc is cross-checked against
the actual source files and prometheus/rules/alerts.yml, so a future
rename/removal fails this test instead of the docs silently going stale.
Mutation-tested (a deliberately wrong metric name correctly fails it).

Separately: the self-hosting docs section was flat (13 pages under one
sidebar heading) and sat as its own top-level nav category alongside
"Maintainers" -- misleadingly implying self-hosting is an alternative to,
rather than a maintainer concern under, "Maintainers". Nests it as 4
sub-categories (setup / integrations / operations / release & security)
inside the Maintainers group instead, alongside the existing maintainer
pages as a "Hosted app" sub-category. No routes changed, only the nav
data model (extended to support one level of subgroups) and its render
logic. Verified in a live preview: nested titles render, active-link
highlighting and prev/next navigation both correctly span subgroup and
group boundaries.
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.06%. Comparing base (62262af) to head (cc404f7).
⚠️ Report is 12 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2638   +/-   ##
=======================================
  Coverage   96.06%   96.06%           
=======================================
  Files         234      234           
  Lines       26305    26305           
  Branches     9538     9538           
=======================================
  Hits        25270    25270           
  Misses        425      425           
  Partials      610      610           
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored
JSONbored merged commit 354d98b into main Jul 2, 2026
14 checks passed
@JSONbored
JSONbored deleted the docs/selfhost-observability-runbooks branch July 2, 2026 23:35
JSONbored added a commit that referenced this pull request Jul 3, 2026
…-rule syntax (#2659)

* chore(selfhost): validate Grafana dashboard JSON and Prometheus alert-rule syntax (#1943)

Completes the last unverified deliverable from #1943 -- the rest (dashboard
panels, alert rules, troubleshooting runbooks) was audited and found already
comprehensive or shipped in #2638. Grafana and Prometheus both fail OPEN on a
malformed dashboard/rule file (skip it, log a warning), so nothing else catches
this until an operator notices a panel or alert is simply missing.

scripts/validate-observability-configs.mjs checks every grafana/dashboards/*.json
file parses and has a title + panels array, and prometheus/rules/alerts.yml
parses and every rule has alert/expr/labels.severity/annotations.summary. Wired
into test:ci. 12 unit tests cover both validators' happy paths and failure modes.

* fix(selfhost): guard against non-object dashboard/group/rule values crashing the validator

Gate review caught a real bug: valid JSON/YAML that parses to a non-object (a
dashboard file containing literally "null", or a YAML sequence entry like
"- null") crashed the validator with a TypeError instead of producing the
structured validation error it exists to provide -- the one failure mode it's
explicitly meant to catch cleanly.

Added an isObject() guard before every dereference (dashboard, group, rule) and
4 new regression tests covering null/array/string/primitive-at-each-level.

* fix(selfhost): add a lightweight PromQL sanity check, drop unused test import

Gate review correctly noted the validator claimed to check "alert rule syntax"
but only verified expr was a non-empty string -- malformed PromQL like
"up ==" passed silently. Added a lightweight (not a real parser -- no
promtool/PromQL-grammar dependency, out of scope for this "if available"
deliverable) sanity check: balanced brackets and no dangling trailing binary
operator. Verified zero false positives against all 21 real rules in
prometheus/rules/alerts.yml. Also dropped an unused mkdirSync import the
gate flagged as a nit.

* fix(selfhost): use a bracket stack, not a depth counter, in the PromQL sanity check

Gate review caught a real bug: a plain depth counter only checks net nesting
count, so "sum(foo[5m))" -- opened with "(" and "[", closed with ")" and ")" --
nets to depth 0 and wrongly passes as balanced, even though the second ")"
doesn't match the "[" it's supposed to close. Switched to a stack that checks
each closer against the delimiter it actually needs to match. Verified against
the real alerts.yml (still zero false positives) and added a regression test
for the exact mismatched-type case the gate identified.
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.

Development

Successfully merging this pull request may close these issues.

1 participant