Operator simplification: REST-only SCM + standardized LLM_* config - #134
Merged
Conversation
Checkout now clones over HTTPS with the bot token supplied per git invocation as an auth header (never written to .git/config); posting goes straight through the REST API. Removes the glab/gh CLIs, the upstream GitLab/GitHub MCP servers (and bin/bubo mcp-upstream), the bubo.mcp client, the [mcp_servers.gitlab] profile block + its tool allowlist, and denied_tools_regex. Net SCM dependency is now git + the agent CLI. Bubo's own MCP server and the local-git MCP are unchanged.
Collapse the LLM agent knobs to LLM_API_KEY / LLM_MODEL / LLM_MODEL_EFFORT plus an optional LLM_BASE_URL for custom OpenAI-compatible endpoints, and make them actually take effect: `bubo init` now templates the model, effort, and (when a base_url is set) a [model_providers] block into the Codex profile, and writes the model into the Claude settings. Previously model/effort were hardcoded in the template and silently ignored. The credential-stripping reviewer allowlist gains one deliberate, documented exception: in base_url mode (and only then) LLM_API_KEY + LLM_BASE_URL are passed through to the agent, since a custom endpoint reads the key from the environment at request time. `llm_api_key_env` is deprecated (still honored); `reasoning_effort` is read as a fallback for the new `llm_model_effort`. The GitHub Action writes config before templating the agent profile and supports llm-base-url / llm-model-effort. Auto-running the agent login from LLM_API_KEY at init is deferred to a follow-up (needs validation against the real Codex/Claude CLIs); for now the agent authenticates via its own login.
Reflect the two code changes in the operator docs: - Drop glab/gh and the SCM MCP servers from prerequisites, recipes, and configuration; the only runtime SCM deps are now git + the agent CLI. - Document the standardized LLM surface (LLM_API_KEY, LLM_MODEL, LLM_MODEL_EFFORT) and the optional LLM_BASE_URL for OpenAI-compatible endpoints, including the env-exfiltration tradeoff of base_url mode. - Refresh the GitHub Action inputs and recipes accordingly.
Bubo is free and open source; the only way to learn what to improve is anonymous usage signal from real installs. Add an on-by-default [analytics] block that ships NUMBERS ONLY to PostHog over OTLP logs. - New [analytics] config (on by default). Opt out via `enabled = false`, `BUBO_ANALYTICS=0`, or the cross-tool `DO_NOT_TRACK=1` convention. - src/bubo/analytics.py emits anonymized events directly through the OTel logs API (no stdlib logging, so bubo's own logs can never leak): session_start, review_completed (incl. lines-of-code reviewed), and a per-cycle usage_snapshot derived from the existing SQLite aggregate readers (metrics/outcomes/latency). - Privacy is enforced structurally by a default-deny allowlist: only counts, durations, LoC, tokens, SCM type, and model name leave the machine. Never repo/project names, paths, SHAs, finding text, errors, or credentials. Resource is built without env-merge so OTEL_RESOURCE_* cannot ride along. An anonymous random install id is the PostHog distinct_id so distinct installs can be counted without identifying them. - Best-effort throughout: bounded timeouts, all exceptions swallowed; analytics never slows or breaks a review, and the extra LoC API call is skipped entirely when analytics is disabled. - Adds opentelemetry-exporter-otlp-proto-http; docs + env.example updated.
| if _logger is None and not _logger_failed: | ||
| built = _build_pipeline(cfg) | ||
| if built is None: | ||
| _logger_failed = True |
…shot Make analytics a pure event stream emitted at each DB-write point instead of reading the SQLite aggregates back. Per the design directive: review -> metric generation -> parallel(db, otel to posthog), one OTLP path to PostHog. - Drop usage_snapshot / emit_usage_snapshot and the metrics_summary, outcomes_summary, latency_summary poller import (the db readers stay; they still back report.py and the MCP stats tool). No code path reads the DB for analytics anymore. - Add a finding_outcome event in sync_outcomes beside the outcome upsert, carrying only the outcome name. It is transition-gated (emit only on the false->true flip): a posted finding is re-checked every cycle and PostHog has no per-finding key to dedupe on (the fingerprint is never sent), so an every-sync emit would multiply each outcome count. posted_findings_for_ outcome_sync now returns prior_outcome flags to drive the gate. - review_completed and session_start unchanged. Together the event stream reconstructs the usage report (totals from review_completed; engagement counts from finding_outcome) without reading the DB. - backfill_* import outcomes DB-only by design (documented): emitting imported history would stamp events at backfill time and corrupt the daily breakdown. Only the live poller + sync-outcomes path emits.
…nfig # Conflicts: # docs/configuration.md # docs/install-and-configure.md # docs/prerequisites.md # docs/recipes.md # src/bubo/poller.py # tests/test_inline_review.py
mountainowl
marked this pull request as ready for review
July 7, 2026 03:07
This was referenced Jul 7, 2026
mountainowl
added a commit
that referenced
this pull request
Jul 7, 2026
#152) Update the Nextra docs to match the shipped operator surface (PR #134): - SCM is REST-only: drop glab/gh from prerequisites and recipes; the token is now the credential for the tokenized HTTPS clone (per-call auth header, never written to .git/config). Remove denied_tools_regex. - Posting goes through the REST API for both providers; drop the MCP-first posting description and BUBO_GITHUB_MCP_TOOL / create_merge_request_thread. - LLM config: document llm_model (now drives the model via bubo init), llm_model_effort (low/medium/high), llm_base_url (OpenAI-compatible endpoint), and the login-based auth model. Mark llm_api_key_env deprecated and stop emitting it in generated configs; the self-hosted recipe now uses llm_base_url, which bubo init templates into the Codex profile. - Add the [analytics] section (opt-out; BUBO_ANALYTICS / DO_NOT_TRACK). Co-authored-by: mountainowl <mountainowl@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
1. REST-only SCM — drop glab/gh + SCM MCP servers (
refactor(scm))glab/gh. Clone/fetch/checkout go throughgitwith the token passed per-invocation viagit -c http.extraHeader=...so the token never lands in.git/config(the agent has read access to the worktree).mcp-upstreamdispatcher, the[mcp_servers.gitlab]block (~310 lines) and GitLab tool perms from the templates, anddenied_tools_regex/GITLAB_DENIED_TOOLS_REGEX.bin/bubo mcp,[mcp_servers.bubo]) and the local-git MCP.git+ the agent CLI.2. Standardize LLM config + OpenAI-compat (
feat(config))[agents]auth/model knobs toLLM_API_KEY,LLM_MODEL,LLM_MODEL_EFFORT, plus optionalLLM_BASE_URLfor OpenAI-compatible endpoints.llm_model/reasoning_effortwere hardcoded incodex-config.toml(no substitution), so they silently never took effect.bubo initnow templates the real values into the Codex profile (and a[model_providers.bubo]block whenllm_base_urlis set).reasoning_effort,llm_api_key_env) are still honored with aconfig_key_deprecatedlog event.action.ymlreordered to writeenv.tomlbeforebubo initso templating sees the real values.3. Anonymous, opt-out usage analytics → PostHog (
feat(analytics))"Help improve Bubo." On by default; numbers only, no identifying content.
[analytics]block. Opt out viaenabled = false,BUBO_ANALYTICS=0, or the cross-toolDO_NOT_TRACK=1.session_start,review_completed(incl. lines-of-code reviewed), and a per-cycleusage_snapshotbuilt from the existing SQLite aggregate readers._ALLOWED_ATTRS): only counts, durations, LoC, tokens, SCM type, and model name leave the machine — never repo/project names, paths, SHAs, finding text, errors, or credentials. The resource is built without env-merge soOTEL_RESOURCE_ATTRIBUTEScan't ride along. Anonymous random install id = PostHogdistinct_id.distinct_id→ identity mapping) has not been confirmed against a real project. That's the gate before relying on the data.Docs
prerequisites,recipes,configuration,install-and-configure,github-action).Verification
ruff✅ ·mypy✅ ·pytest538 passed (+30 new analytics tests; 4 pre-existingtest_project_layoutREADME-link failures present onmain, unrelated) ·mkdocs build --strictexit 0 ·cz check✅.Still to come on this branch
build(docker): bundle both agent CLIs (@openai/codex,@anthropic-ai/claude-code) + node + git in the image; env-driven entrypoint.