---
name: ci-debug
description: >
  Debug a GitLab CI/CD pipeline failure: pull the failing job logs, identify the root cause,
  and propose or apply a fix. Use when the user says /ci-debug, pastes a GitLab job or pipeline URL,
  'debug this pipeline', 'debug la CI', 'debug ce job', 'ce job fail', 'ce job a planté',
  'regarde ce job', 'pourquoi ça fail', 'ça fail en CI', 'erreur CI', 'la pipeline fail',
  'la CI fail', 'pipeline échouée', 'CI échouée', 'pourquoi la pipeline fail',
  'fix the CI', 'répare la pipeline', 'why is the build failing', 'the pipeline is red',
  or encounters any CI/CD job failure. Distinct from /ci-reports, which only reads the
  structured JUnit test report — /ci-debug digs into the raw job logs to find and fix the cause.
argument-hint: "[GitLab job URL, pipeline URL, or error description]"
allowed-tools:
  - Read
  - Edit
  - Write
  - Glob
  - Grep
  - Bash
  - Skill
  - AskUserQuestion
---

Debug a GitLab CI/CD job failure — analyze logs, identify root cause, and propose or apply a fix.

## Required tools

- `glab`
- `jq`

Run `pysae-ai-tools tools require <tools>` before starting; if it exits non-zero, abort and report the install command from stderr to the user.

## Mode detection

Detect the execution context using `detect_context` (pass `$ARGUMENTS` refs for automatic parsing of job/pipeline URLs):
```bash
CTX=$(pysae-ai-tools internal detect-context $ARGUMENTS_REFS)
IS_CI=$(echo "$CTX" | jq -r '.is_ci')
PROJECT_ID=$(echo "$CTX" | jq -r '.project_id // empty')
PROJECT_PATH=$(echo "$CTX" | jq -r '.project_path // empty')
PIPELINE_ID=$(echo "$CTX" | jq -r '.pipeline_id // empty')
JOB_ID=$(echo "$CTX" | jq -r '.job_id // empty')
```

In CI mode (`IS_CI` is `true`):
- **Never use `AskUserQuestion`** — use `PIPELINE_ID` and `JOB_ID` as defaults when no URL is provided (these come from `$CI_PIPELINE_ID` and `$CI_JOB_ID` via detect_context)
- **Apply fixes automatically** without asking for confirmation (code, test, lint fixes)
- For non-actionable issues (infra, flaky, auth), print the diagnosis to stdout and **fail with exit code 1**

## Step 1 : Input parsing

`PROJECT_PATH`, `JOB_ID`, and `PIPELINE_ID` are already resolved by detect_context from `$ARGUMENTS` refs or CI env vars.

### No URL/ref provided
If the user describes an error without a URL:
1. **CI mode**: use `PROJECT_ID`, `PIPELINE_ID`, and `JOB_ID` from detect_context directly
2. **Local mode**: detect the current repo from `git remote get-url origin`, fetch the latest failed pipeline (`glab ci list -s failed --per-page 1`), and ask the user to confirm

## Step 2 : Fetch job context

### Single job
```bash
# Job metadata (status, stage, name, runner, duration, timestamps)
glab api "projects/${PROJECT_ID}/jobs/${JOB_ID}"

# Job logs (raw trace)
glab api "projects/${PROJECT_ID}/jobs/${JOB_ID}/trace"
```

### Full pipeline (when PIPELINE_ID provided or multiple jobs failed)
```bash
# List all jobs in pipeline
glab api "projects/${PROJECT_ID}/pipelines/${PIPELINE_ID}/jobs?per_page=100" | \
  jq -r '.[] | select(.status == "failed") | "\(.id) | \(.stage) | \(.name) | \(.failure_reason)"'
```

Then fetch logs for each failed job.

### Test report (JUnit) — preferred entry point for test failures

When the failure is (or might be) a **test** failure, read the pipeline test report before parsing raw logs. GitLab aggregates every `artifacts:reports:junit` of the pipeline into a structured report — failing test name, class, message and stack trace, no trace scraping:

```bash
pysae-ai-tools ci test-report --pipeline-id "${PIPELINE_ID}" --json
```

Exit `2` = failures present, `0` = no failures, `1` = no JUnit report published (fall back to log trimming below). Use each case's `classname` + `name` to build the exact local reproduction command in Step 4. The `/ci-reports` skill wraps this same command if you want to surface the report on its own.

### Other artifacts

For non-JUnit artifacts (coverage reports, build logs, screenshots, generated files), list then download via the artifacts command (the `/ci-artifacts` skill wraps it):

```bash
pysae-ai-tools ci artifacts list --job-id "${JOB_ID}"
pysae-ai-tools ci artifacts download --job-id "${JOB_ID}" --path <path/to/file>
```

Never print artifact contents that may embed secrets (env dumps, `.env` files) — apply the masking rule below.

### Log trimming

Job logs can be huge. Apply smart trimming:
1. Search for error markers: `ERROR`, `FATAL`, `error:`, `failed`, `exit code`, `FAIL`, `E   `, `AssertionError`, `TypeError`, `SyntaxError`
2. Extract 50 lines before and 10 lines after each error marker
3. Always include the last 80 lines (often contains the summary/exit status)
4. If log is under 500 lines, read it entirely

## Step 3 : Root cause classification

Classify the failure into one of these categories (see [references/failure-patterns.md](references/failure-patterns.md) for detailed patterns):

