feat(settings): auto-fix CI failures with chat launcher hook#770
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #770 +/- ##
==========================================
+ Coverage 79.65% 79.70% +0.05%
==========================================
Files 115 115
Lines 39741 39847 +106
==========================================
+ Hits 31657 31762 +105
- Misses 8084 8085 +1 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a CI-automation feature that detects CI failures during SCM polling and auto-creates a new chat session preloaded with a failure-analysis prompt (optionally including CI logs fetched via SCM plugins), plus Settings/Repo Settings controls to configure the behavior.
Changes:
- Backend: detect CI status transitions to failure during SCM polling, apply cooldown/dedup, create a new chat session, and emit a
ci-auto-fix-session-createdevent with the formatted prompt. - Frontend: add Settings + Repo Settings controls for CI auto-fix enablement/model/prompt/cooldown, and wire an App-level listener to open the new session and send the prompt as the first message.
- SCM plugins: introduce a new
ci_failure_logsoperation for GitHub/GitLab to fetch failed-check log output (best-effort, truncated).
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ui/src/types/plugin.ts | Adds a CiFailureLog type for plugin log payloads. |
| src/ui/src/components/settings/Settings.module.css | Adds styling for a new settings subsection heading. |
| src/ui/src/components/settings/sections/RepoSettings.tsx | Adds per-repo CI auto-fix override + optional prompt override UI, persisted via app settings. |
| src/ui/src/components/settings/sections/GitSettings.tsx | Adds global CI automation settings (toggle/model/prompt/cooldown), persisted via app settings. |
| src/ui/src/App.tsx | Listens for ci-auto-fix-session-created, opens/selects the session, and sends the generated prompt. |
| src/scm/types.rs | Adds CiFailureLog + derive_overall_ci_status helper and tests. |
| src-tauri/src/state.rs | Adds in-memory per-workspace CI transition/cooldown tracking state. |
| src-tauri/src/commands/scm.rs | Implements CI failure transition detection, prompt formatting, session creation, event emission, and formatting tests. |
| plugins/scm-gitlab/plugin.json | Declares new ci_failure_logs operation for GitLab SCM plugin. |
| plugins/scm-gitlab/init.lua | Implements ci_failure_logs via glab to return failed job traces. |
| plugins/scm-github/plugin.json | Declares new ci_failure_logs operation for GitHub SCM plugin. |
| plugins/scm-github/init.lua | Implements ci_failure_logs via gh to return failed run logs. |
1ea999d to
d930c88
Compare
d930c88 to
9b14900
Compare
When CI transitions to a failed state, automatically create a new chat session with a configurable prompt containing failure details and logs. - Add ci_failure_logs plugin operation to GitHub and GitLab plugins - Add CI transition detection in the SCM polling loop with cooldown - Add CI Automation settings (toggle, model, prompt template, cooldown) - Add per-repo override for CI auto-fix in repository settings - Add CiFailureLog type and derive_overall_ci_status helper with tests - Wire ci-auto-fix-session-created event through to sendChatMessage
326e6cb to
9ae1afb
Compare
`buildModelRegistry(alternativeBackendsEnabled, agentBackends)` skips the `codexEnabled` flag and the Claude-OAuth Pi-anthropic filter that `useModelRegistry()` applies elsewhere, so the CI auto-fix model picker could save a model the Rust resolver later rejects at send time (or hide valid Codex Native models). Switch to the hook so this picker matches every other chat-side selector.
`gh pr checks` returns job/check names while `gh run list` returns workflow names, so exact-name matching almost always misses (a failed job named "Lint" inside a workflow named "CI" produces `failed_checks = ["Lint"]` and `run.name = "CI"`). When that happens, fall back to fetching logs from the most recent failed runs as a best-effort signal so the prompt still has something useful to work with. Also cap fetched logs at 3 runs. Each `gh run view --log-failed` is a sequential network call and the SCM polling semaphore caps workspace concurrency, not per-workspace fan-out — without a cap, a noisy repo could stretch a single poll tick well past the 30 s cadence.
When `auto_create_ci_fix_session` returned `false` (DB open / lookup / session-create failure), the polling loop left `ci_last_status` at the *prior* non-failure value. The next 30 s tick re-evaluated the same transition as still happening and tried again — under a persistent DB error that meant an auto-fix attempt every 30 s indefinitely. Always record the observed `overall_status`. The cooldown stamp (`last_auto_fix_triggered`) stays gated on success so a genuine failure can still be retried on the next observed transition. Extracted the decision into `next_ci_transition_state` so the bookkeeping rules can be unit-tested without an `AppState`.
When `ci_auto_fix_model` is unset, the model already falls back to `default_model`, but the provider stayed at the (empty) `ci_auto_fix_model_provider` setting. For a user with a non-Anthropic default (Codex Native, Pi, OpenAI), that routed auto-fix sessions through the default Anthropic gateway with the wrong model name — silently using the wrong harness. Fall back as a *pair*: when there's no explicit CI auto-fix model, read both `default_model` and `default_agent_backend` together. Explicit-model selections still take their explicit provider. Also documents the intentional parameter omissions in `useCiAutoFixSession` — auto-fix sessions use backend defaults for permission/thinking/plan/fast flags rather than inheriting toolbar state, because there is no per-tab UI state to inherit on a background-triggered session.
`Database::open` returns an owned value and the local `db` is only used through `&db` / `db.method(...)` afterwards, so the `mut` was unused and produced an `unused_mut` warning on dev builds. CI doesn't clippy `claudette-tauri`, so the warning was visible only when running the app locally — drop it.
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.
Summary
Automatically starts a CI auto-fix chat session when SCM polling observes a workspace branch transition into failed CI.
The implementation uses a split ownership model:
useCiAutoFixSession()hook owns the Tauri event subscription, adding/selecting the new session tab, and callingsendChatMessage.sequenceDiagram participant Poll as Rust SCM polling loop participant DB as SQLite/app_settings participant Plugin as SCM plugin participant Hook as useCiAutoFixSession() participant Chat as Chat send path Poll->>Poll: derive_overall_ci_status(checks) Poll->>Poll: Detect observed transition to Failure Poll->>DB: Read global/per-repo settings + cooldown Poll->>Plugin: ci_failure_logs(branch, failed_checks) Plugin-->>Poll: Failure logs when supported Poll->>DB: create_chat_session(workspace_id) Poll->>Hook: emit ci-auto-fix-session-created Hook->>Hook: get session, add tab, select session Hook->>Chat: sendChatMessage(prompt, model/backend)Key behavior
parse_ci_auto_fix_cooldown_seconds).repo:<id>:ci_auto_fix_enabled,repo:<id>:ci_auto_fix_prompt), prefetched once per poll cycle into an in-memory snapshot.ci_last_statusestablishes a baseline without firing (is_ci_failure_transition(None, _) == false).useModelRegistry(), so it applies the samecodexEnabled/ Pi-SDK / Claude-OAuth filters as every other chat-side selector and cannot save a model the Rust resolver would later reject.default_model+default_agent_backendas a pair, so a user whose global default is a non-Anthropic backend (Codex Native / Pi / OpenAI) gets the matching harness on the auto-fix session.gh run view --log-failed. Matches by check/job name first; falls back to the most recent failed runs when exact-name matching misses, sincegh pr checksreturns job names whilegh run listreturns workflow names. Capped at 3 runs to bound per-tick wall time.glab ci traceper failed job.result.codebefore readingstdoutand logstderron failure.markdown_code_fence_for), so logs containing ``` don't break prompt formatting.OperationNotSupported⇒ check names + URLs only).model,backend_id, anddisable1mContextare passed through. The hook documents this in-line.Reliability & dedup
ci_last_status: HashMap<workspace_id, CiTransitionState>onAppState.next_ci_transition_statealways records the observedoverall_status— including when the attempt fails (DB open,create_chat_session, or Tauri event emit). This prevents a persistent failure from firing every 30 s. The cooldown stamp (last_auto_fix_triggered) only advances on success, so a genuine failure can still be retried on the next observed transition.handle.emit(...)fails after a session was created in SQLite, the just-created session is archived to avoid orphan tabs. The path still counts as "handled" so a transient emit failure doesn't spam retries.Settings UI
Settings → Git → CI Automation:
default_model+default_agent_backendwhen unset){{failed_checks}},{{failure_logs}},{{branch}},{{pr_title}},{{pr_url}},{{pr_number}},{{all_checks}})Translations added under the
settingsnamespace for all five shipped locales (en, es, ja, pt-BR, zh-CN).Docs
site/src/content/docs/features/settings.mdx— rows in the Git settings table.site/src/content/docs/features/scm-providers.mdx— cross-reference to the feature.site/src/content/docs/features/per-repo-settings.mdx— per-repo override row.Verification
nix develop -c cargo fmt --all --checknix develop -c cargo clippy -p claudette -p claudette-server -p claudette-cli --all-targets --all-features(CI scope;-Dwarnings)nix develop -c cargo test -p claudette-tauri commands::scm::ci_auto_fix_tests— 12 tests pass (prompt formatting, transition logic, cooldown clamping, fence-sizing, and the newnext_ci_transition_state_*cases pinning the unhandled-status-record behavior).nix develop -c bash -lc 'cd src/ui && bunx tsc -b'nix develop -c bash -lc 'cd src/ui && bun run lint'— passes with existing warnings in unrelated files.nix develop -c bash -lc 'cd src/ui && bun run test'— 2517 frontend tests pass.Checklist
settings.mdx,scm-providers.mdx,per-repo-settings.mdx)origin/main