| Category | Signals | Typical fix |
|----------|---------|-------------|
| **build** | compile error, type error, missing import, syntax | Fix source code |
| **test** | test failure, assertion, snapshot mismatch | Fix test or source code |
| **lint** | eslint, prettier, semgrep, ruff, mypy | Fix lint issue |
| **deps** | npm install fail, yarn resolution, pip conflict, lock mismatch | Fix lockfile or deps |
| **docker** | image build fail, layer cache, registry auth | Fix Dockerfile or registry config |
| **infra** | runner timeout, OOM, disk full, network, k8s | Retry or adjust resources |
| **auth** | 401/403, token expired, missing variable | Fix CI/CD variables or permissions |
| **config** | missing env var, wrong ref, template include fail | Fix .gitlab-ci.yml or variables |
| **deploy** | helm error, argocd sync fail, k8s apply error | Fix helm values or manifests |
| **flaky** | intermittent, works locally, race condition | Retry or fix flaky test |

## Step 4 : Diagnosis

Based on the category, perform targeted analysis:

### Code issues (build, test, lint)
1. Identify the exact file(s) and line(s) from the log
2. Read the failing file(s) locally
3. Compare with the CI branch: `git log --oneline -5` to see recent changes
4. Identify the root cause (what changed that broke it)

### Test failures — local reproduction
When the category is **test**, try to reproduce and fix locally before proposing a CI retry:
1. **Pull the JUnit test report** (Step 2) — it gives you the failing `classname`/`name` and the assertion message/stack trace directly, which is more reliable than scraping the log
2. Install dependencies if needed (invoke `/ci-prepare` if available)
3. Run the exact failing test(s) locally, building the selector from the test report's `classname` + `name`:
   - Python: `pytest <test_file>::<test_class>::<test_name> -v`
   - Node: `yarn test -- --testPathPattern <pattern>` or `npx vitest run <file>`
4. If the test fails locally:
   - Analyze the failure, fix the code or the test
   - Re-run to confirm the fix
   - Commit the fix with an appropriate prefix
5. If the test passes locally (flaky or env-dependent):
   - Note the difference (CI image, env vars, versions)
   - Suggest retrying the job or adjusting the test for CI compatibility

### Dependency issues (deps)
1. Check lock file consistency: `git diff HEAD~3 -- *lock*` or `yarn.lock` / `package-lock.json` / `poetry.lock`
2. Check node/python version mismatch between local and CI
3. Look for registry-specific issues (private npm, PyPI)

### Infrastructure issues (infra, docker, auth)
1. Check if it's a transient issue (retry might fix it)
2. Check runner tags and image configuration
3. Check CI/CD variable availability: `glab variable list` (if permissions allow)

### Config issues (config, deploy)
1. Read `.gitlab-ci.yml` and any included templates
2. Check for template version mismatches (gitlab-templates `2.x` branch)
3. Verify variable references (`$CI_*`, custom variables)

## Step 5 : Resolution

### Actionable fix (code, test, lint, deps, config)

1. **Describe** the root cause in 2-3 lines
2. **For test failures**: if local reproduction was done in Step 4 and the fix is already committed, skip to step 6
3. **Show** the exact fix needed (file + diff)
4. **Ask** the user to confirm before applying, then apply the fix (Edit tool)
5. Verify the fix locally:
   - Node: `yarn test`, `yarn lint`, `yarn build`
   - Python: `pytest`, `ruff check .`, `mypy .`
   - Docker: `docker build .`

### Non-actionable (infra, flaky, auth)

1. **Explain** the root cause
2. **Suggest** remediation:
   - Infra: "Retry the job" + how to increase resources if recurring
   - Flaky: identify the flaky test, suggest `retry: 1` or fixing the race condition
   - Auth: which variable/token needs refreshing and where to configure it
3. If retryable: invoke the `/ci-run` skill to retry the job (ask user first)

## Step 6 : Memory & learning

After resolution, check if this failure pattern is recurring:

1. **Same job failed before?** Check recent pipelines: `glab ci list -s failed --per-page 10`
2. **Known pattern?** Compare with [references/failure-patterns.md](references/failure-patterns.md)
3. **New pattern?** If the failure recurs, propose adding it to `references/failure-patterns.md` (shared asset, benefits the `/code-autopilot` CI flow and every dev) rather than only personal or project-local memory.

## Pysae-specific knowledge

Use [references/ci-conventions.md](references/ci-conventions.md) for Pysae CI/CD conventions:
- Shared templates from `pysae/infra/gitlab-templates` (branch `2.x`)
- Standard stages, image patterns, and variable naming
- Repo-specific quirks (driver Android build, info iOS build, etc.)

## Critical rules

- **Never modify `.gitlab-ci.yml`** without explicit user approval (high-risk file)
- **Never expose secrets** from CI/CD variables or job logs in output
- **Mask sensitive data** when displaying log excerpts (tokens, passwords, API keys)
- **Prefer local reproduction** when possible — suggest running the failing command locally before pushing a fix
- **Language**: diagnosis in French (user preference), code/commands in English
- **Emojis**: only in section headers

## Self-check before output

- [ ] Job/pipeline identified (ID, project, stage, name)
- [ ] Logs fetched and error section extracted
- [ ] For test failures: JUnit test report pulled (or confirmed absent) before scraping logs
- [ ] Failure category classified
- [ ] Root cause identified with evidence from logs
- [ ] Fix proposed (or remediation for non-actionable)
- [ ] Sensitive data masked in any displayed output
- [ ] Local reproduction command suggested (if applicable)

$ARGUMENTS
