# jenkins-job-insight > Turn Jenkins failures into actionable analysis, team review, and ready-to-file bugs. --- Source: quickstart.md # Running Your First Analysis You want to get from a fresh setup to one real Jenkins analysis you can inspect from both the terminal and the browser. The fastest supported path uses Docker Compose so the service, web UI, and supported AI CLIs come up together with the fewest moving parts. ## Prerequisites - Docker with Docker Compose - `uv` installed so you can install the `jji` CLI - A Jenkins URL, username, API token or password, and a build number you want to inspect - One AI provider credential; the simplest first run is `claude` with `ANTHROPIC_API_KEY` ## Quick Example ```bash cp .env.example .env ``` ```dotenv # set at least these values in .env JENKINS_URL=https://jenkins.example.com JENKINS_USER=your-username JENKINS_PASSWORD=your-api-token AI_PROVIDER=claude AI_MODEL=your-model-name ANTHROPIC_API_KEY=your-anthropic-api-key ``` ```bash docker compose up -d uv tool install jenkins-job-insight export JJI_SERVER=http://localhost:8000 export JJI_USERNAME=jdoe jji health jji analyze --job-name test --build-number 123 ``` Open `http://localhost:8000`, enter the same username on the register page, and use the dashboard to open the new run. After `jji analyze`, copy the printed `job_id`; `jji status ` follows the run from the terminal, and the `Poll:` URL is the browser URL for the same stored result. ```text Job queued: Status: queued Poll: /results/ ``` ## Step-by-Step ```mermaid flowchart LR A[Set Jenkins and AI values] --> B[docker compose up -d] B --> C[Open /register and save a username] C --> D[jji analyze --job-name ... --build-number ...] D --> E[/status/ while work is in progress] E --> F[/results/ when analysis is complete] ``` 1. Create the server environment file. ```bash cp .env.example .env ``` ```dotenv JENKINS_URL=https://jenkins.example.com JENKINS_USER=your-username JENKINS_PASSWORD=your-api-token AI_PROVIDER=claude AI_MODEL=your-model-name ANTHROPIC_API_KEY=your-anthropic-api-key ``` This is the minimum setup for a Jenkins-backed analysis. Keep the rest of `.env` at the defaults for your first run. > **Tip:** Use a failed build number for your first run. A passed build finishes cleanly, but there is nothing to analyze. 2. Start the service and point the CLI at it. ```bash docker compose up -d uv tool install jenkins-job-insight export JJI_SERVER=http://localhost:8000 export JJI_USERNAME=jdoe jji health ``` Use the same `JJI_USERNAME` you plan to save in the browser so future comments and review actions line up under one name. When the service is ready, `jji health` returns `healthy`. 3. Register your browser profile. Open `http://localhost:8000/`. On a first visit, the app sends you to `/register`. Enter a username and click **Save**. For a normal first run, leave the API key, GitHub token, Jira email, and Jira token fields empty. > **Note:** You do not need an admin API key or tracker tokens to run your first analysis. A username is enough. 4. Queue the analysis from the CLI. ```bash jji analyze --job-name test --build-number 123 ``` Replace `test` and `123` with a real Jenkins job and build number. Because Jenkins and AI defaults are already in `.env`, this first command only needs the job name and build number. The CLI prints three important lines: - `Job queued`: the new `job_id` - `Status`: the current stored status, starting as `queued` - `Poll`: the stored result URL for this run If the Jenkins build is still running, the stored job can stay in `waiting` before JJI starts the AI analysis. 5. Inspect the run from the CLI. ```bash jji status jji results show jji results dashboard ``` Use `jji status` while the run is still moving through `waiting`, `pending`, or `running`. Use `jji results show` for a one-run summary, and `jji results dashboard` when you want the recent-analysis list with failure counts and review progress. 6. Inspect the same run in the web UI. Open the `Poll:` URL from `jji analyze`, or go back to `http://localhost:8000/` and click the row on the dashboard. The UI handles the rest: - in-progress work opens the status page - completed work opens the report page - the status page refreshes every 10 seconds and switches to the final report automatically Once the run is complete, the report page shows the summary, AI provider/model, grouped failures, and a direct link back to the Jenkins build. ## Advanced Usage ### Save your CLI server profile If you do not want to export `JJI_SERVER` and `JJI_USERNAME` in every shell, create `~/.config/jji/config.toml`: ```toml [default] server = "local" [servers.local] url = "http://localhost:8000" username = "jdoe" ``` After that, `jji health` and `jji analyze ...` use that profile automatically. The same file can also hold Jenkins, AI, and other per-server defaults when you want the CLI to supply them. ### Override Jenkins or AI settings on one run ```bash jji analyze \ --job-name test \ --build-number 123 \ --jenkins-url https://jenkins.example.com \ --jenkins-user your-username \ --jenkins-password your-api-token \ --provider claude \ --model your-model-name ``` Use this when you do not want to keep Jenkins credentials or AI defaults in the server environment. ### Switch AI providers Use one provider at a time and set the matching authentication variable. | Provider | Example `.env` values | | --- | --- | | `claude` | `AI_PROVIDER=claude`, `AI_MODEL=opus-4`, `ANTHROPIC_API_KEY=...` | | `gemini` | `AI_PROVIDER=gemini`, `AI_MODEL=gemini-2.5-pro`, `GEMINI_API_KEY=...` | | `cursor` | `AI_PROVIDER=cursor`, `AI_MODEL=gpt-5.4-xhigh`, `CURSOR_API_KEY=...` | If you want multi-model peer review after your first successful run, see [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) for details. ### Run from a source checkout instead of Docker ```bash uv sync cd frontend && npm install && npm run build uv run jenkins-job-insight ``` Use this when you want a local process instead of a container. Build the web UI before starting the server, or browser routes will not load. If you want to analyze JUnit XML or raw failures without Jenkins, see [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) for details. ### Make generated links absolute Set `PUBLIC_BASE_URL` on the server if you want `Poll:` and other generated links to use a full public URL instead of `/results/`. ## Troubleshooting - `Error: No server specified`: export `JJI_SERVER=http://localhost:8000`, pass `--server http://localhost:8000`, or save a CLI server profile. - The browser keeps sending you to `/register`: save a username first. A normal user does not need a password or API key. - The browser shows `Frontend not built`: this only happens on a local source checkout. Run `cd frontend && npm install && npm run build`, then restart the server. - Jenkins certificate errors on the first run: set `JENKINS_SSL_VERIFY=false` in `.env`, or pass `--no-jenkins-ssl-verify` on the CLI. - The run stays in `waiting`: JJI is still watching Jenkins finish the build. If you do not want that behavior, re-run with `--no-wait`. ## Related Pages - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [CLI Command Reference](cli-command-reference.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) --- Source: analyzing-jenkins-jobs.md # Analyzing Jenkins Jobs Queue a Jenkins build when you already know the job name and build number and want a report you can review instead of digging through Jenkins by hand. The main choices are whether to wait for the build to finish, how much artifact context to include, and whether to enrich product-bug findings with Jira matches. ## Prerequisites - A running JJI server, and `jji` pointed at it with `--server`, `JJI_SERVER`, or `~/.config/jji/config.toml` - A Jenkins job name and build number - Jenkins access configured on the server or supplied on the command line - An AI provider/model available on the server, or chosen per run with `--provider` and `--model` - Optional Jira URL, credentials, and project key if you want Jira enrichment - Need the initial setup first? See [Running Your First Analysis](quickstart.html) ## Quick Example ```bash jji analyze --job-name my-job --build-number 42 jji results show ``` ```text Job queued: Status: queued Poll: /results/ ``` The first command queues the analysis and prints the poll URL. The second command shows the current summary while the run is queued, waiting on Jenkins, running, or complete. ## Step-by-Step 1. Queue the Jenkins build. ```bash jji analyze --job-name my-job --build-number 42 ``` If your job lives inside folders, keep the Jenkins path style, such as `folder/job-name`. If you do not keep a default server in config, prefix the command with `jji --server http://localhost:8000`. 2. Check what state the queued run is in. ```bash jji results show jji results dashboard ``` | Status | What it means | | --- | --- | | `pending` | JJI has accepted the run and queued the work. | | `waiting` | JJI is polling Jenkins until the build finishes. | | `running` | JJI is collecting failures and analyzing them. | | `completed` | The report is ready. | | `failed` | Waiting or analysis ended with an error. | ```mermaid flowchart LR A[jji analyze] --> B[queued] B --> C{Wait enabled
and Jenkins configured?} C -->|Yes| D[waiting] C -->|No| E[running] D --> E E --> F{Jira enrichment
enabled and configured?} F -->|Yes| G[search Jira for product bug matches] F -->|No| H[save result] G --> H H --> I[completed report] ``` 3. Choose whether JJI should wait for Jenkins before it analyzes the build. ```bash jji analyze --job-name my-job --build-number 42 --wait jji analyze --job-name my-job --build-number 42 --no-wait jji analyze --job-name my-job --build-number 42 --wait --poll-interval 5 --max-wait 30 ``` | Option | Effect | | --- | --- | | `--wait` | Wait for Jenkins to finish before analysis starts. | | `--no-wait` | Skip waiting and analyze immediately. | | `--poll-interval 5` | Poll Jenkins every 5 minutes while waiting. | | `--max-wait 30` | Fail the run after 30 minutes of waiting. | > **Note:** The built-in wait defaults are `--wait`, a 2-minute poll interval, and `--max-wait 0`, which means "wait as long as needed." If Jenkins is not configured for the run, JJI skips the waiting step even when wait is enabled. > **Warning:** `--no-wait` is best when the Jenkins build is already finished, or when you intentionally want a snapshot of whatever Jenkins exposes at that moment. 4. Override Jenkins or AI settings for just this run. ```bash jji analyze \ --job-name my-job \ --build-number 42 \ --provider gemini \ --model gemini-2.5-pro \ --jenkins-url https://jenkins.local \ --jenkins-user admin \ --jenkins-password \ --no-jenkins-ssl-verify ``` Use this when the run needs different Jenkins access, a different model, or different SSL behavior than your usual defaults. Command-line flags override values from your environment or `~/.config/jji/config.toml` for that one run only. 5. Decide how much build-artifact context to include. ```bash jji analyze --job-name my-job --build-number 42 --no-get-job-artifacts jji analyze --job-name my-job --build-number 42 --get-job-artifacts --jenkins-artifacts-max-size-mb 50 ``` Artifact downloads are enabled by default. When available, JJI downloads build artifacts, unpacks tar and zip archives when possible, and uses that material as extra context for the analysis. > **Note:** The default artifact size cap is 500 MB. Oversize or failed downloads are skipped instead of failing the whole analysis. 6. Turn Jira enrichment on or off for the run. ```bash jji analyze \ --job-name my-job \ --build-number 42 \ --jira \ --jira-url https://jira.example.com \ --jira-project-key PROJ \ --jira-email user@example.com \ --jira-api-token \ --jira-max-results 10 ``` | Jira deployment | Use these flags | | --- | --- | | Cloud | `--jira --jira-url ... --jira-project-key ... --jira-email ... --jira-api-token ...` | | Server/Data Center | `--jira --jira-url ... --jira-project-key ... --jira-pat ...` | Use `--no-jira` when you want a clean run with no Jira lookups, even if Jira is normally enabled for your server profile. Jira enrichment happens after analysis, and matches are attached only to failures classified as product bugs. > **Tip:** If you omit both `--jira` and `--no-jira`, JJI follows its configured default behavior. In practice, Jira matches only appear when the Jira URL, credentials, and project key are all available. ## Advanced Usage ```bash jji results show --full jji analyze --job-name my-job --build-number 42 --tests-repo-url https://github.com/org/tests:feature/bar ``` The full results view is useful when you want to confirm the exact stored request parameters for a run, including wait settings, provider/model, artifact settings, and Jira toggles. Appending `:branch-or-tag` to `--tests-repo-url` lets you pin analysis context to a specific ref for a single run. For self-signed endpoints, use `--no-jenkins-ssl-verify` or `--no-jira-ssl-verify` only on the runs that need them. That keeps your normal defaults strict while still letting one-off runs succeed. Pipeline builds are supported too. If the parent job fails because child jobs failed, the finished report includes child-job analyses even when the parent has no direct failed tests of its own. If Jenkins has no structured test report, JJI falls back to console-based analysis. Peer review flags do not apply in that console-only path; for that workflow, see [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html). If you want to analyze failures without a live Jenkins build, see [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html). ## Troubleshooting - `Error: No server specified.` Point `jji` at a server with `--server`, set `JJI_SERVER`, or add a default server to `~/.config/jji/config.toml`. - `Job not found` or an immediate Jenkins 404 while waiting. Double-check `--job-name` and `--build-number`; jobs inside folders must use `folder/job-name`. - `Timed out waiting for Jenkins job ...`. Increase `--max-wait`, use `--max-wait 0` for no time limit, or switch to `--no-wait`. - The run completed with no failures. The Jenkins build finished successfully, so JJI returned a completed result with nothing to analyze. - Artifact evidence is missing. Make sure artifact downloads are enabled and consider raising `--jenkins-artifacts-max-size-mb`. - No Jira matches appeared. Confirm Jira was enabled for the run, the Jira URL and project key are set, and valid Cloud or Server/DC credentials were supplied. Remember that Jira matches are only attached to product bug results. ## Related Pages - [Running Your First Analysis](quickstart.html) - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) - [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) - [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) --- Source: analyzing-junit-xml-and-raw-failures.md # Analyzing JUnit XML and Raw Failures Use this workflow when you already have failing test output and want JJI to classify it immediately without waiting on a Jenkins build. It is the fastest way to turn a local test run, a CI artifact, or custom parser output into a stored report you can reopen later. ## Prerequisites - A running JJI server. If you still need to start one, see [Running Your First Analysis](quickstart.html). - An AI provider and model available either as server defaults (`AI_PROVIDER`, `AI_MODEL`) or in each request as `ai_provider` and `ai_model`. - One input source: a parsed failure list or a JUnit XML file. > **Note:** You do not need Jenkins credentials for this workflow. ## Quick Example ```bash JJI_SERVER=http://localhost:8000 curl -sS "$JJI_SERVER/analyze-failures" \ -H 'Content-Type: application/json' \ --data @- <<'JSON' { "failures": [ { "test_name": "test_foo", "error_message": "assert False", "stack_trace": "File test.py, line 10" } ], "ai_provider": "claude", "ai_model": "your-model-name" } JSON ``` That response is already final. Check the JSON `status`, then keep `job_id` or `result_url` if you want to reopen the stored result later. ## Step-by-Step 1. Pick the input mode that matches what you already have. | Send this | Use it when | What you get back | | --- | --- | --- | | `failures` | Another tool already parsed the failures | Final analysis JSON, `job_id`, and `result_url` | | `raw_xml` | You already have a JUnit XML artifact | Everything above, plus `enriched_xml` with AI annotations | > **Warning:** Send exactly one input source. `failures` and `raw_xml` are mutually exclusive, and `failures: []` is rejected. 2. Post your JUnit XML and save the enriched report. ```python import os from pathlib import Path import requests server = os.environ["JJI_SERVER"] raw_xml = Path("report.xml").read_text() response = requests.post( f"{server}/analyze-failures", json={ "raw_xml": raw_xml, "ai_provider": "claude", "ai_model": "your-model-name", }, timeout=600, ) response.raise_for_status() result = response.json() if result["status"] != "completed": raise SystemExit(result["summary"]) Path("report.enriched.xml").write_text(result["enriched_xml"]) print(result["summary"]) print(result["result_url"]) ``` In JUnit XML mode, JJI parses the failing testcases, analyzes them, and returns the same report with extra metadata and readable AI output attached. 3. Use the returned artifacts. ```mermaid sequenceDiagram participant You participant JJI participant Stored as Stored result You->>JJI: POST /analyze-failures\nraw failures or raw_xml JJI-->>You: status, summary, failures,\njob_id, result_url, enriched_xml? You->>Stored: Open result_url or GET /results/{job_id} ``` - Use `failures` in the JSON response for automation. - Use `result_url` to open the stored report in a browser or fetch it again with `GET /results/{job_id}`. - Use `enriched_xml` when you want the AI annotations embedded back into your JUnit artifact. - Continue the manual review flow from the stored report. See [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) for details. 4. Know what JJI adds in JUnit XML mode. - `report_url` on the first `testsuite` - `ai_classification` and `ai_details` on enriched failing testcases - a readable AI summary in `system-out` > **Tip:** Keep the full stack trace when you send structured failures. JJI groups repeated failures by the error message plus the first five stack-trace lines, but the full trace still gives the model better context. ## Advanced Usage ### How JJI reads JUnit XML | In your XML | JJI uses | | --- | --- | | `` or `` | `error_message` comes from the `message` attribute | | No `message` attribute | the first line of the element text becomes `error_message` | | `classname` and `name` | `test_name` becomes `classname.name` | | Only `name` | `test_name` uses `name` alone | | `` | `status` becomes `ERROR` | | `` | `status` becomes `FAILED` | ### Useful request options | Option | Use it when | Example | | --- | --- | --- | | `tests_repo_url` | The model should inspect your test repo | `"tests_repo_url": "https://github.com/org/my-tests:develop"` | | `additional_repos` | The model also needs product or infrastructure repos | `[{"name":"infra","url":"https://github.com/org/infra","ref":"main"}]` | | `raw_prompt` | You want request-specific instructions | `"raw_prompt": "Treat platform outages as product issues unless the test itself is broken."` | | `peer_ai_configs` | You want extra reviewer models | `[{"ai_provider":"gemini","ai_model":"pro"}]` | | `peer_analysis_max_rounds` | You want more or fewer reviewer rounds | `7` | | `enable_jira` | You want matching Jira issues attached when Jira is configured | `true` | - `tests_repo_url` accepts a `:ref` suffix, such as `https://github.com/org/my-tests:develop`. - Each `additional_repos` entry can also carry a `ref`, and every `name` must be unique. - Omit `peer_ai_configs` or `additional_repos` to use server defaults. Send `[]` to turn either one off for just one request. - `peer_analysis_max_rounds` accepts `1` through `10`. - When you need request-level Jira settings, send `jira_url`, `jira_project_key`, `jira_email`, plus either `jira_pat` or `jira_api_token`. ### Automate a pytest-generated JUnit file ```bash export JJI_SERVER=http://localhost:8000 export JJI_AI_PROVIDER=claude export JJI_AI_MODEL=your-model-name pytest --junitxml=report.xml --analyze-with-ai ``` The repository includes a ready-to-copy pytest helper for this flow. It posts the generated XML to JJI and rewrites the XML file only when `enriched_xml` comes back. - `JJI_TIMEOUT` controls the helper's HTTP timeout and defaults to `600` seconds. - Invalid `JJI_TIMEOUT` values fall back to `600`. - The helper skips enrichment on dry runs, all-pass runs, or when `--junitxml` or the required `JJI_*` settings are missing. ### Output behavior worth knowing - `POST /analyze-failures` is synchronous. You get a final `completed` or `failed` result in the response body right away. - If the XML has no failing testcases, JJI returns `completed`, keeps the original XML, and sets the summary to `No test failures found in the provided XML.` - If several tests share the same failure signature, one analysis is reused across that group instead of re-running the model for every testcase. - `enriched_xml` is only returned when you send `raw_xml`. - If the server does not set `PUBLIC_BASE_URL`, `result_url` and XML `report_url` are relative paths. - `raw_xml` accepts payloads up to `50,000,000` characters. ## Troubleshooting - `400 No AI provider configured` or `400 No AI model configured`: send `ai_provider` and `ai_model` in the request, or configure server defaults with `AI_PROVIDER` and `AI_MODEL`. - `400 Invalid XML`: the string in `raw_xml` is not valid XML. - `422` before analysis starts: you sent both `failures` and `raw_xml`, sent neither, or used `failures: []`. - HTTP 200 but analysis still failed: this endpoint reports runtime failures in the JSON `status` and `summary`. Check those fields before you overwrite any XML artifacts. - No `enriched_xml` in the response: you used `failures` mode instead of `raw_xml`, or the request finished with `status: "failed"`. - `result_url` is relative: the server is running without `PUBLIC_BASE_URL`. Use it as a path on that server, or configure a public base URL if you need absolute links. ## Related Pages - [Running Your First Analysis](quickstart.html) - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) - [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) - [REST API Reference](rest-api-reference.html) --- Source: monitoring-and-rerunning-analyses.md # Monitoring and Re-Running Analyses Track queued analyses until they finish, then launch another pass when the first run fails, times out, or needs different AI settings. This lets you recover from transient problems and compare reruns without losing the original result. ## Prerequisites - A running JJI server that you can reach from the web app or the `jji` CLI. - An analysis you want to follow, or permission to start one. See [Running Your First Analysis](quickstart.html). - For the web app, a saved username/profile so you can open the Dashboard. > **Note:** This page covers queued Jenkins-backed analyses. For direct XML or raw failure analysis, see [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html). ## Quick Example ```bash jji analyze --job-name "folder/job-name" --build-number 123 --provider claude --model "" jji status jji re-analyze ``` Use the `job_id` printed by the first command. The rerun keeps the same Jenkins job and build, but creates a new JJI job ID so the original result stays intact. > **Warning:** `jji re-analyze` reruns with the original settings. If you need to change timeout, model, peer review, repository context, Jira search, or artifact collection, use the web UI's `Re-Analyze` button instead. ## Step-by-Step 1. Queue the analysis and keep the job ID. ```bash jji analyze --job-name "folder/job-name" --build-number 123 --provider claude --model "" ``` The response is immediately `queued` and includes a poll link you can open in the browser. > **Note:** `queued` is the submit response. Once the run is stored, you will normally see `pending` or `waiting`, then `running`, and finally `completed` or `failed`. ```mermaid flowchart LR Submit[Submit analysis] --> Queued[queued] Queued --> Pending[pending] Queued --> Waiting[waiting] Pending --> Running[running] Waiting --> Running[running] Running --> Completed[completed] Running --> Failed[failed] Waiting --> Failed[failed] Completed --> Rerun[Re-Analyze] Failed --> Rerun Rerun --> Queued ``` 2. Monitor the run from the CLI or the Dashboard. ```bash jji status jji results dashboard ``` Use `jji status` when you care about one run. Use `jji results dashboard` or the web Dashboard when you want a live overview of multiple jobs, including failure counts, review progress, comments, and child-job counts. | What you see | Where you see it | What to do next | | --- | --- | --- | | `queued` | Submit or rerun response | Save the `job_id` and start monitoring | | `pending` | Dashboard, status page, `jji status` | JJI has accepted the job and has not started AI work yet | | `waiting` | Dashboard, status page, `jji status` | JJI is waiting for Jenkins to finish before analysis starts | | `running` | Dashboard, status page, `jji status` | AI analysis is in progress | | `completed` | Dashboard, report page, `jji status` | Open the finished report | | `failed` | Dashboard, status page, `jji status` | Read the error and decide whether to rerun | | `Timed Out` | Web UI only | The AI analysis timed out; rerun with a higher AI timeout | > **Tip:** The Dashboard and status page refresh automatically, so they are the easiest way to follow a live run. 3. Open the finished result. ```bash jji results show ``` In the browser, click the row in the Dashboard. Active jobs open the status view; completed jobs open the full report. See [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) for what to do after the report is ready. 4. Rerun the same analysis quickly. ```bash jji re-analyze ``` This is the fastest retry path when you want another pass with the same settings. JJI creates a brand-new analysis record and leaves the original one untouched. 5. Rerun with adjusted settings from the web UI. - Open the failed run's status page or the completed run's report page. - Click `Re-Analyze`. - Change only the fields you need, such as AI provider or model, AI CLI timeout, raw prompt, peer review, repository context, Jira search, or artifact collection. - Submit the rerun and monitor the new job from the Dashboard or the status page. > **Tip:** The `Re-Analyze` dialog starts from the original run's settings, so you usually only need to change the field that caused the rerun, such as a higher AI timeout or a different model. See [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) for repository, prompt, and artifact guidance. See [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) for peer analysis settings. ## Advanced Usage | If you need to... | Best option | | --- | --- | | Retry immediately with the same settings | Run `jji re-analyze ` | | Change AI provider, model, or AI timeout | Use `Re-Analyze` in the web UI | | Add or change repo context, prompt, Jira search, or artifact download settings | Use `Re-Analyze` in the web UI | | Enable or retune peer review | Use `Re-Analyze` in the web UI | | Change Jenkins wait behavior such as wait/no-wait, poll interval, or max wait | Start a fresh analysis with new analyze options | The live status page can show more than a single state. On longer or more complex runs, it can step through waiting for Jenkins, failure analysis, child-job analysis, Jira search, peer review rounds, and the final save step. > **Warning:** Re-analysis does not change Jenkins wait behavior. If the error says it timed out waiting for Jenkins, submit a new analysis with different wait settings instead of relying on `Re-Analyze`. ## Troubleshooting - `Re-Analyze` is missing or unavailable: the job is still active, or the original result does not have reusable saved settings. - The web UI shows `Timed Out`, but the CLI shows `failed`: that is expected. `Timed Out` is a web UI label for AI-analysis timeouts. - The run says it timed out waiting for Jenkins: that is a Jenkins wait timeout, not an AI timeout. Start a fresh analysis with different wait settings. - A rerun did not help: make sure you changed the setting that matches the failure. Raise AI timeout for AI stalls, switch model/provider for weak analysis, or update repository context when the model lacked enough code or artifact evidence. - The server restarted while a job was live: jobs that were still waiting on Jenkins resume automatically, but jobs already in `pending` or `running` are marked failed and should be rerun. ## Related Pages - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) - [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) - [Running Your First Analysis](quickstart.html) --- Source: reviewing-commenting-and-reclassifying-failures.md # Reviewing, Commenting, and Reclassifying Failures Use the report page to turn an AI-generated failure analysis into a reviewed team record. You do this so the next person sees which failures are understood, what context matters, and where human judgment overruled the AI. ## Prerequisites - You can open a completed analysis report in the web UI. See [Running Your First Analysis](quickstart.html) if you need one. - You have already saved a username in the UI, so your reviews and comments are attributed to you. ## Quick Example 1. Open a completed report from the dashboard. 2. Expand a failure card that shows `+2 more with same error`. 3. Click `Review All (0/3)`. 4. Type a note in `Add a comment...` and press `Post`. 5. Change the classification dropdown to `INFRASTRUCTURE`. 6. Confirm the sticky header changes from `Needs Review` to `3/3 Reviewed`. > **Tip:** Press `Enter` to post a comment. Use `Shift+Enter` for a multiline note. ```mermaid flowchart LR A[Open completed report] --> B[Expand a failure card] B --> C[Inspect error and AI analysis] C --> D[Mark review complete] C --> E[Leave a team comment] C --> F[Override the classification] D --> G[Saved report reflects human review] E --> G F --> G ``` ## Step-by-Step 1. Open the failure card you want to review. - Look for `+N more with same error` when several tests were grouped under the same root cause. - Expand the card to inspect `Error`, `Analysis`, `Artifacts Evidence`, and, for grouped cards, `Affected Tests`. 2. Mark the failure as reviewed. - On a single-test card, click `Review`. - On a grouped card, click `Review All (x/y)` to mark the whole group at once, or use the smaller `Review` buttons next to individual tests. - Click `Reviewed` again if you need to clear the mark. 3. Add a comment for your team. - Type into `Add a comment...` and click `Post`. - Each comment shows the author and timestamp. - If the comment was posted under your current username, a trash icon appears on hover so you can delete it. 4. Correct the classification when the AI got it wrong. - Use the dropdown at the bottom of the card. - The available overrides are `CODE ISSUE`, `PRODUCT BUG`, and `INFRASTRUCTURE`. | Choose this | Use it when | What you will see next | | --- | --- | --- | | `CODE ISSUE` | The fix belongs on the code or test side | Any `Bug Report` section disappears; `Suggested Fix` stays visible if one was generated | | `PRODUCT BUG` | The product behavior is the real problem | Any `Suggested Fix` section disappears; `Bug Report` stays visible if one was generated | | `INFRASTRUCTURE` | The failure came from Jenkins, the lab, the environment, or another external dependency | Both `Suggested Fix` and `Bug Report` disappear | > **Warning:** On a grouped card, changing the classification updates the whole group, not just the representative test name shown in the header. 5. Verify that your changes stuck. - The sticky header shows overall progress as `Needs Review`, `x/y Reviewed`, or `Reviewed`. - There is no final `Save` button. Review toggles, comments, and classification changes are saved as you make them. ## Advanced Usage Grouped cards change the scope of your actions, not just the layout. | Action | Single failure card | Grouped failure card | | --- | --- | --- | | Review | Marks one test | `Review All` marks the full group, or you can review tests individually | | Comment | Adds one comment | Posts the same comment to every test in the group | | Classification | Changes one test | Applies the override across the full group | > **Note:** Because grouped comments are stored per affected test, posting once on a large group can create several comment entries with the same text. That is expected. If one grouped card shows more than one classification badge, the tests inside that group already have mixed saved overrides. Choosing a new value from the dropdown is the quickest way to standardize the group again. For pipeline results, expand `Child Jobs` and make the change inside the exact child build you mean to update. The same test name can appear in more than one child build, and each build keeps its own review state, comments, and classification override. Comments and review state refresh automatically, usually about every 30 seconds. While you are typing a draft comment, that refresh pauses so your in-progress note is not interrupted. > **Tip:** Paste a full URL into a comment when you want a clickable link to a Jenkins console page, a runbook, or another external reference. ## Troubleshooting - I do not see a delete icon for my comment: switch back to the same username that was active when the comment was posted. - My comment appeared several times: you likely posted it on a grouped card, so JJI stored one copy for each affected test. - I saw `Posted X/Y. Failed: ...`: some copies were saved and some were not. Press `Post` again without editing the text to retry only the failed tests. - I saw `Failed to update...` or `Failed to save...`: some tests in the group did not change. Retry from the same card; the message names the failures. - Not every similar failure changed when I reclassified one card: if the same test name appears in another child build, reclassify it in that child build too. - I am not seeing a teammate's latest note yet: wait for the automatic refresh cycle, or reload the page when you are not typing in a comment box. - I am not seeing a teammate's new classification yet: refresh the page. Comments and review state auto-refresh, but classification changes still require a reload for other open viewers. ## Related Pages - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [Tracking Failure History](tracking-failure-history.html) - [Creating GitHub Issues and Jira Bugs](creating-github-issues-and-jira-bugs.html) - [Managing Your Profile and Personal Tokens](managing-your-profile-and-personal-tokens.html) - [Pushing Classifications to Report Portal](pushing-classifications-to-report-portal.html) --- Source: managing-your-profile-and-personal-tokens.md # Managing Your Profile and Personal Tokens Set the username JJI should show for you, save your own GitHub and Jira credentials, and validate them so report follow-up runs as you. Once this is set up, JJI uses your personal tracker credentials instead of shared server credentials and adds your username to the attribution it includes when it files a tracker item for you. ## Prerequisites - Access to the JJI web UI. - A username you want JJI to use for comments, reviews, and issue attribution. - A GitHub personal access token with `repo` scope if you want GitHub issue creation. - For Jira Cloud, your Atlassian email plus an API token. For Jira Server/Data Center, a personal access token. - A completed analysis report if you want to test issue creation right away. Need one first? See [Running Your First Analysis](quickstart.html). ## Quick Example ```bash jji validate-token github --token "" ``` If that succeeds, open `/register` or `Settings`, enter your username, paste the same value into `GitHub Token`, and click **Save**. Then open a completed report and choose `GitHub Issue` to file under your own GitHub account. | What you save | What you can do | | --- | --- | | Username only | Use JJI normally and preview generated issue or bug text. | | Username plus a personal tracker token | Create GitHub issues or Jira bugs directly from the report UI as your own tracker account. | > **Note:** You can still preview generated content without personal tokens, but the final create action in the report UI stays disabled until the matching token is saved. ## Step-by-Step 1. Open your profile page. On first use, go to `/register`. After that, use the `Settings` icon in the user badge. > **Note:** Leave `API Key` blank unless you need admin access. It is separate from your GitHub and Jira tokens. For admin-specific access, see [Managing Admin Users and API Keys](managing-admin-users-and-api-keys.html). 2. Enter your username. This is the name JJI uses for comments, review actions, and the `Reported by: via jenkins-job-insight` line it adds when it creates a tracker item for you. 3. Fill in only the tracker fields you need. | You want to create | Fill in | Credential to use | | --- | --- | --- | | GitHub issue | `GitHub Token` | GitHub personal access token with `repo` scope | | Jira Cloud bug | `Jira Email` and `Jira Token` | Atlassian account email and API token | | Jira Server/Data Center bug | `Jira Token` | Personal access token, with `Jira Email` left blank | > **Warning:** For Jira Cloud, `Jira Email` is required. If you leave it blank, JJI treats the token like a non-Cloud token and Jira Cloud validation or bug creation can fail. > **Tip:** GitHub and Jira are independent. Save only the credentials you actually use, and clear any field you want to stop using before you click **Save**. 4. Click **Save** and let JJI validate the new credentials. There is no separate validation step in the web UI. Saving a new or changed token triggers validation automatically, and a successful check shows `Authenticated as ...`. 5. Test the result from a report. Open a completed analysis report and choose `GitHub Issue` or `Jira Bug`. JJI generates a preview first, lets you edit the title and body, and then submits with the matching personal token from `Settings`. ## Advanced Usage ### Validate tokens from the CLI ```bash jji validate-token github --token "" jji validate-token jira --token "" --email "you@example.com" ``` Use this when you want a quick pass/fail check before updating the UI, or when you want something scriptable. For Jira Server/Data Center, omit `--email`. If you create issues from the CLI instead of the web UI, pass your personal credentials per command with `--github-token`, `--jira-token`, `--jira-email`, `--jira-project-key`, and `--jira-security-level`. ### Check which Jira projects and security levels your account can use ```bash jji jira-projects --jira-token "" --jira-email "you@example.com" --query PROJ jji jira-security-levels PROJ --jira-token "" --jira-email "you@example.com" ``` This is useful when the bug dialog needs a project key or optional security level and you want to confirm what your own Jira account can access. For Jira Server/Data Center, omit `--jira-email`. ### How your saved profile is used ```mermaid sequenceDiagram participant You participant UI as JJI Web UI participant Server as JJI Server participant Tracker as GitHub or Jira You->>UI: Enter username and personal token UI->>Tracker: Validate token UI->>Server: Sync saved credentials Server->>Server: Store credentials encrypted at rest You->>UI: Create issue from a report UI->>Tracker: Create using your personal credentials Tracker-->>UI: Return issue URL UI->>Server: Add a comment linking the new issue ``` JJI keeps your active profile in the browser and syncs saved tracker credentials to the server so they can be restored later for the same username. If you switch to a different username, save the matching credentials again for that profile. ## Troubleshooting - **`Invalid token` or `Invalid token (HTTP 401/403)` while saving:** the token is wrong or expired. Generate a new one and try again. - **Jira validation or creation fails even though the token looks right:** for Jira Cloud, make sure `Jira Email` matches your Atlassian account. For Jira Server/Data Center, leave `Jira Email` blank and use a personal access token. - **You see `Jira URL not configured on server`:** your administrator needs to finish Jira setup on the JJI server. Your personal token alone cannot fix this. - **The `GitHub Issue` or `Jira Bug` action stays disabled after you saved a token:** the server may have GitHub or Jira issue creation turned off. Run `jji capabilities --json` or ask your administrator to confirm the integration is enabled. - **Your tokens seem to disappear after you change usernames:** server-side saved credentials are tied to the username you selected. Switch back to the old username or save the credentials again under the new one. - **GitHub issue creation says no repository URL is available:** the analysis did not have a usable repository target, so JJI has nowhere to open the issue. Re-run the analysis with repository context available. ## Related Pages - [Creating GitHub Issues and Jira Bugs](creating-github-issues-and-jira-bugs.html) - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Managing Admin Users and API Keys](managing-admin-users-and-api-keys.html) - [CLI Command Reference](cli-command-reference.html) - [Running Your First Analysis](quickstart.html) --- Source: tracking-failure-history.md # Tracking Failure History Use failure history when you want to answer two practical questions quickly: have we seen this failure before, and does it keep coming back in the same way? JJI helps you search stored failures, open one test's record, and reuse earlier labels and comments so you can separate flaky noise from a recurring product or infrastructure problem faster. ## Prerequisites - JJI is running and you already have at least one completed analysis with stored results. See [Running Your First Analysis](quickstart.html). - For the web UI, sign in first. See [Managing Your Profile and Personal Tokens](managing-your-profile-and-personal-tokens.html). - The CLI examples assume `jji` is already configured. If it is not, add `--server ` to the commands. ## Quick Example ```bash jji history failures --search "DNS resolution failed" jji history test "tests.network.TestDNS.test_lookup" --job-name "ocp-e2e" ``` The first command finds matching historical failures across JJI. The second drills into one exact test so you can check how often it has failed in `ocp-e2e`, what classifications were saved, and whether earlier comments already point to a bug or fix. | If you want to... | Fastest path | | --- | --- | | Browse recent failures and click into a result | Open `History` in the web UI | | Inspect one exact test over time | `jji history test ... --job-name ...` | | Page through a large filtered result set | `jji history failures ... --limit ... --offset ...` | ## Step-by-Step ```mermaid flowchart LR A[Open History] --> B[Search or filter failures] B --> C[Open a test] C --> D[Check labels, timestamps, and comments] D --> E[Open a linked result for full context] ``` 1. Open `History` in the web UI. 2. Narrow the list until you see the failure you care about. - Use the search box for part of a test name, a stored error string, or a job name. - Use the classification filter when you want to focus on labels such as `PRODUCT BUG`, `CODE ISSUE`, `FLAKY`, `REGRESSION`, `INFRASTRUCTURE`, `KNOWN_BUG`, or `INTERMITTENT`. - The newest entries appear first, and you can sort the current page by test name, job, classification, or date. 3. Open the right level of detail. - Click the test name to open that test's history page. - Click the row itself to open the original analysis result for that specific job. 4. Read the single-test view to decide whether the problem is repeating. - Check `First seen` and `Last seen` to see how long the failure has been around. - Look at the classification breakdown to see whether earlier triage kept landing on the same label. - Use `Recent Runs` to jump straight to matching analyzed failures. - Read the `Comments` section for earlier notes such as `Opened bug: OCPBUGS-12345` or `Fix PR merged`. > **Note:** The web UI test page is centered on recorded failures. If you need build totals, pass counts, or a failure-rate estimate for one Jenkins job, use `jji history test ... --job-name ...`. 5. Use what you found to make a faster triage decision. - Repeated `PRODUCT BUG` or `INFRASTRUCTURE` labels across several results usually point to a systemic issue. - Repeated `FLAKY` or `INTERMITTENT` labels usually point to unstable tests or environment noise. - If you need a fresh run to confirm the pattern, see [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html). - If the history points to a new bug that should be tracked, see [Creating GitHub Issues and Jira Bugs](creating-github-issues-and-jira-bugs.html). ## Advanced Usage ### Scope one test to one Jenkins job ```bash jji history test "tests.network.TestDNS.test_lookup" --job-name "ocp-e2e" ``` Add `--job-name` whenever you want an estimated `Failures` / `Total Runs` view for one job. Without it, JJI can still show recorded failure history, but it cannot calculate a reliable pass count. ### Page through a large filtered history list ```bash jji history failures --job-name "ocp-e2e" --classification "FLAKY" --limit 50 --offset 50 ``` Use this when the web UI list is too broad or when you want exact job scoping. `--search`, `--job-name`, and `--classification` can be combined. ### Exclude the run you are currently triaging ```bash jji history test "tests.network.TestDNS.test_lookup" --job-name "ocp-e2e" --exclude-job-id "job-99" ``` This is useful when you want to compare the current failure against older history without letting the current run change the totals. ### Pivot from an existing failure signature ```bash jji --json history search --signature "sig-dns" ``` Use this when another JJI command or JSON result already gave you an `error_signature` and you want to see every test that has failed with the same stored signature. JSON output is the easiest way to capture the matching tests and any saved comments tied to that signature. > **Tip:** If a test history page already has useful comments, start there before you re-open a stack of older results one by one. > **Note:** Older completed analyses are also loaded into failure history automatically when JJI starts, so past results can appear after a restart even if you are opening `History` for the first time. ## Troubleshooting - **The single-test page shows `N/A` for failure rate, or the totals look smaller than expected.** The web UI page is failure-centered. Use `jji history test "..." --job-name "..."` to estimate totals across completed builds of one job. - **History looks empty.** Make sure you have at least one completed analysis. If you just started or restarted JJI, refresh after startup finishes so previously stored results have time to appear. - **I need to narrow history to one Jenkins job in the UI.** Use `jji history failures --job-name "..."` from the CLI. The web UI list does not expose a job-name filter. - **I only see failure-centered history, not a full pass timeline.** That is expected in the current history views. Use the job-scoped CLI form when you need estimated totals across completed builds. ## Related Pages - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [Creating GitHub Issues and Jira Bugs](creating-github-issues-and-jira-bugs.html) - [CLI Command Reference](cli-command-reference.html) - [REST API Reference](rest-api-reference.html) --- Source: creating-github-issues-and-jira-bugs.md # Creating GitHub Issues and Jira Bugs Use this flow when you want to turn a failed test into tracked follow-up work without copying analysis details by hand. JJI drafts the issue text from the report, lets you adjust the destination and wording, and adds the created link back to the same failure so the trail stays in one place. ## Prerequisites - A completed analysis report. If you need one, see [Running Your First Analysis](quickstart.html). - A saved username in `Settings`. - A saved `GitHub Token` or `Jira Token` in `Settings`. Jira Cloud also needs `Jira Email`. - The tracker you want to use must be available in your JJI deployment. ## Quick Example ```text completed report -> expand failure -> GitHub Issue -> edit Title/Body -> Create GitHub Issue ``` 1. Open a completed report and expand the failure you want to track. 2. Click `GitHub Issue`. 3. If `Repository` is shown, choose the repo you want. 4. Review the generated draft, edit `Title` or `Body`, then click `Create GitHub Issue`. 5. Use `Open GitHub Issue`, then return to the report to confirm the new follow-up link was added to that failure. > **Note:** The Jira flow is the same, but you click `Jira Bug` and choose a `Jira Project` before creating it. > **Note:** You can open the preview before saving a tracker token, but JJI does not enable the final create button until the matching token is saved in `Settings`. ```mermaid sequenceDiagram participant You participant Report as Analysis Report participant JJI participant Tracker as GitHub or Jira You->>Report: Open failure and choose tracker Report->>JJI: Request draft JJI->>Tracker: Check for similar issues Tracker-->>JJI: Matches when available JJI-->>Report: Draft title and body You->>Report: Pick destination and edit final text Report->>JJI: Create issue or bug JJI->>Tracker: Submit final draft Tracker-->>JJI: Created URL JJI-->>Report: Success link and report comment ``` ## Step-by-Step 1. Open a completed analysis report. Expand the failure card you want to file. The tracker actions live on each expanded failure card, not on the summary view. 2. Choose the tracker. | If you want to file in | Click | Pick before creating | | --- | --- | --- | | GitHub | `GitHub Issue` | `Repository` when more than one repo is available | | Jira | `Jira Bug` | `Jira Project`, and optionally `Security Level` | > **Warning:** If a tracker button is greyed out, that tracker is not available in your current deployment. 3. Review the generated preview. JJI fills in `Title` and `Body` automatically from the failure details. When it can find possible duplicates, it shows them above the editor with links and current status so you can decide whether to create a new item or reuse an existing one. 4. Pick the destination. For GitHub, use `Repository` when the dialog offers more than one repo. If no repo picker appears, there is no repo choice to make in the dialog. For Jira, type at least two characters in `Jira Project` to search accessible projects, then pick the right project from the list. 5. Optionally set Jira visibility. After you pick a Jira project, `Security Level` can show project-specific visibility choices. Leave it empty if you want the bug to use the project's normal visibility. 6. Edit the final text. Update `Title` and `Body` before you submit. Turn on `Include links` when you want the draft to reference the Jenkins build and the JJI report. 7. Create the follow-up work. Click `Create GitHub Issue` or `Create Jira Bug`. JJI shows a success link, and it also adds a comment back on the same failure so the report keeps the tracker URL with the analysis. ## Advanced Usage - If the failure card shows `AI for issue generation`, you can choose a different provider and model before opening the dialog. If draft generation fails, JJI still builds a structured draft from the existing analysis so you can keep working. - JJI shapes the draft from the current failure classification. If you change the classification first, the next preview uses that updated classification. - The GitHub `Repository` picker only appears when the report gives you more than one repo to choose from. Otherwise there is no repo selection step in the dialog. - A Jira project key may already be filled in for you. Project suggestions narrow as you type, and security levels appear only after a project is selected. - `Include links` produces clickable report links only when your deployment has a public external URL. Otherwise JJI falls back to plain-text references. - On grouped failure cards, JJI builds the draft from the test shown at the top of the card. ## Troubleshooting - `Create GitHub Issue` or `Create Jira Bug` is disabled: save the matching token in `Settings`. For Jira Cloud, also save `Jira Email`. - The dialog opens, but preview fails with a tracker-disabled or configuration message: your admin needs to finish enabling that tracker on the server. - GitHub creation fails because no repository is available: JJI does not have a GitHub destination for this report. Run the analysis again with the right repository information, or ask an admin to configure a default GitHub repo. See [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) for details. - Jira project search or `Security Level` is empty: check that your Jira token is valid, add `Jira Email` for Jira Cloud, and select a project before looking for security levels. - Your GitHub or Jira token is invalid or expired: update it in `Settings` and try again. - The similar issues list is empty: duplicate lookup is best effort, so you can still review the draft and create the issue manually. ## Related Pages - [Managing Your Profile and Personal Tokens](managing-your-profile-and-personal-tokens.html) - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Tracking Failure History](tracking-failure-history.html) - [CLI Command Reference](cli-command-reference.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) --- Source: improving-analysis-with-repository-context.md # Improving Analysis with Repository Context You want the AI to inspect the same repositories, prompts, and build evidence your team uses so it can point to the right files and quote the right evidence instead of guessing from stack traces alone. The best results usually come from combining a main repo, any supporting repos, a short run-specific prompt, and Jenkins artifacts when the proof lives outside the test report. Prerequisites: - `jji` is already configured to talk to your JJI server. - The machine running JJI can clone the repositories you reference. - Your Jenkins account can read build artifacts if you want artifact-based evidence. ## Quick Example ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --tests-repo-url https://github.com/org/tests \ --additional-repos "infra:https://github.com/org/infra" \ --raw-prompt "Focus on the failing code path and cite artifact evidence when it exists." \ --jenkins-artifacts-max-size-mb 50 ``` This gives JJI one main repository, one supporting repository, temporary instructions for this run, and a larger artifact budget for evidence collection. | Add this | What it improves | Best for | | --- | --- | --- | | `--tests-repo-url` | Main code and test context | Finding the exact file or branch involved | | `--additional-repos` | Cross-repo context | Shared libraries, infra repos, or split codebases | | `--raw-prompt` | Run-specific guidance | Temporary instructions you do not want to commit | | `--get-job-artifacts` and `--jenkins-artifacts-max-size-mb` | File and log evidence | Failures explained by generated files, logs, or archives | ```mermaid flowchart LR A["`jji analyze`"] --> B["Main repo"] A --> C["Extra repos"] A --> D["Run-specific prompt"] A --> E["Jenkins artifacts"] B --> F["AI has better context"] C --> F D --> F E --> F F --> G["More accurate code paths and evidence"] ``` ## Step-by-Step 1. Start with the main repository the AI should inspect. ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --tests-repo-url https://github.com/org/tests ``` If you give JJI a repository URL, it clones that repo before analysis starts. If you do not, analysis still runs, but it has to rely on the Jenkins test report, console output, and any artifact evidence instead of source code. > **Tip:** You can pin a branch or tag by appending `:ref`, such as `https://github.com/org/my-tests:develop` or `https://github.com/org/repo.git:feature-x`. 2. Add supporting repositories when the failing path crosses repo boundaries. ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --tests-repo-url https://github.com/org/tests \ --additional-repos "infra:https://github.com/org/infra,product:https://github.com/org/product" ``` Use short, unique names such as `infra` or `product`. Those names identify each extra repository during analysis, so they should be stable and easy to recognize. 3. Add one-off instructions with `--raw-prompt`. ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --tests-repo-url https://github.com/org/tests \ --raw-prompt "Check shared fixtures before calling this a product bug. Prefer quoting log or artifact lines over summarizing them." ``` Use `--raw-prompt` when you want to steer a single run without changing any repository content. This is the right tool for incident-specific instructions, temporary experiments, or a one-time request to bias the analysis toward a particular kind of evidence. 4. Keep Jenkins artifacts available when the proof is in logs, archives, or generated files. ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --get-job-artifacts \ --jenkins-artifacts-max-size-mb 50 ``` Artifact download is enabled by default for Jenkins-backed analyses, so most of the time you only need to change the size cap. If artifacts are noisy, unavailable, or too expensive to download, turn them off explicitly: ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --no-get-job-artifacts ``` > **Note:** Artifact settings apply to Jenkins-backed analysis. They do not apply when you send raw failures or JUnit XML directly. 5. Save the repository defaults you use all the time. ```toml [default] server = "dev" [defaults] tests_repo_url = "https://github.com/org/tests" [servers.dev] url = "http://localhost:8000" additional_repos = "infra:https://github.com/org/infra,product:https://github.com/org/product" ``` Put this in `~/.config/jji/config.toml` to avoid repeating the same repository context on every run. Per-run CLI flags still win, so you can keep stable defaults and override them when a single analysis needs a different branch, repo, or prompt. > **Tip:** The config file is a good home for `tests_repo_url` and `additional_repos`. Keep `--raw-prompt` for per-run guidance instead of trying to make it a saved default. ## Advanced Usage Use branch or tag refs on both the main repo and extra repos when the failure only makes sense on a non-default line of development. ```bash jji analyze \ --job-name my-job \ --build-number 1 \ --tests-repo-url https://github.com/org/my-tests:develop \ --additional-repos "infra:https://gitlab.internal:8443/org/infra:main" ``` The repository URL must use `https://` or `git://`. JJI rejects `ssh://` and `file://` repository URLs. For long-lived project guidance, add a `JOB_INSIGHT_PROMPT.md` file at the root of a cloned repository. When that file exists, JJI tells the AI to read it alongside the rest of the repository context. > **Warning:** `JOB_INSIGHT_PROMPT.md` is only auto-discovered at the cloned repository root, not in subdirectories. If you already have a result and want to improve the next run without changing saved defaults, use the report page's `Re-Analyze` action. The dialog lets you adjust `Tests Repo URL`, `Ref / Branch`, `Additional Repositories`, `Raw Prompt`, and `Jenkins Artifacts` before queuing a new analysis. See [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) for details. The same repository and prompt settings also work when you analyze raw failures or JUnit XML directly. Artifact download does not, because there is no Jenkins build to fetch from. See [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) for details. Use this quick rule of thumb when deciding where to save each setting: | Setting | Save in `~/.config/jji/config.toml` | Set per run | Set as a server default | | --- | --- | --- | --- | | Main repo | Yes | Yes | Yes | | Additional repos | Yes | Yes | Yes | | Raw prompt | No | Yes | No | | Artifact download on/off | No | Yes | Yes | | Artifact size cap | No | Yes | Yes | For the full list of flags and server-side defaults, see [CLI Command Reference](cli-command-reference.html) and [Configuration and Environment Reference](configuration-and-environment-reference.html) for details. ## Troubleshooting - If `--additional-repos` is rejected, use comma-separated `name:url` pairs such as `infra:https://github.com/org/infra`. Each name must be unique. - If JJI inspects the wrong branch, add `:ref` to the repo URL instead of assuming the default branch matches the failing run. - If the result has little or no artifact evidence, check whether the build actually published artifacts, whether artifact download was disabled, whether your Jenkins account can read them, and whether the size cap is too low. - If repository cloning fails immediately, switch to `https://` or `git://` URLs. `ssh://` and `file://` URLs are not accepted. ## Related Pages - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [Adding Peer Review with Multiple AI Models](adding-peer-review-with-multiple-ai-models.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) --- Source: adding-peer-review-with-multiple-ai-models.md # Adding Peer Review with Multiple AI Models You want difficult failures to get a second opinion before you trust the AI's triage. Peer review keeps your normal main model in place and adds other models that can confirm the result or force another pass when the first answer looks shaky. ## Prerequisites - A working JJI setup and a successful single-model analysis flow. See [Running Your First Analysis](quickstart.html). - A primary AI provider and model already configured for normal analyses. - Every peer provider you plan to use must be installed and authenticated where JJI runs. ## Quick example ```bash jji analyze \ --job-name my-job \ --build-number 42 \ --peers "cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro" \ --peer-analysis-max-rounds 3 ``` This runs the usual analysis, adds two peer reviewers, and lets the discussion continue for up to three rounds before JJI accepts the final triage. ## Step-by-step 1. Choose where peer review should be the default. | Scope | Use this when | What to set | | --- | --- | --- | | Server default | Every caller should inherit the same peer reviewers | `PEER_AI_CONFIGS` and `PEER_ANALYSIS_MAX_ROUNDS` | | CLI profile default | Your `jji` runs should reuse the same peers | `peers` and `peer_analysis_max_rounds` in `~/.config/jji/config.toml` | | One run only | You want to override peers for a single analysis | `--peers` and `--peer-analysis-max-rounds` | Set server defaults in `.env` or your container environment: ```dotenv AI_PROVIDER=claude AI_MODEL=your-model-name PEER_AI_CONFIGS=cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro PEER_ANALYSIS_MAX_ROUNDS=3 ``` Or set a personal CLI default in `~/.config/jji/config.toml`: ```toml [defaults] peers = "cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro" peer_analysis_max_rounds = 3 ``` > **Note:** The main analysis still uses your primary `AI_PROVIDER` and `AI_MODEL`. Peer reviewers challenge that result; they do not replace your main model. 2. Run the analysis. If you already set defaults, a normal `jji analyze` command will use them: ```bash jji analyze \ --job-name my-job \ --build-number 42 ``` If you want a one-off override, add the peer flags: ```bash jji analyze \ --job-name my-job \ --build-number 42 \ --peers "cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro" \ --peer-analysis-max-rounds 5 ``` `--peers` uses a comma-separated `provider:model` list. `--peer-analysis-max-rounds` accepts `1` through `10`, and the default is `3`. > **Tip:** The same `provider:model,provider:model` format is used in `PEER_AI_CONFIGS`, `peers`, and `--peers`. 3. Follow the debate while the run is active. ```bash jji status JOB_ID ``` The CLI prints a `Poll:` URL when you queue the job. Open that URL in the browser to see the live status page, including the main AI, the peer models, and progress messages for each review and revision round. ```mermaid flowchart LR A[Main AI analyzes the failure] --> B[Peer review round] B -->|Consensus| C[Result saved] B -->|No consensus and rounds remain| D[Main AI revises] D --> E[Another peer review round] E -->|Consensus| C E -->|Max rounds reached| F[Best available result saved] ``` 4. Review the outcome after completion. ```bash jji results show JOB_ID --full ``` In the browser report, look for the `Peer Analysis` section. It shows whether each debated failure reached `Consensus` or `No Consensus`, how many rounds were used, and a round-by-round timeline of what each model said. For the manual review flow after the AI finishes, see [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html). 5. Re-run with different peers when a hard failure still looks unclear. ```bash jji re-analyze JOB_ID ``` The CLI shortcut repeats the stored settings. If you want to change the peer list, turn peer review off for just the rerun, or adjust max rounds, use the report page's `Re-Analyze` dialog and submit a new analysis from there. ## Advanced Usage Use this command to see provider/model pairs that have already produced successful analyses in your environment: ```bash jji ai-configs ``` That is the fastest way to pick peer models that already work with your current runtime and authentication setup. Use rounds to control how much debate you want: | Rounds | What changes | | --- | --- | | `1` | One peer-review pass only | | `3` | Default behavior | | Higher values | More chances for the main AI to revise after disagreements | | `10` | Highest allowed limit | From round `2` onward, each peer sees the other peers' previous responses. Extra rounds are most useful when the first round disagrees, not when every model already agrees immediately. You can add more than one peer by listing more `provider:model` entries. JJI also reuses one peer debate across failures that share the same underlying error, so repeated failures with the same signature do not each start a separate discussion. ## Troubleshooting - If `--peers` or `peers` is rejected, use `provider:model,provider:model` format and remove any trailing comma. - If you get an unsupported-provider error, use the provider names supported by your JJI install, such as `claude`, `gemini`, or `cursor`, and make sure that provider's CLI is available in the same runtime. - If `--peer-analysis-max-rounds` is rejected, use a value from `1` to `10`. - If the run is much slower than usual, remember that each round calls every peer and may trigger another main-model revision. - If the report has no `Peer Analysis` section, peers were not enabled for that run, or the Jenkins job had no test report and fell back to console-only analysis. - If the run completes but the peers did not help, JJI keeps the main AI result when all peer calls fail. Check peer authentication and model names first. ## Related Pages - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) - [Monitoring and Re-Running Analyses](monitoring-and-rerunning-analyses.html) - [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) --- Source: pushing-classifications-to-report-portal.md # Pushing Classifications to Report Portal Use this workflow when you want the classifications you already have in JJI to show up on the matching Report Portal failures, so your triage stays in sync without relabeling items by hand. It also works for child-job runs inside pipeline-style analyses. ## Prerequisites - A completed JJI analysis and its `job_id`. See [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) if you need to create one first. - JJI running with `REPORTPORTAL_URL`, `REPORTPORTAL_PROJECT`, `REPORTPORTAL_API_TOKEN`, and `PUBLIC_BASE_URL` set on the server. - A Report Portal token that is allowed to update the target launch. - If your Report Portal uses a self-signed certificate, set `REPORTPORTAL_VERIFY_SSL=false`. - Do not leave `ENABLE_REPORTPORTAL=false` if you want the feature available. ## Quick Example ```bash jji push-reportportal ``` This pushes the saved analysis for `` into the matching Report Portal launch. JJI prints how many items were updated, the launch ID, and whether anything was unmatched or failed. ## Step-by-Step 1. Enable Report Portal integration on the JJI server. ```bash REPORTPORTAL_URL=https://reportportal.example.com REPORTPORTAL_PROJECT=my-project REPORTPORTAL_API_TOKEN=your-rp-token PUBLIC_BASE_URL=https://jji.example.com # Optional explicit toggle ENABLE_REPORTPORTAL=true # Optional for self-signed certificates REPORTPORTAL_VERIFY_SSL=false ``` `REPORTPORTAL_URL`, `REPORTPORTAL_PROJECT`, and `REPORTPORTAL_API_TOKEN` make the integration available. `PUBLIC_BASE_URL` is required for pushes because JJI includes a link back to the saved JJI report in each Report Portal update. > **Tip:** You usually do not need `ENABLE_REPORTPORTAL=true`. JJI enables the feature automatically when the URL, project, and token are present. 2. Push the completed analysis. ```bash jji push-reportportal ``` JJI looks up the matching Report Portal launch from the Jenkins build URL stored with the analysis, then updates matched failed items with the JJI classification and a link back to the JJI report. JJI maps classifications like this: - `PRODUCT BUG` -> `Product Bug` - `CODE ISSUE` -> `Automation Bug` - `INFRASTRUCTURE` -> `System Issue` ```mermaid sequenceDiagram participant You participant JJI participant RP as Report Portal You->>JJI: Push classifications for JJI->>RP: Find the launch for the analyzed Jenkins build JJI->>RP: Load failed items in that launch JJI->>RP: Update matched items with defect type and report link RP-->>JJI: launch_id, unmatched items, errors JJI-->>You: Push result ``` 3. Push a child-job run when the report contains pipeline children. ```bash jji push-reportportal \ --child-job-name "" \ --child-build-number ``` Use this when the failures you want live under a child job instead of the top-level run. The same flags work for nested child jobs too. > **Warning:** `--child-job-name` and `--child-build-number` are a pair. If you supply the child job name, you must also supply the child build number. 4. Use the web UI when you prefer point-and-click. Open the saved JJI report and use **Push to Report Portal**. For reports without child jobs, the button appears in the main report header; for pipeline-style reports, use the button on the child job you want to push. 5. Capture the result when the push needs follow-up. ```bash jji --json push-reportportal ``` The JSON result tells you: - `pushed`: how many Report Portal items were updated - `unmatched`: items that were found but could not be matched or mapped - `errors`: push problems that still need attention - `launch_id`: the Report Portal launch JJI targeted ## Advanced Usage Use JSON output whenever you need to distinguish full success, partial success, and no-op results without guessing from terminal text. ```bash jji --json push-reportportal ``` | Result | What it means | What to do next | | --- | --- | --- | | `pushed > 0`, `unmatched = []`, `errors = []` | Everything matched and updated. | No follow-up needed. | | `pushed > 0`, plus `unmatched` or `errors` | Some items were updated, but not all. | Review the unmatched items or error text, fix the cause, then rerun the same command. | | `pushed = 0`, `unmatched` populated | JJI found candidates but could not match or map them. | Check naming differences between JJI failures and Report Portal items. | | `pushed = 0`, `errors` populated | JJI could not complete the push. | Fix the reported problem, then rerun. | The plain `jji push-reportportal` command is best for interactive use. In automation, inspect the JSON fields instead of relying only on the process exit code, because a structured push result can still contain `errors` or `unmatched`. Matching is forgiving, but not unlimited. JJI can match fully qualified test names and short-name variants, so a fully qualified JJI test name can still line up with a shorter Report Portal item name, but unrelated naming schemes stay unmatched. Only failed Report Portal items are updated. If JJI already has Jira matches attached to a product bug, those issue links are pushed into Report Portal along with the classification. > **Note:** If you do not have a saved analysis yet, see [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html). ## Troubleshooting - `Report Portal integration is disabled or not configured`: make sure `REPORTPORTAL_URL`, `REPORTPORTAL_PROJECT`, and `REPORTPORTAL_API_TOKEN` are set, and that `ENABLE_REPORTPORTAL` is not forcing the feature off. - `PUBLIC_BASE_URL must be set`: point `PUBLIC_BASE_URL` at the external JJI URL users actually open in the browser. - The **Push to Report Portal** button is missing: the server is not currently exposing Report Portal integration, or it has been explicitly disabled. - `Job not found`: use the JJI analysis `job_id`, not the Jenkins build number. - `Child job ... not found`: recheck both the child job name and child build number from the JJI report. - `No Report Portal launch found`: make sure the matching Report Portal launch description contains the Jenkins build URL from the analyzed run. - `Ambiguous RP launch`: JJI found more than one launch for the same build. Remove or consolidate the duplicate launches, then rerun the push. - `No failed test items found in RP launch`: JJI only updates failed items, so there was nothing eligible to change in that launch. - `No overlap` or many `unmatched` items: the Report Portal item names do not line up closely enough with the JJI failure names. Keep the test names consistent between the two systems. - `403` or `Not a launch owner`: use a Report Portal token that is allowed to update that launch. - TLS or certificate errors against an internal Report Portal: set `REPORTPORTAL_VERIFY_SSL=false` if you are using a self-signed certificate. ## Related Pages - [Reviewing, Commenting, and Reclassifying Failures](reviewing-commenting-and-reclassifying-failures.html) - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) - [CLI Command Reference](cli-command-reference.html) - [REST API Reference](rest-api-reference.html) --- Source: managing-admin-users-and-api-keys.md # Managing Admin Users and API Keys You want to grant, rotate, and revoke admin access without sharing one long-lived secret or locking out the team. In JJI, the safe pattern is to use the server `ADMIN_KEY` only to bootstrap access, then create named admins and manage their keys separately. ## Prerequisites - A running JJI server you can reach from a browser or with `jji`. If you still need that setup, see [Running Your First Analysis](quickstart.html). - The server's `ADMIN_KEY`. - `jji` installed if you want the CLI examples. - If you plan to promote an existing regular user, that username must already exist in JJI. ## Quick Example ```bash jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users create alice ``` Run that once with the bootstrap key. JJI creates a named admin, prints a new API key once, and that new admin can then sign in in the browser, run CLI admin commands, or call the API with a Bearer token. > **Warning:** Copy the generated key immediately. JJI does not let you retrieve it later; you can only rotate it. | Task | Fastest option | What happens | | --- | --- | --- | | Create a named admin | UI `Users` page or `jji admin users create` | A new admin key is returned once | | Promote a tracked user | Role change in UI or `jji admin users change-role admin` | The user becomes admin and gets a new key | | Rotate an admin key | UI rotate button or `jji admin users rotate-key ` | The old key stops working immediately | | Demote an admin | Role change in UI or `jji admin users change-role user` | The admin key is revoked and current admin sessions end | | Remove access | UI delete button or `jji admin users delete ` | The user is removed and current admin sessions end | ## Step-by-Step 1. Use the bootstrap admin once. ```bash jji --server https://jji.example.com --api-key "$ADMIN_KEY" auth whoami ``` Verify the bootstrap key first. In the browser, go to `/register`, enter username `admin`, paste the same `ADMIN_KEY`, and click `Save`. After that, the `Users` navigation item appears. > **Note:** The browser creates an admin session. The CLI does not keep an admin session between later commands, so use `--api-key`, `JJI_API_KEY`, or a saved `api_key` in CLI config for the commands you run. 2. Create a named admin and stop sharing the bootstrap key. ```bash jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users create alice ``` In the UI, open `Users` and select `Create Admin`. For API automation, the equivalent operation is `POST /api/admin/users` with a Bearer admin key. > **Note:** Managed usernames must be 2-50 characters, start with a letter or digit, and may contain `.`, `_`, and `-`. The username `admin` is reserved for the bootstrap login. 3. Have the new admin sign in and verify access. ```bash export JJI_API_KEY="paste-alice-key-here" jji --server https://jji.example.com auth whoami ``` In the browser, the new admin signs in at `/register` with username `alice` and that key. For API calls, send the same key as a Bearer token instead of relying on browser cookies. ```bash curl -sS \ -H "Authorization: Bearer $JJI_API_KEY" \ https://jji.example.com/api/admin/users ``` 4. Rotate a key when you suspect exposure or during a handoff. ```bash jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users rotate-key alice ``` In the UI, use the rotate button next to the admin user. The old key stops working immediately, and JJI ends that user's current admin sessions, so they must sign in again with the new key. ```mermaid sequenceDiagram participant Admin participant JJI participant Alice Admin->>JJI: Rotate Alice's key JJI-->>Admin: Return new key once JJI->>JJI: End Alice's current admin sessions Alice->>JJI: Old key JJI-->>Alice: Access denied Alice->>JJI: New key JJI-->>Alice: Admin access restored ``` 5. Promote, demote, or delete access. ```bash jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users change-role bob admin jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users change-role alice user jji --server https://jji.example.com --api-key "$ADMIN_KEY" admin users delete carol --force ``` The `Users` page shows tracked users and named admins, so you can promote an existing user or demote an admin without leaving the UI. API automation uses the same flow with `PUT /api/admin/users/{username}/role` and `DELETE /api/admin/users/{username}`. > **Tip:** If someone is not listed yet, have them open JJI once and save a username. After that, refresh `Users` and promote them from `user` to `admin`. > **Warning:** You cannot change or delete your own current admin account, and JJI blocks deleting or demoting the last admin user. ## Advanced Usage ### Choose the right admin auth method | Interface | Recommended auth | What persists | | --- | --- | --- | | Browser UI | Sign in at `/register` with username and admin key | A server-side admin session, up to 8 hours | | CLI | `--api-key`, `JJI_API_KEY`, or a saved `api_key` | Only what you keep in your shell or config | | API | `Authorization: Bearer ` | Nothing unless you intentionally use login cookies | > **Note:** `jji auth login --username ... --api-key ...` validates credentials, but it does not make later CLI commands remember them. ### Save a CLI admin key in your config ```toml [default] server = "prod" [servers.prod] url = "https://jji.example.com" username = "alice" api_key = "paste-admin-key-here" ``` After that, commands like `jji admin users list` use the saved key automatically. Treat this file as sensitive and do not share it or check it into version control. ### Set a specific replacement key through the API ```bash curl -sS -X POST \ -H "Authorization: Bearer $JJI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"new_key":"replace-with-a-password-manager-generated-key"}' \ https://jji.example.com/api/admin/users/alice/rotate-key ``` Only the API lets you supply `new_key` yourself; the UI and CLI always generate one for you. The replacement key must be at least 16 characters long. ### Rotate the bootstrap `ADMIN_KEY` The reserved `admin` login is separate from the managed user list. There is no UI, CLI, or admin-users API action for rotating that bootstrap secret. Use this sequence instead: 1. Create and verify at least one named admin. 2. Change the server `ADMIN_KEY`. 3. Restart or redeploy JJI. 4. Test the new bootstrap login only if you still need it. > **Note:** Rotating `ADMIN_KEY` changes only the reserved `admin` login. Existing named admin keys continue to work. ### Reduce exposure when you finish ```bash unset JJI_API_KEY ``` Use the browser logout button when you are done with interactive admin work. If you saved `api_key` in CLI config, remove it when you no longer need admin access. > **Tip:** `jji auth logout` clears a server session. It does not unset `JJI_API_KEY` or remove `api_key` from your CLI config. ## Troubleshooting - **`Invalid username or API key`**: For bootstrap browser login, the username must be exactly `admin`. For named admins, the username and key must belong to the same account. - **`Admin access required`**: You are signed in as a regular user, or your CLI/API request is missing `--api-key` or a Bearer token. Check with `jji auth whoami` or `GET /api/auth/me`. - **The user you want to promote is not listed**: JJI can only promote existing usernames. Have that person open JJI once and save a username, then refresh the `Users` page. - **A demote or delete action is blocked**: JJI prevents deleting or demoting the last admin user and also prevents changing or deleting your own active admin account. - **The browser does not keep the admin session on local HTTP**: The server may still be using secure cookies. Use HTTPS, or turn off `SECURE_COOKIES` only for local HTTP development. ## Related Pages - [Managing Your Profile and Personal Tokens](managing-your-profile-and-personal-tokens.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) - [REST API Reference](rest-api-reference.html) - [CLI Command Reference](cli-command-reference.html) - [Running Your First Analysis](quickstart.html) --- Source: cli-command-reference.md # CLI Command Reference ## Global Options These options apply to every command. `jji config ...` commands do not require a server connection. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--server`, `-s` | string | `JJI_SERVER`, then `[default].server` | Server profile name or full `http://`/`https://` URL. A full URL does not load any other profile keys. | | `--json` | flag | `false` | Pretty-print JSON instead of text or table output. | | `--user` | string | `JJI_USERNAME`, then profile `username`, else empty | Username cookie used for comment and review attribution. | | `--api-key` | string | `JJI_API_KEY`, then profile `api_key`, else empty | Bearer token used for admin and other protected API calls. | | `--no-verify-ssl` | flag | `JJI_NO_VERIFY_SSL`, then profile `no_verify_ssl`, else `false` | Disable HTTPS certificate verification for the server connection. | | `--verify-ssl` | flag | unset | Force HTTPS certificate verification on, overriding profile `no_verify_ssl`. | | `--insecure` | flag | `false` | Alias for `--no-verify-ssl`. | ```bash jji --server prod --api-key "$JJI_API_KEY" results list --json jji --server https://jji.example.com --insecure health ``` Effect: The CLI resolves the server, username, SSL behavior, and admin API key before running the selected command. > **Note:** `--json` works both before the command and after the leaf command, for example `jji --json results list` and `jji results list --json`. > **Warning:** `--verify-ssl` and `--insecure` are mutually exclusive. > **Note:** When the server returns `401` or `403`, the CLI prints a hint to use `--api-key` or `JJI_API_KEY`. ## Output Modes | Mode | Syntax | Behavior | | --- | --- | --- | | Text / table | default | Commands print aligned tables or command-specific summary text. Generic table output preserves full values and prints `No results.` for empty datasets. | | JSON | `--json` | Commands print pretty-formatted JSON with 2-space indentation. | ```bash jji results list --json jji --json history test tests.TestA.test_one ``` Effect: JSON mode returns the raw response body for the command. Text mode may show a summary instead of every field. > **Note:** `jji capabilities` prints JSON even without `--json`. > **Note:** `jji results show --full` also prints the full stored result document as JSON. ## Config File `jji` reads its local config file from `$XDG_CONFIG_HOME/jji/config.toml`. When `XDG_CONFIG_HOME` is unset, the path is `~/.config/jji/config.toml`. ### Top-Level Sections | Section | Keys | Description | | --- | --- | --- | | `[default]` | `server` | Name of the default server profile used when `--server` and `JJI_SERVER` are unset. | | `[defaults]` | any server-profile keys except `server` | Shared values merged into every `[servers.]` entry. | | `[servers.]` | `url` plus optional profile keys | Named server profile. | ```toml [default] server = "dev" [defaults] username = "alice" tests_repo_url = "https://github.com/example/tests" ai_provider = "claude" ai_model = "opus-4" [servers.dev] url = "http://localhost:8000" no_verify_ssl = true [servers.prod] url = "https://jji.example.com" api_key = "jji_admin_..." jira_token = "jira-token" jira_email = "alice@example.com" github_repo_url = "https://github.com/example/repo" ``` Effect: The selected profile provides default values for shared connection settings and for commands such as `analyze`, `preview-issue`, `create-issue`, `jira-projects`, and `jira-security-levels`. > **Warning:** `[defaults].server` is invalid. Put the default profile name in `[default].server`. ### Resolution Rules | Priority | Source | Notes | | --- | --- | --- | | 1 | CLI flags and bound environment variables | Highest precedence. | | 2 | Selected `[servers.]` entry | Loaded when `--server NAME` or `[default].server` selects a profile. | | 3 | `[defaults]` section | Merged into the selected server entry before command-specific overrides. | | 4 | Omitted | Unset fields are not sent by the CLI. | ```bash jji --server prod health JJI_SERVER=prod jji health jji --server https://jji.example.com health ``` Effect: A concrete `http://` or `https://` value for `--server` or `JJI_SERVER` is self-contained; no other profile fields are inherited. ### Profile Keys | Key | Type | Default | Used by | Description | | --- | --- | --- | --- | --- | | `url` | string | required | all non-`config` commands | Base URL of the JJI server. Must be a non-empty trimmed string. | | `username` | string | `""` | shared global option | Default username cookie for comment and review actions. | | `no_verify_ssl` | boolean | `false` | shared global option | Default TLS verification behavior for the server connection. | | `api_key` | string | `""` | shared global option | Default Bearer token for admin and protected commands. | | `jenkins_url` | string | `""` | `analyze` | Default Jenkins server URL. | | `jenkins_user` | string | `""` | `analyze` | Default Jenkins username. | | `jenkins_password` | string | `""` | `analyze` | Default Jenkins password or API token. | | `jenkins_ssl_verify` | boolean | unset | `analyze` | Default Jenkins TLS verification flag. | | `tests_repo_url` | string | `""` | `analyze` | Default tests repository URL. | | `ai_provider` | string | `""` | `analyze` | Default AI provider. | | `ai_model` | string | `""` | `analyze` | Default AI model. | | `ai_cli_timeout` | integer | `0` | `analyze` | Default AI CLI timeout in minutes. `0` means unset in the CLI config. | | `jira_url` | string | `""` | `analyze` | Default Jira instance URL. | | `jira_email` | string | `""` | `analyze`, `jira-projects`, `jira-security-levels`, Jira issue preview/create | Default Jira Cloud email. | | `jira_api_token` | string | `""` | `analyze` | Default Jira Cloud API token for analysis-time Jira lookup. | | `jira_pat` | string | `""` | `analyze` | Default Jira Server / Data Center personal access token for analysis-time Jira lookup. | | `jira_token` | string | `""` | `jira-projects`, `jira-security-levels`, Jira issue preview/create | Default Jira token for Jira utility and Jira issue commands. | | `jira_project_key` | string | `""` | `analyze`, Jira issue preview/create | Default Jira project key. | | `jira_security_level` | string | `""` | Jira issue preview/create | Default Jira security level name. | | `jira_ssl_verify` | boolean | unset | `analyze` | Default Jira TLS verification flag. | | `jira_max_results` | integer | `0` | `analyze` | Default Jira search result limit. `0` means unset in the CLI config. | | `enable_jira` | boolean | unset | `analyze` | Default Jira integration toggle. | | `github_token` | string | `""` | `analyze`, GitHub issue preview/create | Default GitHub token. | | `github_repo_url` | string | `""` | GitHub issue preview/create | Default GitHub repository URL override. | | `peers` | string | `""` | `analyze` | Default peer-review list in `provider:model[,provider:model...]` format. | | `peer_analysis_max_rounds` | integer | `0` | `analyze` | Default peer review round limit. `0` means unset in the CLI config. | | `additional_repos` | string | `""` | `analyze` | Default additional repository list in `name:url[,name:url...]` format. Each URL may append `:ref`. | | `wait_for_completion` | boolean | unset | `analyze` | Default wait behavior before analysis starts. | | `poll_interval_minutes` | integer | `0` | `analyze` | Default Jenkins polling interval. `0` means unset in the CLI config. | | `max_wait_minutes` | integer | `0` | `analyze` | Default maximum wait time. `0` means unset in the CLI config. | ```toml [servers.dev] url = "http://localhost:8000" username = "alice" jenkins_url = "https://jenkins.example.com" jira_token = "jira-token" github_repo_url = "https://github.com/example/repo" peers = "claude:opus-4,gemini:2.5-pro" additional_repos = "infra:https://github.com/example/infra:main" ``` Effect: These keys are read only when a named server profile is selected. > **Note:** `jira_token` is separate from `jira_api_token` and `jira_pat`. `analyze` reads `jira_api_token` / `jira_pat`; Jira utility and Jira issue commands read `jira_token`. > **Note:** `peers`, `jira_token`, `jira_security_level`, `github_repo_url`, and `additional_repos` must be strings. `peer_analysis_max_rounds` must be an integer. ## Command Groups | Command | Effect with no leaf subcommand | | --- | --- | | `jji` | Prints top-level help. | | `jji results` | Prints `results` group help. | | `jji history` | Prints `history` group help. | | `jji comments` | Prints `comments` group help. | | `jji classifications` | Prints `classifications` group help. | | `jji auth` | Prints `auth` group help. | | `jji admin` | Prints `admin` group help. | | `jji admin users` | Prints `admin users` group help. | | `jji config` | Runs `jji config show`. | ```bash jji --help jji results --help jji config ``` Effect: Group commands are navigation points; the executable work happens in the leaf commands below. > **Tip:** `--help` is available on the root command, every command group, and every leaf command. ## Core Commands ### `jji health` Checks server health. Parameters/options: shared global options only. ```bash jji health ``` Effect: Default output is a one-row status table. `--json` returns the raw health object. ### `jji analyze` Queues a Jenkins build for analysis. #### Required parameters | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--job-name`, `-j` | string | required | Jenkins job name. | | `--build-number`, `-b` | integer | required | Build number to analyze. Must be greater than `0`. | #### Analysis selection and AI options | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--provider` | string | profile `ai_provider`, else unset | AI provider name. | | `--model` | string | profile `ai_model`, else unset | AI model name. | | `--jira` / `--no-jira` | flag pair | profile `enable_jira`, else unset | Enable or disable Jira integration for this request. | | `--raw-prompt` | string | unset | Extra instructions appended to the AI prompt. | | `--peers` | string | profile `peers`, else unset | Peer-review list in `provider:model[,provider:model...]` format. | | `--peer-analysis-max-rounds` | integer | profile `peer_analysis_max_rounds`, else unset | Peer-review round limit. Must be between `1` and `10`. | #### Jenkins, repository, Jira, and GitHub options | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--jenkins-url` | string | `JENKINS_URL`, then profile `jenkins_url`, else unset | Jenkins server URL. | | `--jenkins-user` | string | `JENKINS_USER`, then profile `jenkins_user`, else unset | Jenkins username. | | `--jenkins-password` | string | `JENKINS_PASSWORD`, then profile `jenkins_password`, else unset | Jenkins password or API token. | | `--jenkins-ssl-verify` / `--no-jenkins-ssl-verify` | flag pair | profile `jenkins_ssl_verify`, else unset | Jenkins TLS verification flag. | | `--jenkins-artifacts-max-size-mb` | integer | unset | Maximum Jenkins artifact size in MB. Must be greater than `0`. | | `--get-job-artifacts` / `--no-get-job-artifacts` | flag pair | unset | Download or skip downloading all build artifacts for AI context. | | `--tests-repo-url` | string | `TESTS_REPO_URL`, then profile `tests_repo_url`, else unset | Tests repository URL. | | `--jira-url` | string | `JIRA_URL`, then profile `jira_url`, else unset | Jira instance URL. | | `--jira-email` | string | `JIRA_EMAIL`, then profile `jira_email`, else unset | Jira Cloud email. | | `--jira-api-token` | string | `JIRA_API_TOKEN`, then profile `jira_api_token`, else unset | Jira Cloud API token for analysis-time lookup. | | `--jira-pat` | string | `JIRA_PAT`, then profile `jira_pat`, else unset | Jira Server / Data Center personal access token for analysis-time lookup. | | `--jira-project-key` | string | `JIRA_PROJECT_KEY`, then profile `jira_project_key`, else unset | Jira project key. | | `--jira-ssl-verify` / `--no-jira-ssl-verify` | flag pair | profile `jira_ssl_verify`, else unset | Jira TLS verification flag. | | `--jira-max-results` | integer | profile `jira_max_results`, else unset | Maximum Jira search results. Must be greater than `0`. | | `--github-token` | string | `GITHUB_TOKEN`, then profile `github_token`, else unset | GitHub token. | | `--additional-repos` | string | profile `additional_repos`, else unset | Additional repositories in `name:url[,name:url...]` format. Each URL may append `:ref`. | #### Monitoring options | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--wait` / `--no-wait` | flag pair | profile `wait_for_completion`, else unset | Wait or do not wait for Jenkins completion before analysis begins. | | `--poll-interval` | integer | profile `poll_interval_minutes`, else unset | Jenkins polling interval in minutes. Must be greater than `0`. | | `--max-wait` | integer | profile `max_wait_minutes`, else unset | Maximum wait time in minutes. Must be `0` or greater. | ```bash jji analyze \ --job-name ocp-e2e \ --build-number 142 \ --provider claude \ --model opus-4 \ --jenkins-url https://jenkins.example.com \ --tests-repo-url https://github.com/example/tests \ --wait \ --poll-interval 5 ``` Effect: Queues an analysis request. Default output prints `Job queued`, `Status`, and `Poll`; `--json` returns the queued response, including `job_id` and `result_url`. > **Note:** `--peers` accepts `provider:model[,provider:model...]`. `--additional-repos` accepts `name:url[,name:url...]`, and each URL may append `:ref` after the repository path. > **Warning:** Invalid `--peers`, invalid `--additional-repos`, out-of-range `--peer-analysis-max-rounds`, or invalid numeric bounds exit with status `1`. ### `jji re-analyze JOB_ID` Re-runs a stored analysis with the same settings. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id to re-run. | ```bash jji re-analyze old-job-1 ``` Effect: Queues a new analysis. Default output prints the new `job_id`, `status`, and poll URL; `--json` returns the queued response. ### `jji status JOB_ID` Checks the current status of an analysis job. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | ```bash jji status abc-123 ``` Effect: Default output prints only `job_id` and `status`. `--json` returns the full stored result object when available. ## Results Commands ### `jji results list` Lists recent analyzed jobs. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--limit`, `-l` | integer | `50` | Maximum number of jobs to return. | ```bash jji results list --limit 10 ``` Effect: Default output shows `job_id`, `status`, `jenkins_url`, and `created_at`. `--json` returns the raw list. ### `jji results dashboard` Lists jobs with dashboard metadata. Parameters/options: shared global options only. ```bash jji results dashboard ``` Effect: Default output shows `job_id`, `job_name`, `build_number`, `status`, `failure_count`, `reviewed_count`, `comment_count`, and `created_at`. `--json` returns the raw list. ### `jji results show JOB_ID` Shows a stored analysis result. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--full`, `-f` | flag | `false` | Print the full stored result document as JSON. | ```bash jji results show abc-123 jji results show abc-123 --full ``` Effect: Default output is a summary row with `job_id`, `status`, `summary`, `failures`, `children`, `ai_provider`, and `created_at`. `--full` or `--json` prints the full stored result document. ### `jji results delete JOB_ID` Deletes a stored job and related data. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id to delete. | ```bash jji results delete abc-123 ``` Effect: Default output prints `Deleted job `. `--json` returns the API response. ### `jji results review-status JOB_ID` Shows review progress for a stored analysis. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | ```bash jji results review-status job-1 ``` Effect: Default output is a one-row table with `total_failures`, `reviewed_count`, and `comment_count`. `--json` returns the raw response object. ### `jji results set-reviewed JOB_ID` Sets or clears the reviewed state for a test failure. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--test`, `-t` | string | required | Fully qualified test name. | | `--reviewed` / `--not-reviewed` | flag pair | required | Choose the reviewed state to apply. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. Must be non-negative. Values greater than `0` require `--child-job`. | ```bash jji results set-reviewed job-1 --test tests.TestA.test_one --reviewed ``` Effect: Default output prints `Marked as reviewed` or `Marked as not reviewed`, optionally with `reviewed_by`. `--json` returns the API response. ### `jji results enrich-comments JOB_ID` Refreshes comment metadata such as live PR or ticket status. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | ```bash jji results enrich-comments job-1 ``` Effect: Default output prints `Enriched N comment(s).` `--json` returns the raw response object. ## History Commands ### `jji history test TEST_NAME` Shows failure history for one test. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `TEST_NAME` | string | required | Fully qualified test name. | | `--limit`, `-l` | integer | `20` | Maximum number of recent runs to include. | | `--job-name`, `-j` | string | `""` | Restrict history to one Jenkins job name. | | `--exclude-job-id` | string | `""` | Exclude one stored analysis job id from the history query. | ```bash jji history test tests.TestA.test_one --limit 10 ``` Effect: Default output prints summary fields plus optional `Recent runs` and `Comments` tables. `--json` returns the full history document. ### `jji history search` Finds tests that share an error signature. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--signature`, `-s` | string | required | Error signature hash. | | `--exclude-job-id` | string | `""` | Exclude one stored analysis job id from the search. | ```bash jji history search --signature sig-abc123 ``` Effect: Default output prints signature totals and a test occurrence table. `--json` returns the full search response. ### `jji history stats JOB_NAME` Shows aggregate statistics for one Jenkins job. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_NAME` | string | required | Jenkins job name. | | `--exclude-job-id` | string | `""` | Exclude one stored analysis job id from the stats query. | ```bash jji history stats ocp-e2e ``` Effect: Default output prints aggregate counts plus an optional `Most common failures` table. `--json` returns the full stats object. ### `jji history failures` Lists paginated failure history. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--limit`, `-l` | integer | `50` | Page size. | | `--offset`, `-o` | integer | `0` | Page offset. | | `--search`, `-s` | string | `""` | Test-name search text. | | `--classification`, `-c` | string | `""` | Classification filter. | | `--job-name`, `-j` | string | `""` | Jenkins job-name filter. | ```bash jji history failures --classification "PRODUCT BUG" --job-name ocp-e2e --limit 25 ``` Effect: Default output prints a `Total:` line and a failure table. `--json` returns the paginated response object. ## Classification Commands ### `jji classify TEST_NAME` Creates a manual classification for a failure. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `TEST_NAME` | string | required | Fully qualified test name. | | `--type`, `-t` | string | required | Manual classification. The CLI uppercases the value before sending it. CLI help lists `FLAKY`, `REGRESSION`, `INFRASTRUCTURE`, `KNOWN_BUG`, and `INTERMITTENT`. | | `--job-id` | string | required | Stored analysis job id this classification applies to. | | `--reason`, `-r` | string | `""` | Free-form reason text. | | `--job-name`, `-j` | string | `""` | Parent job name when no child job is supplied. | | `--references` | string | `""` | Bug URLs or ticket keys. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. | ```bash jji classify tests.TestA.test_one --type FLAKY --job-id job-123 --reason "intermittent DNS" ``` Effect: Default output prints the created record id. `--json` returns the created classification object. ### `jji classifications list` Lists stored manual classifications. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--job-id` | string | `""` | Filter by stored analysis job id. | | `--test-name`, `-t` | string | `""` | Filter by fully qualified test name. | | `--type`, `-c` | string | `""` | Classification filter. The CLI uppercases the value before sending it. | | `--job-name`, `-j` | string | `""` | Filter by job name. | | `--parent-job-name` | string | `""` | Filter by parent job name. | ```bash jji classifications list --type flaky --parent-job-name pipeline-parent ``` Effect: Default output prints classification rows or `No classifications found.` `--json` returns the full `{classifications:[...]}` payload. ### `jji override-classification JOB_ID` Overrides a failure classification in a stored result. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--test`, `-t` | string | required | Fully qualified test name. | | `--classification`, `-c` | string | required | Override value. CLI help lists `CODE ISSUE`, `PRODUCT BUG`, and `INFRASTRUCTURE`. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. | ```bash jji override-classification job-1 --test tests.TestA.test_one --classification "PRODUCT BUG" ``` Effect: Default output prints the new classification. `--json` returns the API response. ## Comment Commands > **Note:** Comment and review commands use the shared `--user` option or the selected profile `username` for attribution. ### `jji comments list JOB_ID` Lists comments for one stored analysis job. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | ```bash jji comments list job-1 ``` Effect: Default output prints comment rows or `No comments for this job.` `--json` returns the full response, including `comments` and `reviews`. ### `jji comments add JOB_ID` Adds a comment to one failure. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--test`, `-t` | string | required | Fully qualified test name. | | `--message`, `-m` | string | required | Comment text. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. | ```bash jji comments add job-1 --test tests.TestA.test_one --message "Opened JIRA-123" ``` Effect: Default output prints `Comment added (id: ...)`. `--json` returns the created comment object. ### `jji comments delete JOB_ID COMMENT_ID` Deletes a comment. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `COMMENT_ID` | integer | required | Comment id to delete. | ```bash jji comments delete job-1 42 ``` Effect: Default output prints `Comment deleted.` `--json` returns the API response. ## Tracker and Automation Commands > **Note:** `preview-issue` and `create-issue` send only the credentials for the selected `--type`. GitHub runs ignore Jira credentials; Jira runs ignore GitHub credentials. ### `jji capabilities` Shows server-supported post-analysis automation features. Parameters/options: shared global options only. ```bash jji capabilities ``` Effect: Prints the capability object. Text mode and JSON mode both emit JSON, including fields such as `github_issues_enabled` and `jira_issues_enabled`. ### `jji jira-projects` Lists available Jira projects. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--query` | string | `""` | Optional search text used to filter projects. | | `--jira-token` | string | profile `jira_token`, else unset | Jira token. | | `--jira-email` | string | profile `jira_email`, else unset | Jira Cloud email. | ```bash jji jira-projects --query platform ``` Effect: Default output prints `key` / `name` rows or `No Jira projects found.` `--json` returns the raw list. ### `jji jira-security-levels PROJECT_KEY` Lists Jira security levels for one project. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `PROJECT_KEY` | string | required | Jira project key. | | `--jira-token` | string | profile `jira_token`, else unset | Jira token. | | `--jira-email` | string | profile `jira_email`, else unset | Jira Cloud email. | ```bash jji jira-security-levels PROJ ``` Effect: Default output prints `name` / `description` rows or `No security levels found.` `--json` returns the raw list. ### `jji ai-configs` Lists known AI provider/model pairs from completed analyses. Parameters/options: shared global options only. ```bash jji ai-configs ``` Effect: Default output prints `ai_provider` and `ai_model` rows. If no completed analyses exist, the CLI prints `No AI configurations found from completed analyses.` and exits successfully. `--json` returns the raw list. ### `jji preview-issue JOB_ID` Previews generated issue content for GitHub or Jira. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--test`, `-t` | string | required | Fully qualified test name. | | `--type` | string | required | Target tracker. Allowed values: `github`, `jira`. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. | | `--include-links` | flag | `false` | Include clickable URLs in the generated body. | | `--ai-provider` | string | unset | AI provider override for issue-content generation. | | `--ai-model` | string | unset | AI model override for issue-content generation. | | `--github-token` | string | profile `github_token`, else unset | GitHub token for GitHub issue preview. | | `--github-repo-url` | string | profile `github_repo_url`, else unset | GitHub repository URL override. | | `--jira-token` | string | profile `jira_token`, else unset | Jira token for Jira bug preview. | | `--jira-email` | string | profile `jira_email`, else unset | Jira Cloud email for Jira bug preview. | | `--jira-project-key` | string | profile `jira_project_key`, else unset | Jira project key override. | | `--jira-security-level` | string | profile `jira_security_level`, else unset | Jira security level name. | ```bash jji preview-issue job-1 \ --test tests.TestA.test_one \ --type github \ --include-links \ --ai-provider claude \ --ai-model opus-4 ``` Effect: Default output prints `Title`, `Body`, and any `Similar issues`. `--json` returns the preview object. ### `jji create-issue JOB_ID` Creates a GitHub issue or Jira bug from a stored failure. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--test`, `-t` | string | required | Fully qualified test name. | | `--type` | string | required | Target tracker. Allowed values: `github`, `jira`. | | `--title` | string | required | Final issue title. | | `--body` | string | required | Final issue body. | | `--child-job` | string | `""` | Optional child job name. | | `--child-build` | integer | `0` | Optional child build number. | | `--github-token` | string | profile `github_token`, else unset | GitHub token for GitHub issue creation. | | `--github-repo-url` | string | profile `github_repo_url`, else unset | GitHub repository URL override. | | `--jira-token` | string | profile `jira_token`, else unset | Jira token for Jira bug creation. | | `--jira-email` | string | profile `jira_email`, else unset | Jira Cloud email for Jira bug creation. | | `--jira-project-key` | string | profile `jira_project_key`, else unset | Jira project key override. | | `--jira-security-level` | string | profile `jira_security_level`, else unset | Jira security level name. | ```bash jji create-issue job-1 \ --test tests.TestA.test_one \ --type jira \ --title "DNS timeout in ocp-e2e" \ --body "Reproduced in multiple runs." ``` Effect: Default output prints the created issue key or number, the URL, and any `comment_id` added to the JJI record. `--json` returns the created issue object. ### `jji validate-token {github|jira}` Validates a tracker token. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `{github\|jira}` | string | required | Token type. Allowed values: `github`, `jira`. | | `--token` | string | interactive prompt | Token value to validate. Input is hidden when prompted. | | `--email` | string | `""` | Jira Cloud email. Used only for Jira validation. | ```bash jji validate-token jira --token jira-token --email user@example.com ``` Effect: Valid tokens exit with status `0` and print `Valid`; invalid tokens exit with status `1` and print `Invalid`. `--json` returns the validation object. ### `jji push-reportportal JOB_ID` / `jji push-rp JOB_ID` Pushes stored classifications into Report Portal. `push-rp` is a hidden alias. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `JOB_ID` | string | required | Stored analysis job id. | | `--child-job-name` | string | unset | Optional child job name for pipeline child pushes. | | `--child-build-number` | integer | unset | Optional child build number for pipeline child pushes. | ```bash jji push-rp job-123 ``` Effect: Default output prints the pushed count, optional launch id, unmatched tests, and any errors. `--json` returns the raw response object. ## Auth and Admin Commands > **Note:** `jji auth login` validates credentials only. It does not persist authentication between CLI invocations. For later commands, use the shared `--api-key`, `JJI_API_KEY`, or profile `api_key`. > **Warning:** `jji admin users create`, `jji admin users rotate-key`, and `jji admin users change-role USER admin` may print API keys only once. ### `jji auth login` Validates admin credentials. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `--username`, `-u` | string | required | Admin username. | | `--api-key`, `-k` | string | required | Admin API key to validate. | ```bash jji auth login --username admin --api-key jji_admin_key ``` Effect: Default output prints `username`, `role`, and `admin` status. `--json` returns the auth object. ### `jji auth logout` Sends the logout request. Parameters/options: shared global options only. ```bash jji auth logout --json ``` Effect: Sends the logout request. Use `--json` to inspect returned fields. ### `jji auth whoami` Shows current authenticated user info. Parameters/options: shared global options only. ```bash jji auth whoami ``` Effect: Default output prints `username`, `role`, and `is_admin`. `--json` returns the same fields as a JSON object. ### `jji admin users list` Lists all users. Parameters/options: shared global options only. ```bash jji admin users list ``` Effect: Default output prints user rows or `No users found.` `--json` returns `{users:[...]}`. ### `jji admin users create USERNAME` Creates a new admin user. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `USERNAME` | string | required | Username for the new admin user. | ```bash jji admin users create newadmin ``` Effect: Default output prints the created username and the new API key once. `--json` returns the same response object. ### `jji admin users delete USERNAME` Deletes an admin user. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `USERNAME` | string | required | Username to delete. | | `--force`, `-f` | flag | `false` | Skip the confirmation prompt. | ```bash jji admin users delete oldadmin --force ``` Effect: Prompts for confirmation unless `--force` is set. Default output prints `Deleted admin user: `; `--json` returns the API response. ### `jji admin users rotate-key USERNAME` Rotates an admin user's API key. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `USERNAME` | string | required | Username whose key should be rotated. | ```bash jji admin users rotate-key myuser ``` Effect: Default output prints the username and the new API key once. `--json` returns the same response object. ### `jji admin users change-role USERNAME ROLE` Changes a user's role. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `USERNAME` | string | required | Username to update. | | `ROLE` | string | required | New role. CLI help lists `admin` and `user`. | ```bash jji admin users change-role myuser admin ``` Effect: Default output prints the new role. If the API returns a new key during promotion to `admin`, the CLI prints it once. `--json` returns the same response object. ## Config Commands Config commands use the local config file only and do not contact the server. ### `jji config` / `jji config show` Shows the current local config file summary. `jji config` is the same command as `jji config show`. Parameters/options: none. ```bash jji config ``` Effect: If the config file exists, the CLI prints the resolved file path, the selected default server, and one summary line per server. If the file is missing, the CLI prints the resolved path and a bootstrap snippet. ### `jji config completion [SHELL]` Prints shell-completion setup instructions. | Name | Type | Default / source | Description | | --- | --- | --- | --- | | `SHELL` | string | `zsh` | Shell type. Allowed values: `bash`, `zsh`. | ```bash jji config completion bash ``` Effect: Prints shell setup instructions that call `jji --show-completion bash` or `jji --show-completion zsh`. Unsupported shell values exit with status `1`. ### `jji config servers` Lists configured server profiles. Parameters/options: shared `--json` only. ```bash jji config servers jji config servers --json ``` Effect: Default output prints a table with `name`, `url`, `username`, `no_verify_ssl`, and a `*` marker for the default server. `--json` returns an object keyed by server name with `url`, `username`, `no_verify_ssl`, and `default`. ## Related Pages - [Configuration and Environment Reference](configuration-and-environment-reference.html) - [REST API Reference](rest-api-reference.html) - [Running Your First Analysis](quickstart.html) - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Managing Admin Users and API Keys](managing-admin-users-and-api-keys.html) --- Source: rest-api-reference.md # REST API Reference > **Note:** `server default` in request tables means the field inherits the running server configuration when omitted. See [Configuration and Environment Reference](configuration-and-environment-reference.html) for the exact defaults and feature flags. > **Tip:** CLI wrappers for these endpoints are documented in [CLI Command Reference](cli-command-reference.html). ## Conventions ### Authentication Inputs | Name | Type | Default | Description | | --- | --- | --- | --- | | `Authorization` | `Bearer ` header | none | Admin bootstrap key or admin user API key. | | `jji_session` | cookie | none | Admin session cookie set by `POST /api/auth/login`. | | `jji_username` | cookie | none | Username context used for token storage, comments, reviews, and issue attribution. | Most read and analysis endpoints are accessible without authentication. Admin-only endpoints are under `/api/admin/*` and `DELETE /results/{job_id}`. ### Common Status Codes | Code | Meaning | Notes | | --- | --- | --- | | `200` | Success | Some integration endpoints also use `200` for partial/soft failures and put details in the JSON body. | | `201` | Created | Used for comments, classifications, and issue creation. | | `202` | Accepted / in progress | Returned when a job is queued or when a referenced analysis is still pending, waiting, or running. | | `400` | Invalid request | Used for invalid JSON, missing required fields in object-parsed endpoints, or invalid integration/config state. | | `401` | Authentication failed | Used for invalid tracker credentials or missing username on user-token endpoints. | | `403` | Forbidden | Used for admin-only or disabled feature endpoints. | | `404` | Not found | Missing job, comment, user, or admin account. | | `409` | Conflict | Used when an endpoint targets a stored analysis that finished with `status="failed"`. | | `422` | Validation failed | FastAPI/Pydantic body or query validation error. | | `502` | Upstream tracker failure | GitHub or Jira API error/unreachable response during issue creation. | ### Child Failure Targeting Many failure-targeting request bodies accept `child_job_name` and `child_build_number`. | Name | Type | Default | Description | | --- | --- | --- | --- | | `child_job_name` | `string` | `""` | Child pipeline job name. Empty string targets top-level failures. | | `child_build_number` | `integer >= 0` | `0` | Child build number. `0` means “no specific build” and works as a wildcard for lookup-oriented endpoints. | > **Note:** `PUT /results/{job_id}/override-classification` is stricter than the shared validator: when `child_job_name` is set, it requires a non-zero `child_build_number`. ### Progress Phases `GET /results/{job_id}` may expose `result.progress_phase` and `result.progress_log`. | Phase | Description | | --- | --- | | `waiting_for_jenkins` | Waiting for the Jenkins build to finish before analysis starts. | | `analyzing` | AI analysis is running. | | `enriching_jira` | Jira post-processing is running for `PRODUCT BUG` results. | | `saving` | Final result persistence is running. | ### Sensitive Stored Request Fields Public result responses strip secret values from `result.request_params`. | Stripped key | | --- | | `jenkins_password` | | `jenkins_user` | | `jira_api_token` | | `jira_pat` | | `jira_email` | | `jira_token` | | `github_token` | | `reportportal_api_token` | ## Shared Schemas ### `AiConfigEntry` One peer-review AI configuration. | Name | Type | Default | Description | | --- | --- | --- | --- | | `ai_provider` | `claude \| gemini \| cursor` | required | Peer AI provider. | | `ai_model` | `string` | required | Peer AI model identifier. Blank values are rejected. | Effect: one entry in `peer_ai_configs`. ```json { "ai_provider": "gemini", "ai_model": "gemini-2.5-pro" } ``` ### `AdditionalRepo` Additional repository cloned into the analysis workspace. | Name | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Directory name used for the clone. Must be non-blank, must not contain `/`, `\`, `..`, or start with `.`, and must not use reserved names. | | `url` | `string (URL)` | required | Repository URL. | | `ref` | `string` | `""` | Branch or tag to check out. Empty string uses the remote default branch. | Effect: adds extra repository context for AI analysis. ```json { "name": "product-repo", "url": "https://github.com/acme/product.git", "ref": "main" } ``` ### `TestFailure` Raw failure input for direct analysis. | Name | Type | Default | Description | | --- | --- | --- | --- | | `test_name` | `string` | required | Fully qualified test name. | | `error_message` | `string` | `""` | Failure message. | | `stack_trace` | `string` | `""` | Full stack trace. | | `duration` | `number` | `0.0` | Test duration in seconds. | | `status` | `string` | `"FAILED"` | Original test status label. | Effect: one input failure for `POST /analyze-failures`. ```json { "test_name": "tests.api.test_login.test_admin_login", "error_message": "AssertionError: expected 200 got 500", "stack_trace": "Traceback...", "duration": 1.24, "status": "FAILED" } ``` ### `BaseAnalysisRequest` Fields shared by Jenkins-backed and direct-failure analysis. | Name | Type | Default | Description | | --- | --- | --- | --- | | `tests_repo_url` | `string \| null` | `null` | Tests repository URL override. A `:ref` suffix is parsed and later exposed as `tests_repo_ref` in stored `request_params`. | | `ai_provider` | `claude \| gemini \| cursor \| null` | `null` | Main AI provider override. | | `ai_model` | `string \| null` | `null` | Main AI model override. | | `enable_jira` | `boolean \| null` | `null` | Enable Jira bug search during analysis. | | `ai_cli_timeout` | `integer > 0 \| null` | `null` | AI CLI timeout in minutes. | | `jira_url` | `string \| null` | `null` | Jira base URL override. | | `jira_email` | `string \| null` | `null` | Jira Cloud email override. | | `jira_api_token` | `string \| null` | `null` | Jira Cloud API token override. | | `jira_pat` | `string \| null` | `null` | Jira Server/Data Center PAT override. | | `jira_project_key` | `string \| null` | `null` | Jira project key override. | | `jira_ssl_verify` | `boolean \| null` | `null` | Jira SSL verification override. | | `jira_max_results` | `integer > 0 \| null` | `null` | Maximum Jira search matches per failure. | | `raw_prompt` | `string \| null` | `null` | Extra prompt text appended to the analysis prompt. | | `github_token` | `string \| null` | `null` | GitHub token override for private-repo enrichment. | | `peer_ai_configs` | `array \| null` | `null` | Peer-review configs. Omit to inherit the server setting; send `[]` to disable peer review for this request. | | `peer_analysis_max_rounds` | `integer 1-10` | `server default (schema default: 3)` | Maximum peer debate rounds. | | `additional_repos` | `array \| null` | `null` | Additional repositories for analysis context. Omit to inherit the server setting; send `[]` to disable. | Effect: baseline analysis configuration persisted into `result.request_params`. ```json { "ai_provider": "claude", "ai_model": "sonnet", "tests_repo_url": "https://github.com/acme/tests.git:main", "peer_ai_configs": [ { "ai_provider": "gemini", "ai_model": "gemini-2.5-pro" } ], "additional_repos": [ { "name": "product-repo", "url": "https://github.com/acme/product.git", "ref": "main" } ] } ``` ### `AnalyzeRequest` Jenkins-backed analysis request. Includes all `BaseAnalysisRequest` fields plus the fields below. | Name | Type | Default | Description | | --- | --- | --- | --- | | `job_name` | `string` | required | Jenkins job name. Folder paths are allowed. | | `build_number` | `integer` | required | Jenkins build number. | | `wait_for_completion` | `boolean` | `server default (schema default: true)` | Wait for Jenkins completion before analysis. | | `poll_interval_minutes` | `integer > 0` | `server default (schema default: 2)` | Jenkins polling interval while waiting. | | `max_wait_minutes` | `integer >= 0` | `server default (schema default: 0)` | Maximum wait time. `0` means no limit. | | `jenkins_url` | `string \| null` | `null` | Jenkins base URL override. | | `jenkins_user` | `string \| null` | `null` | Jenkins username override. | | `jenkins_password` | `string \| null` | `null` | Jenkins password or API token override. | | `jenkins_ssl_verify` | `boolean \| null` | `null` | Jenkins SSL verification override. | | `jenkins_artifacts_max_size_mb` | `integer > 0 \| null` | `null` | Artifact download size cap in MB. | | `get_job_artifacts` | `boolean \| null` | `null` | Download build artifacts for AI context. | Effect: request body for `POST /analyze`. ```json { "job_name": "folder/job-name", "build_number": 123, "wait_for_completion": true, "poll_interval_minutes": 2, "tests_repo_url": "https://github.com/acme/tests.git:main", "ai_provider": "claude", "ai_model": "sonnet" } ``` ### `AnalyzeFailuresRequest` Direct failure-analysis request. Includes all `BaseAnalysisRequest` fields plus the fields below. | Name | Type | Default | Description | | --- | --- | --- | --- | | `failures` | `array \| null` | `null` | Raw failures to analyze. | | `raw_xml` | `string \| null` | `null` | Raw JUnit XML. Maximum length: `50000000` characters. | Effect: exactly one of `failures` or `raw_xml` must be provided. ```json { "failures": [ { "test_name": "tests.api.test_login.test_admin_login", "error_message": "AssertionError: expected 200 got 500", "stack_trace": "Traceback..." } ], "ai_provider": "claude", "ai_model": "sonnet" } ``` ### `PreviewIssueRequest` Preview body for GitHub or Jira issue content. | Name | Type | Default | Description | | --- | --- | --- | --- | | `test_name` | `string` | required | Failure test name to preview. | | `child_job_name` | `string` | `""` | Child pipeline job name. | | `child_build_number` | `integer >= 0` | `0` | Child build number. | | `include_links` | `boolean` | `false` | Include report and Jenkins links when `PUBLIC_BASE_URL` is configured. | | `ai_provider` | `string` | `""` | Issue-content generation provider override. Empty string uses the current server default. | | `ai_model` | `string` | `""` | Issue-content generation model override. Empty string uses the current server default. | | `github_token` | `string` | `""` | GitHub token used for duplicate search. | | `jira_token` | `string` | `""` | Jira token used for duplicate search. | | `jira_email` | `string` | `""` | Jira Cloud email paired with `jira_token`. | | `jira_project_key` | `string` | `""` | Jira project override for duplicate search. | | `jira_security_level` | `string` | `""` | Jira security level name. Not used by preview generation. | | `github_repo_url` | `string` | `""` | GitHub repository override for duplicate search. | Effect: selects the target failure and optional tracker credentials used during preview. ```json { "test_name": "tests.api.test_login.test_admin_login", "include_links": true, "github_repo_url": "https://github.com/acme/tests", "github_token": "ghp_example" } ``` ### `CreateIssueRequest` Create body for GitHub or Jira issue creation. | Name | Type | Default | Description | | --- | --- | --- | --- | | `test_name` | `string` | required | Failure test name to file. | | `title` | `string` | required | Final issue title. Blank values are rejected. | | `body` | `string` | required | Final issue body text. | | `child_job_name` | `string` | `""` | Child pipeline job name. | | `child_build_number` | `integer >= 0` | `0` | Child build number. | | `github_token` | `string` | `""` | GitHub token used for issue creation. | | `jira_token` | `string` | `""` | Jira token used for bug creation. | | `jira_email` | `string` | `""` | Jira Cloud email paired with `jira_token`. | | `jira_project_key` | `string` | `""` | Jira project override. | | `jira_security_level` | `string` | `""` | Jira security level name. | | `github_repo_url` | `string` | `""` | GitHub repository override. | Effect: final payload for `POST /results/{job_id}/create-github-issue` and `POST /results/{job_id}/create-jira-bug`. ```json { "test_name": "tests.api.test_login.test_admin_login", "title": "Admin login fails with HTTP 500", "body": "## Failure\n`tests.api.test_login.test_admin_login`\n\n## Observed error\nAssertionError: expected 200 got 500", "github_repo_url": "https://github.com/acme/tests", "github_token": "ghp_example" } ``` ### `AnalysisDetail` Structured AI output inside each failure result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `classification` | `string` | `""` | `CODE ISSUE`, `PRODUCT BUG`, or `INFRASTRUCTURE`. | | `affected_tests` | `array` | `[]` | Related test names. | | `details` | `string` | `""` | Main analysis text. | | `artifacts_evidence` | `string` | `""` | Verbatim artifact evidence. | | `code_fix` | `object \| false \| null` | `false` | Present for `CODE ISSUE`. Shape: `{file, line, change}`. | | `product_bug_report` | `object \| false \| null` | `false` | Present for `PRODUCT BUG`. Shape: `{title, severity, component, description, evidence, jira_search_keywords, jira_matches}`. | Nested object shapes: | Object | Fields | | --- | --- | | `code_fix` | `file`, `line`, `change` | | `product_bug_report` | `title`, `severity`, `component`, `description`, `evidence`, `jira_search_keywords[]`, `jira_matches[]` | | `jira_matches[]` item | `key`, `summary`, `status`, `priority`, `url`, `score` | Effect: the core classification and evidence payload returned by analysis endpoints. ```json { "classification": "CODE ISSUE", "affected_tests": [ "tests.api.test_login.test_admin_login" ], "details": "The failure is caused by a missing null-check in the auth handler.", "artifacts_evidence": "ERROR auth/login.py:42 NoneType has no attribute 'id'", "code_fix": { "file": "auth/login.py", "line": "42", "change": "Guard the user lookup before reading user.id." } } ``` ### `PeerDebate` Optional peer-review trail attached to a failure result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `consensus_reached` | `boolean` | required | Whether the debate converged. | | `rounds_used` | `integer` | required | Number of rounds actually used. | | `max_rounds` | `integer` | required | Maximum rounds allowed for the debate. | | `ai_configs` | `array` | required | Peer configs that participated. | | `rounds` | `array` | required | Debate contributions. Each item has `round`, `ai_provider`, `ai_model`, `role`, `classification`, `details`, and `agrees_with_orchestrator`. | Effect: present in `FailureAnalysis.peer_debate` only when peer analysis was used. ```json { "consensus_reached": true, "rounds_used": 1, "max_rounds": 3, "ai_configs": [ { "ai_provider": "gemini", "ai_model": "gemini-2.5-pro" } ], "rounds": [ { "round": 1, "ai_provider": "claude", "ai_model": "sonnet", "role": "orchestrator", "classification": "CODE ISSUE", "details": "Primary analysis.", "agrees_with_orchestrator": null }, { "round": 1, "ai_provider": "gemini", "ai_model": "gemini-2.5-pro", "role": "peer", "classification": "CODE ISSUE", "details": "Agrees with the null-check diagnosis.", "agrees_with_orchestrator": true } ] } ``` ### `FailureAnalysis` One analyzed failure. | Name | Type | Default | Description | | --- | --- | --- | --- | | `test_name` | `string` | required | Failed test name. | | `error` | `string` | required | Failure message or exception text. | | `analysis` | `AnalysisDetail` | required | Structured AI output. | | `error_signature` | `string` | `""` | SHA-256 deduplication signature. | | `peer_debate` | `PeerDebate \| null` | `null` | Peer-review trail when enabled. | Effect: repeated in `AnalysisResult.failures`, `ChildJobAnalysis.failures`, and `FailureAnalysisResult.failures`. ```json { "test_name": "tests.api.test_login.test_admin_login", "error": "AssertionError: expected 200 got 500", "analysis": { "classification": "CODE ISSUE", "details": "The auth handler dereferences a null user." }, "error_signature": "4f9f8a0f..." } ``` ### `ChildJobAnalysis` Recursive pipeline child-job result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `job_name` | `string` | required | Child job name. | | `build_number` | `integer` | required | Child build number. | | `jenkins_url` | `string \| null` | `null` | Child build URL. | | `summary` | `string \| null` | `null` | Child summary text. | | `failures` | `array` | `[]` | Failures for this child. | | `failed_children` | `array` | `[]` | Nested failed child jobs. | | `note` | `string \| null` | `null` | Extra note such as recursion depth limits. | Effect: appears inside `AnalysisResult.child_job_analyses`. ```json { "job_name": "pipeline/component-tests", "build_number": 42, "jenkins_url": "https://jenkins.example.com/job/pipeline/job/component-tests/42/", "summary": "1 PRODUCT BUG in child job", "failures": [ { "test_name": "tests.component.test_ui.test_checkout", "error": "TimeoutError", "analysis": { "classification": "PRODUCT BUG", "details": "Checkout API never responds." }, "error_signature": "a1b2c3..." } ], "failed_children": [] } ``` ### `AnalysisResult` Completed Jenkins-backed analysis result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `job_id` | `string` | required | Analysis job identifier. | | `job_name` | `string` | `""` | Jenkins job name. | | `build_number` | `integer` | `0` | Jenkins build number. | | `jenkins_url` | `string \| null` | `null` | Jenkins build URL. | | `status` | `pending \| waiting \| running \| completed \| failed` | required | Analysis state stored in the result body. | | `summary` | `string` | required | Result summary. | | `ai_provider` | `string` | `""` | AI provider used. | | `ai_model` | `string` | `""` | AI model used. | | `failures` | `array` | `[]` | Top-level analyzed failures. | | `child_job_analyses` | `array` | `[]` | Recursive child-job analyses. | Effect: returned in `ResultEnvelope.result` for completed Jenkins-backed analyses. ```json { "job_id": "9f5d0a0c-32c0-4f3f-b5c4-3a5c3d35d4d0", "job_name": "folder/job-name", "build_number": 123, "jenkins_url": "https://jenkins.example.com/job/folder/job-name/123/", "status": "completed", "summary": "1 CODE ISSUE", "ai_provider": "claude", "ai_model": "sonnet", "failures": [ { "test_name": "tests.api.test_login.test_admin_login", "error": "AssertionError: expected 200 got 500", "analysis": { "classification": "CODE ISSUE", "details": "Null-check missing in auth handler." }, "error_signature": "4f9f8a0f..." } ], "child_job_analyses": [] } ``` ### `FailureAnalysisResult` Completed direct-failure analysis result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `job_id` | `string` | required | Analysis job identifier. | | `status` | `completed \| failed` | required | Direct-analysis status. | | `summary` | `string` | required | Result summary. | | `ai_provider` | `string` | `""` | AI provider used. | | `ai_model` | `string` | `""` | AI model used. | | `failures` | `array` | `[]` | Analyzed failures. | | `enriched_xml` | `string \| null` | `null` | Enriched JUnit XML when `raw_xml` input was used. | Effect: returned directly by `POST /analyze-failures` and via `GET /results/{job_id}` for completed direct analyses. ```json { "job_id": "14a2b5d1-3d67-4d4d-92f8-8b2bc0d1a8a0", "status": "completed", "summary": "1 CODE ISSUE", "ai_provider": "claude", "ai_model": "sonnet", "failures": [ { "test_name": "tests.api.test_login.test_admin_login", "error": "AssertionError: expected 200 got 500", "analysis": { "classification": "CODE ISSUE", "details": "Null-check missing in auth handler." }, "error_signature": "4f9f8a0f..." } ], "enriched_xml": "..." } ``` ### `ResultEnvelope` Stored result wrapper returned by `GET /results/{job_id}`. | Name | Type | Default | Description | | --- | --- | --- | --- | | `job_id` | `string` | required | Analysis job identifier. | | `jenkins_url` | `string` | required | Stored Jenkins URL or empty string for direct-failure analyses. | | `status` | `string` | required | Top-level persisted job status. | | `result` | `object` | required | Result payload. Completed Jenkins analyses use `AnalysisResult`; completed direct analyses use `FailureAnalysisResult`; in-progress and failed rows return partial objects. | | `created_at` | `string` | required | Row creation timestamp. | | `completed_at` | `string \| null` | `null` | Completion timestamp when available. | | `analysis_started_at` | `string \| null` | `null` | Analysis start timestamp when available. | | `capabilities` | `object` | required | Same shape as `GET /api/capabilities`. | | `base_url` | `string` | required | `PUBLIC_BASE_URL` when configured, otherwise `""`. | | `result_url` | `string` | required | Absolute URL when `base_url` is set, otherwise a relative `/results/{job_id}` path. | Additional `result` fields seen in stored rows: | Field | Type | Description | | --- | --- | --- | | `request_params` | `object` | Effective request settings used for the analysis. Mirrors the applicable request schema plus derived `tests_repo_ref`; public responses redact secret keys. | | `progress_phase` | `string` | Current progress phase for in-flight work. | | `progress_log` | `array` | Phase history entries shaped like `{phase, timestamp}`. | | `error` | `string` | Failure message for `status="failed"` rows. | Effect: canonical polling and retrieval response for stored jobs. ```json { "job_id": "9f5d0a0c-32c0-4f3f-b5c4-3a5c3d35d4d0", "jenkins_url": "https://jenkins.example.com/job/folder/job-name/123/", "status": "completed", "result": { "job_id": "9f5d0a0c-32c0-4f3f-b5c4-3a5c3d35d4d0", "job_name": "folder/job-name", "build_number": 123, "status": "completed", "summary": "1 CODE ISSUE", "ai_provider": "claude", "ai_model": "sonnet", "failures": [], "child_job_analyses": [], "request_params": { "ai_provider": "claude", "ai_model": "sonnet", "tests_repo_url": "https://github.com/acme/tests.git", "tests_repo_ref": "main", "peer_ai_configs": [], "additional_repos": [] }, "progress_phase": "saving", "progress_log": [ { "phase": "analyzing", "timestamp": 1711111111.0 } ] }, "created_at": "2026-04-18 10:20:30", "completed_at": "2026-04-18 10:22:01", "analysis_started_at": "2026-04-18 10:20:31", "capabilities": { "github_issues_enabled": true, "jira_issues_enabled": true, "server_github_token": false, "server_jira_token": true, "server_jira_email": true, "server_jira_project_key": "PROJ", "reportportal": false, "reportportal_project": "" }, "base_url": "https://jji.example.com", "result_url": "https://jji.example.com/results/9f5d0a0c-32c0-4f3f-b5c4-3a5c3d35d4d0" } ``` ### `PreviewIssueResponse` Preview response for GitHub and Jira issue content. | Name | Type | Default | Description | | --- | --- | --- | --- | | `title` | `string` | required | Generated title. | | `body` | `string` | required | Generated markdown/body text. | | `similar_issues` | `array` | `[]` | Best-effort duplicate matches. Each item may include `number`, `key`, `title`, `url`, and `status`. | Effect: returned by the preview endpoints. ```json { "title": "Admin login fails with HTTP 500", "body": "## Summary\nThe auth handler dereferences a null user.\n", "similar_issues": [ { "number": 42, "key": "", "title": "Login API returns 500 on missing user", "url": "https://github.com/acme/tests/issues/42", "status": "open" } ] } ``` ### `CreateIssueResponse` Issue creation response for GitHub and Jira. | Name | Type | Default | Description | | --- | --- | --- | --- | | `url` | `string` | required | Created issue URL. | | `key` | `string` | `""` | Jira issue key. Empty for GitHub. | | `number` | `integer` | `0` | GitHub issue number. `0` for Jira. | | `title` | `string` | required | Created issue title. | | `comment_id` | `integer` | `0` | Auto-created comment linking the issue back to the analysis. | Effect: returned by both creation endpoints. ```json { "url": "https://github.com/acme/tests/issues/57", "key": "", "number": 57, "title": "Admin login fails with HTTP 500", "comment_id": 12 } ``` ### `ReportPortalPushResult` Report Portal push result. | Name | Type | Default | Description | | --- | --- | --- | --- | | `pushed` | `integer` | required | Number of Report Portal items updated. | | `unmatched` | `array` | `[]` | Report Portal item names that were not matched or could not be mapped. | | `errors` | `array` | `[]` | User-facing integration errors. | | `launch_id` | `integer \| null` | `null` | Matched Report Portal launch ID when found. | Effect: returned by `POST /results/{job_id}/push-reportportal`. ```json { "pushed": 3, "unmatched": [ "tests.api.test_misc.test_unmapped" ], "errors": [], "launch_id": 1042 } ``` ## Analysis Endpoints ### `POST /analyze` Queue a Jenkins build for analysis. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `body` | body | `AnalyzeRequest` | required | Jenkins-backed analysis request. | Return value/effect: queues a background analysis job and returns `{status, job_id, message, base_url, result_url}`. Status codes: `202` queued; `400` invalid AI or peer configuration; `422` request validation failed. ```bash curl -sS -X POST "$BASE_URL/analyze" \ -H "Content-Type: application/json" \ -d '{ "job_name": "folder/job-name", "build_number": 123, "wait_for_completion": true, "ai_provider": "claude", "ai_model": "sonnet" }' ``` ### `POST /analyze-failures` Analyze raw failures or raw JUnit XML without Jenkins. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `body` | body | `AnalyzeFailuresRequest` | required | Direct-failure analysis request. | Return value/effect: returns a stored `FailureAnalysisResult`. When `raw_xml` is supplied, `enriched_xml` is included in the response. Status codes: `200` result returned; `400` invalid XML or invalid analysis configuration; `422` request validation failed. ```bash curl -sS -X POST "$BASE_URL/analyze-failures" \ -H "Content-Type: application/json" \ -d '{ "failures": [ { "test_name": "tests.api.test_login.test_admin_login", "error_message": "AssertionError: expected 200 got 500", "stack_trace": "Traceback..." } ], "ai_provider": "claude", "ai_model": "sonnet" }' ``` ### `POST /re-analyze/{job_id}` Queue a new Jenkins-backed analysis using a previous job’s stored request parameters, optionally overriding selected analysis fields. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Existing stored result to reconstruct from. | | `body` | body | `BaseAnalysisRequest` | required | Override fields to apply on top of the stored request parameters. | Return value/effect: reconstructs the original Jenkins request, applies only the fields explicitly present in the body, and queues a new job with a fresh `job_id`. Status codes: `202` queued; `400` stored request parameters cannot be reconstructed or original job has no `request_params`; `404` source result not found; `422` validation failed. ```bash curl -sS -X POST "$BASE_URL/re-analyze/$JOB_ID" \ -H "Content-Type: application/json" \ -d '{ "ai_provider": "gemini", "ai_model": "gemini-2.5-pro", "peer_ai_configs": [] }' ``` ## Result Endpoints ### `GET /results/{job_id}` Get the stored result for an analysis job. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | Return value/effect: returns a `ResultEnvelope`. Status codes: `200` completed or failed result; `202` job is still pending, waiting, or running; `404` job not found. > **Note:** Use `Accept: application/json` for API reads. Browser-style HTML requests are handled by the frontend and can redirect in-progress jobs to `/status/{job_id}`. ```bash curl -sS "$BASE_URL/results/$JOB_ID" \ -H "Accept: application/json" ``` ### `GET /results` List recent analysis jobs. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `limit` | query | `integer <= 100` | `50` | Maximum number of rows to return. | Return value/effect: returns an array of summary objects shaped like `{job_id, jenkins_url, status, created_at}`, ordered newest first. Status codes: `200` success; `422` invalid query value. ```bash curl -sS "$BASE_URL/results?limit=25" ``` ### `GET /api/dashboard` Get dashboard-ready result summaries. Parameters: none. Return value/effect: returns up to 500 newest rows. Each row always includes `job_id`, `jenkins_url`, `status`, `created_at`, `completed_at`, `analysis_started_at`, `reviewed_count`, and `comment_count`. When parsed result data exists, rows also include `job_name`, `build_number`, `failure_count`, optional `child_job_count`, optional `summary`, and optional `error`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/api/dashboard" ``` ### `DELETE /results/{job_id}` Delete an analysis job and all related stored data. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | Return value/effect: admin-only delete. Removes the result row plus comments, failure reviews, failure history, and test classifications for the job. Status codes: `200` deleted; `403` admin access required; `404` job not found. ```bash curl -sS -X DELETE "$BASE_URL/results/$JOB_ID" \ -H "Authorization: Bearer $ADMIN_TOKEN" ``` ### `GET /results/{job_id}/comments` Get all comments and review states for a stored job. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | Return value/effect: returns `{"comments": [...], "reviews": {...}}`. Comment item shape: | Name | Type | Default | Description | | --- | --- | --- | --- | | `id` | `integer` | required | Comment ID. | | `job_id` | `string` | required | Analysis job identifier. | | `test_name` | `string` | required | Failure test name. | | `child_job_name` | `string` | `""` | Child job scope. | | `child_build_number` | `integer` | `0` | Child build scope. | | `comment` | `string` | required | Comment text. | | `error_signature` | `string` | `""` | Deduplication signature copied from the stored failure when available. | | `username` | `string` | `""` | Username context stored with the comment. | | `created_at` | `string` | required | Comment timestamp. | Review map value shape: | Name | Type | Default | Description | | --- | --- | --- | --- | | `reviewed` | `boolean` | required | Current reviewed state. | | `username` | `string` | `""` | Username that last updated the review state. | | `updated_at` | `string` | required | Last update timestamp. | Review map keys use `test_name` for top-level failures and `child_job_name#child_build_number::test_name` for child failures. Status codes: `200` success. ```bash curl -sS "$BASE_URL/results/$JOB_ID/comments" ``` ### `POST /results/{job_id}/comments` Add a comment to a failure in a stored result. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `test_name` | body | `string` | required | Failure test name. | | `comment` | body | `string` | required | Comment text. | | `child_job_name` | body | `string` | `""` | Child job scope. | | `child_build_number` | body | `integer >= 0` | `0` | Child build scope. | Return value/effect: creates a comment row and returns `{"id": }`. The server looks up `error_signature` from the stored result and stores the current username when available. Status codes: `201` created; `202` target job still pending/waiting/running; `400` invalid child scope; `404` job or failure not found; `409` target job failed; `422` validation failed. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/comments" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "comment": "Fails only when the auth fixture runs after cache warm-up." }' ``` ### `DELETE /results/{job_id}/comments/{comment_id}` Delete a comment. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `comment_id` | path | `integer` | required | Comment identifier. | Return value/effect: returns `{"status": "deleted"}`. Admins can delete any comment; non-admin users can delete only their own comments. Status codes: `200` deleted; `401` username required; `404` comment not found or not owned by the caller. ```bash curl -sS -X DELETE "$BASE_URL/results/$JOB_ID/comments/12" \ -H "Cookie: jji_username=alice" ``` ### `PUT /results/{job_id}/reviewed` Set the reviewed state for a failure. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `test_name` | body | `string` | required | Failure test name. | | `reviewed` | body | `boolean` | required | New reviewed state. | | `child_job_name` | body | `string` | `""` | Child job scope. | | `child_build_number` | body | `integer >= 0` | `0` | Child build scope. | Return value/effect: returns `{"status": "ok", "reviewed_by": ""}`. Status codes: `200` updated; `202` target job still pending/waiting/running; `400` invalid child scope; `404` job or failure not found; `409` target job failed; `422` validation failed. ```bash curl -sS -X PUT "$BASE_URL/results/$JOB_ID/reviewed" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "reviewed": true }' ``` ### `GET /results/{job_id}/review-status` Get review counters for a job. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | Return value/effect: returns `{total_failures, reviewed_count, comment_count}`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/results/$JOB_ID/review-status" ``` ### `POST /results/{job_id}/enrich-comments` Resolve live GitHub and Jira statuses mentioned in stored comments. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | Return value/effect: returns `{"enrichments": {...}}`, where each key is a comment ID string and each value is an array of objects shaped like `{type, key, status}`. `type` is `github_pr`, `github_issue`, or `jira`. Status codes: `200` success. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/enrich-comments" ``` ### `PUT /results/{job_id}/override-classification` Override a stored failure classification. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `test_name` | body | `string` | required | Representative test in the signature group to override. | | `classification` | body | `CODE ISSUE \| PRODUCT BUG \| INFRASTRUCTURE` | required | New classification. | | `child_job_name` | body | `string` | `""` | Child job scope. | | `child_build_number` | body | `integer >= 0` | `0` | Child build scope. Must be non-zero when `child_job_name` is set. | Return value/effect: returns `{"status": "ok", "classification": ""}`. The override is mirrored into stored history, applied to the matching failure group within the same job, and patched into stored `result_json` for future reads. Status codes: `200` updated; `202` target job still pending/waiting/running; `400` invalid child scope; `404` job or failure not found; `409` target job failed; `422` validation failed. ```bash curl -sS -X PUT "$BASE_URL/results/$JOB_ID/override-classification" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "classification": "CODE ISSUE" }' ``` ## History Endpoints ### `GET /history/failures` Get paginated failure history. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `search` | query | `string` | `""` | Free-text search across `test_name`, `error_message`, and `job_name`. | | `job_name` | query | `string` | `""` | Exact top-level job-name filter. | | `classification` | query | `string` | `""` | Exact classification filter. | | `limit` | query | `integer <= 200` | `50` | Maximum rows to return. | | `offset` | query | `integer >= 0` | `0` | Pagination offset. | Return value/effect: returns `{"failures": [...], "total": }`. Each row includes `id`, `job_id`, `job_name`, `build_number`, `test_name`, `error_message`, `error_signature`, `classification`, `child_job_name`, `child_build_number`, and `analyzed_at`. Status codes: `200` success; `422` invalid query value. ```bash curl -sS "$BASE_URL/history/failures?job_name=folder/job-name&classification=CODE%20ISSUE&limit=20" ``` ### `GET /history/test/{test_name:path}` Get recent history for one test. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `test_name` | path | `string` | required | Full test name. Path form allows embedded slashes. | | `limit` | query | `integer <= 100` | `20` | Maximum `recent_runs` rows to return. | | `job_name` | query | `string` | `""` | Exact top-level job-name filter. | | `exclude_job_id` | query | `string` | `""` | Exclude one analysis job from the result. | Return value/effect: returns an object with `test_name`, `total_runs`, `failures`, `passes`, `failure_rate`, `first_seen`, `last_seen`, `last_classification`, `classifications`, `recent_runs`, `comments`, `consecutive_failures`, and `note`. Field notes: | Field | Type | Description | | --- | --- | --- | | `classifications` | `object` | Count map keyed by classification. | | `recent_runs` | `array` | Rows with `job_id`, `job_name`, `build_number`, `error_message`, `error_signature`, `classification`, `child_job_name`, `child_build_number`, and `analyzed_at`. | | `comments` | `array` | Related comments shaped like `{comment, username, created_at}`. | | `passes` | `integer \| null` | `null` when `job_name` is omitted, because only failures are stored. | | `failure_rate` | `number \| null` | `null` when `job_name` is omitted. | Status codes: `200` success; `422` invalid query value. ```bash curl -sS "$BASE_URL/history/test/tests.api.test_login.test_admin_login?limit=10&job_name=folder/job-name" ``` ### `GET /history/search` Find tests sharing an error signature. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `signature` | query | `string` | required | Error signature hash to search for. | | `exclude_job_id` | query | `string` | `""` | Exclude one analysis job from the result. | Return value/effect: returns `{signature, total_occurrences, unique_tests, tests, last_classification, comments}`. `tests` items are `{test_name, occurrences}`. `comments` items are `{comment, username, created_at}`. Status codes: `200` success; `422` missing or invalid query value. ```bash curl -sS "$BASE_URL/history/search?signature=4f9f8a0f..." ``` ### `GET /history/stats/{job_name:path}` Get aggregate statistics for a top-level job. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_name` | path | `string` | required | Top-level Jenkins job name. Path form allows embedded slashes. | | `exclude_job_id` | query | `string` | `""` | Exclude one analysis job from the calculation. | Return value/effect: returns `{job_name, total_builds_analyzed, builds_with_failures, overall_failure_rate, most_common_failures, recent_trend}`. `most_common_failures` items are `{test_name, count, classification}`. `recent_trend` is `improving`, `worsening`, or `stable`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/history/stats/folder/job-name" ``` ### `POST /history/classify` Store a history-domain classification for a test. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `test_name` | body | `string` | required | Test name to classify. Blank-after-trim is rejected. | | `classification` | body | `FLAKY \| REGRESSION \| INFRASTRUCTURE \| KNOWN_BUG \| INTERMITTENT` | required | History classification. Input is normalized to uppercase. | | `reason` | body | `string` | `""` | Free-text reasoning. | | `job_name` | body | `string` | `""` | Child job name context. Empty string means top-level. | | `references` | body | `string` | `""` | External references. Required for `KNOWN_BUG`. | | `job_id` | body | `string` | required | Analysis job identifier used to scope the classification. | | `child_build_number` | body | `integer >= 0` | `0` | Child build scope. | Return value/effect: creates a classification row and returns `{"id": }`. Status codes: `201` created; `400` invalid business rule such as blank `test_name` or missing `references` for `KNOWN_BUG`; `422` validation failed. > **Note:** Requests with a username context are stored as human-authored and immediately visible. Requests without a username context are stored as `created_by="ai"` and remain hidden until the related analysis makes them visible. ```bash curl -sS -X POST "$BASE_URL/history/classify" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "classification": "FLAKY", "reason": "Intermittent timeout in shared CI", "job_id": "'"$JOB_ID"'" }' ``` ### `GET /history/classifications` Read visible primary override classifications. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `test_name` | query | `string` | `""` | Exact test-name filter. | | `classification` | query | `string` | `""` | Exact primary classification filter. | | `job_name` | query | `string` | `""` | Exact child-job-name filter stored with the classification. | | `parent_job_name` | query | `string` | `""` | Exact top-level job-name filter. | | `job_id` | query | `string` | `""` | Exact analysis job filter. | Return value/effect: returns `{"classifications": [...]}`. Each row includes `id`, `test_name`, `job_name`, `parent_job_name`, `classification`, `reason`, `references_info`, `created_by`, `job_id`, `child_build_number`, and `created_at`. Status codes: `200` success. > **Note:** This reader intentionally returns only visible primary classifications (`CODE ISSUE`, `PRODUCT BUG`, `INFRASTRUCTURE`). History labels from `POST /history/classify` are not returned here. ```bash curl -sS "$BASE_URL/history/classifications?test_name=tests.api.test_login.test_admin_login" ``` ## Issue and Integration Endpoints ### `POST /results/{job_id}/preview-github-issue` Generate GitHub issue content for a stored failure. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `body` | body | `PreviewIssueRequest` | required | Preview request. | Return value/effect: returns `PreviewIssueResponse`. The server resolves the effective stored classification first, so manual overrides affect the generated content. Duplicate search is best-effort and only runs when a target repo URL and GitHub token are available. Status codes: `200` preview returned; `202` target job still pending/waiting/running; `400` target repository URL cannot be resolved or test lookup is invalid; `403` GitHub issue creation disabled; `404` job or failure not found; `409` target job failed; `422` validation failed. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/preview-github-issue" \ -H "Content-Type: application/json" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "include_links": true, "github_repo_url": "https://github.com/acme/tests", "github_token": "ghp_example" }' ``` ### `POST /results/{job_id}/preview-jira-bug` Generate Jira bug content for a stored failure. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `body` | body | `PreviewIssueRequest` | required | Preview request. | Return value/effect: returns `PreviewIssueResponse`. Duplicate search is best-effort and runs only when usable Jira credentials and a Jira project key are available. Status codes: `200` preview returned; `202` target job still pending/waiting/running; `400` Jira URL not configured or target lookup invalid; `403` Jira issue creation disabled; `404` job or failure not found; `409` target job failed; `422` validation failed. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/preview-jira-bug" \ -H "Content-Type: application/json" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "include_links": true, "jira_token": "jira_example", "jira_email": "alice@example.com", "jira_project_key": "PROJ" }' ``` ### `POST /results/{job_id}/create-github-issue` Create a GitHub issue for a stored failure. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `body` | body | `CreateIssueRequest` | required | Final issue payload and optional tracker overrides. | Return value/effect: returns `CreateIssueResponse`. When a username context exists, the server appends reporter attribution to the issue body and creates a matching comment on the analysis report. Status codes: `201` created; `202` target job still pending/waiting/running; `400` missing GitHub token or target repository URL, or invalid repository URL; `401` GitHub token invalid or expired; `403` GitHub issue creation disabled; `404` job or failure not found; `409` target job failed; `422` validation failed; `502` GitHub API error or unreachable response. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/create-github-issue" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "title": "Admin login fails with HTTP 500", "body": "## Failure\n`tests.api.test_login.test_admin_login`\n\n## Error\nAssertionError: expected 200 got 500", "github_repo_url": "https://github.com/acme/tests", "github_token": "ghp_example" }' ``` ### `POST /results/{job_id}/create-jira-bug` Create a Jira bug for a stored failure. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `body` | body | `CreateIssueRequest` | required | Final bug payload and optional tracker overrides. | Return value/effect: returns `CreateIssueResponse`. When a username context exists, the server appends reporter attribution to the Jira description and creates a matching comment on the analysis report. Status codes: `201` created; `202` target job still pending/waiting/running; `400` Jira URL not configured, missing Jira project key, or missing Jira credentials; `401` Jira token invalid or expired; `403` Jira issue creation disabled; `404` job or failure not found; `409` target job failed; `422` validation failed; `502` Jira API error or unreachable response. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/create-jira-bug" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "test_name": "tests.api.test_login.test_admin_login", "title": "Admin login fails with HTTP 500", "body": "Failure observed in CI pipeline build 123.", "jira_token": "jira_example", "jira_email": "alice@example.com", "jira_project_key": "PROJ", "jira_security_level": "Internal" }' ``` ### `POST /results/{job_id}/push-reportportal` Push stored classifications into Report Portal. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `job_id` | path | `string` | required | Analysis job identifier. | | `child_job_name` | query | `string \| null` | `null` | Child job name for a scoped push. | | `child_build_number` | query | `integer \| null` | `null` | Child build number for a scoped push. Required when `child_job_name` is supplied. | Return value/effect: returns `ReportPortalPushResult`. Status codes: `200` result returned; `400` Report Portal disabled/not configured, invalid child scope, or `PUBLIC_BASE_URL` missing; `404` job not found; `422` validation failed. > **Note:** Many integration problems remain HTTP `200` and are reported in `errors[]`. Use the body, not only the status code, to determine success. ```bash curl -sS -X POST "$BASE_URL/results/$JOB_ID/push-reportportal" ``` ### `GET /api/capabilities` Get server feature toggles and credential availability. Parameters: none. Return value/effect: returns an object with the fields below. | Name | Type | Default | Description | | --- | --- | --- | --- | | `github_issues_enabled` | `boolean` | required | Whether GitHub issue creation is enabled. | | `jira_issues_enabled` | `boolean` | required | Whether Jira issue creation is enabled. | | `server_github_token` | `boolean` | required | Whether the server has its own GitHub token configured. | | `server_jira_token` | `boolean` | required | Whether the server has Jira credentials configured. | | `server_jira_email` | `boolean` | required | Whether the server has a Jira Cloud email configured. | | `server_jira_project_key` | `string` | required | Configured default Jira project key, or `""`. | | `reportportal` | `boolean` | required | Whether Report Portal integration is enabled and configured. | | `reportportal_project` | `string` | required | Configured Report Portal project, or `""`. | Status codes: `200` success. ```bash curl -sS "$BASE_URL/api/capabilities" ``` ### `POST /api/jira-projects` List Jira projects visible to the supplied Jira credentials. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `jira_token` | body | `string` | `""` | Jira token. | | `jira_email` | body | `string` | `""` | Jira Cloud email. | | `query` | body | `string` | `""` | Free-text project filter. | Return value/effect: returns an array of `{key, name}` objects. If no user token is supplied, the endpoint returns only the server-configured project key when available. Status codes: `200` success. ```bash curl -sS -X POST "$BASE_URL/api/jira-projects" \ -H "Content-Type: application/json" \ -d '{ "jira_token": "jira_example", "jira_email": "alice@example.com", "query": "PROJ" }' ``` ### `POST /api/jira-security-levels` List Jira security levels for a project. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `jira_token` | body | `string` | `""` | Jira token. | | `jira_email` | body | `string` | `""` | Jira Cloud email. | | `project_key` | body | `string` | required | Jira project key. | Return value/effect: returns an array of `{id, name, description}` objects. Failures are swallowed and returned as `[]`. Status codes: `200` success; `422` validation failed. ```bash curl -sS -X POST "$BASE_URL/api/jira-security-levels" \ -H "Content-Type: application/json" \ -d '{ "jira_token": "jira_example", "jira_email": "alice@example.com", "project_key": "PROJ" }' ``` ### `POST /api/validate-token` Validate a GitHub or Jira token. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `token_type` | body | `github \| jira` | required | Tracker type to validate. | | `token` | body | `string` | required | Tracker token. | | `email` | body | `string` | `""` | Jira Cloud email. Ignored for GitHub. | Return value/effect: returns `{valid, username, message}`. For GitHub, `username` is the GitHub login. For Jira, `username` is the display name. Status codes: `200` validation result; `422` validation failed. > **Note:** Invalid tokens, missing tokens, and unreachable trackers still return HTTP `200`; inspect `valid` and `message`. ```bash curl -sS -X POST "$BASE_URL/api/validate-token" \ -H "Content-Type: application/json" \ -d '{ "token_type": "github", "token": "ghp_example" }' ``` ## Authentication and User Endpoints ### `POST /api/auth/login` Authenticate using the bootstrap admin key or an admin user API key. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `username` | body | `string` | required | Login username. Use literal `admin` when authenticating with the bootstrap admin key. | | `api_key` | body | `string` | required | Bootstrap admin key or stored admin user API key. | Return value/effect: returns `{username, role, is_admin}` and sets `jji_session` plus `jji_username` cookies. Status codes: `200` logged in; `400` invalid JSON body or missing fields; `401` invalid credentials. ```bash curl -i -sS -X POST "$BASE_URL/api/auth/login" \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "api_key": "'"$ADMIN_KEY"'" }' ``` ### `POST /api/auth/logout` Log out the current admin session. Parameters: none. Return value/effect: returns `{"ok": true}` and clears the `jji_session` cookie. The `jji_username` cookie is not removed. Status codes: `200` success. ```bash curl -sS -X POST "$BASE_URL/api/auth/logout" \ -H "Cookie: jji_session=$SESSION_TOKEN" ``` ### `GET /api/auth/me` Get the current request identity. Parameters: none. Return value/effect: returns `{username, role, is_admin}`. Unauthenticated requests return `{"username": "", "role": "user", "is_admin": false}`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/api/auth/me" \ -H "Cookie: jji_username=alice" ``` ### `GET /api/user/tokens` Get the saved personal GitHub and Jira tokens for the current username context. Parameters: none. Return value/effect: returns `{github_token, jira_email, jira_token}` and sends `Cache-Control: no-store`. If the current username is not tracked in the local user table, all values are returned as empty strings. Status codes: `200` success; `401` username required. ```bash curl -sS "$BASE_URL/api/user/tokens" \ -H "Cookie: jji_username=alice" ``` ### `PUT /api/user/tokens` Save personal tracker credentials for the current username context. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `github_token` | body | `string` | omitted | GitHub token. Empty string clears the stored value. | | `jira_email` | body | `string` | omitted | Jira Cloud email. Empty string clears the stored value. | | `jira_token` | body | `string` | omitted | Jira token. Empty string clears the stored value. | Return value/effect: updates only the fields present in the JSON object. Omitted fields are left unchanged. Returns `{"ok": true}`. Status codes: `200` saved; `400` invalid JSON body or non-object body; `401` username required; `404` user not found. ```bash curl -sS -X PUT "$BASE_URL/api/user/tokens" \ -H "Content-Type: application/json" \ -H "Cookie: jji_username=alice" \ -d '{ "github_token": "ghp_example", "jira_email": "alice@example.com", "jira_token": "jira_example" }' ``` ## Admin Endpoints ### `POST /api/admin/users` Create a new admin user. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `username` | body | `string` | required | New admin username. Must be 2-50 characters, start alphanumeric, and then use only alphanumerics, `.`, `_`, or `-`. `admin` is reserved. | Return value/effect: returns `{username, api_key, role}` with `role="admin"` and sends `Cache-Control: no-store`. Status codes: `200` created; `400` invalid JSON, invalid username, or duplicate username; `403` admin access required. ```bash curl -sS -X POST "$BASE_URL/api/admin/users" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "username": "release-manager" }' ``` ### `GET /api/admin/users` List all tracked users. Parameters: none. Return value/effect: returns `{"users": [...]}`. Each user row includes `id`, `username`, `role`, `created_at`, and `last_seen`. Status codes: `200` success; `403` admin access required. ```bash curl -sS "$BASE_URL/api/admin/users" \ -H "Authorization: Bearer $ADMIN_TOKEN" ``` ### `DELETE /api/admin/users/{username}` Delete an admin user. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `username` | path | `string` | required | Admin username to delete. | Return value/effect: returns `{"deleted": ""}`. Active sessions for that user are removed. Status codes: `200` deleted; `400` cannot delete your own account or the last admin; `403` admin access required; `404` admin user not found. ```bash curl -sS -X DELETE "$BASE_URL/api/admin/users/release-manager" \ -H "Authorization: Bearer $ADMIN_TOKEN" ``` ### `PUT /api/admin/users/{username}/role` Change a user’s role. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `username` | path | `string` | required | Username to update. | | `role` | body | `admin \| user` | required | New role. | Return value/effect: returns `{username, role}` and, when promoting to admin, also returns `api_key`. Responses use `Cache-Control: no-store`. Demotion removes the stored API key and invalidates that user’s sessions. Status codes: `200` updated; `400` invalid JSON, invalid role, same role, self-change, reserved `admin`, or last-admin demotion; `403` admin access required; `404` user not found. ```bash curl -sS -X PUT "$BASE_URL/api/admin/users/alice/role" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "role": "admin" }' ``` ### `POST /api/admin/users/{username}/rotate-key` Rotate an admin user API key. | Name | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `username` | path | `string` | required | Admin username whose key will be rotated. | | `new_key` | body | `string \| omitted` | omitted | Optional replacement API key. Must be at least 16 characters when supplied. If omitted, the server generates a new key. | Return value/effect: returns `{username, new_api_key}` and sends `Cache-Control: no-store`. Existing sessions for that user are invalidated. Status codes: `200` rotated; `400` invalid JSON, non-object body, or invalid custom key; `403` admin access required; `404` admin user not found. ```bash curl -sS -X POST "$BASE_URL/api/admin/users/alice/rotate-key" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "new_key": "jji_custom_admin_key_123456" }' ``` ## Service Endpoints ### `GET /ai-configs` List distinct AI provider/model pairs found in completed stored analyses. Parameters: none. Return value/effect: returns an array of objects shaped like `{ai_provider, ai_model}`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/ai-configs" ``` ### `GET /health` Basic health check. Parameters: none. Return value/effect: returns `{"status": "healthy"}`. Status codes: `200` success. ```bash curl -sS "$BASE_URL/health" ``` ## Related Pages - [CLI Command Reference](cli-command-reference.html) - [Configuration and Environment Reference](configuration-and-environment-reference.html) - [Analyzing Jenkins Jobs](analyzing-jenkins-jobs.html) - [Analyzing JUnit XML and Raw Failures](analyzing-junit-xml-and-raw-failures.html) - [Managing Admin Users and API Keys](managing-admin-users-and-api-keys.html) --- Source: configuration-and-environment-reference.md # Configuration and Environment Reference > **Note:** `Settings`-backed variables support a local `.env` file. Variables marked `process env` are read directly with `os.getenv()` and must already be in the real environment when the server or CLI process starts. `docker-compose.yaml` in this repository injects `.env` into the container, so both kinds work there. ## Server Environment Variables ### Analysis Runtime Default AI selection and timeout settings for analysis requests. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `AI_PROVIDER` | string | `unset` | `process env` | Primary analysis provider. Valid values: `claude`, `gemini`, `cursor`. Required unless the request body provides `ai_provider`. | | `AI_MODEL` | string | `unset` | `process env` | Primary analysis model. Required unless the request body provides `ai_model`. | | `AI_CLI_TIMEOUT` | integer (minutes) | `10` | `Settings` | Timeout for AI CLI calls. | ```bash AI_PROVIDER=claude AI_MODEL=claude-opus-4-1 AI_CLI_TIMEOUT=15 ``` Effect: `POST /analyze` and `POST /analyze-failures` use these values when the request body omits them. Missing or invalid `AI_PROVIDER`/`AI_MODEL` returns `400`. ### Jenkins Connection and Artifact Collection Default Jenkins access, wait behavior, and artifact download limits for Jenkins-backed analysis. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `JENKINS_URL` | string | `""` | `Settings` | Default Jenkins base URL for `POST /analyze`. | | `JENKINS_USER` | string | `""` | `Settings` | Default Jenkins username. | | `JENKINS_PASSWORD` | string | `""` | `Settings` | Default Jenkins password or API token. | | `JENKINS_SSL_VERIFY` | boolean | `true` | `Settings` | TLS verification for Jenkins requests. | | `WAIT_FOR_COMPLETION` | boolean | `true` | `Settings` | Default wait behavior before starting analysis. | | `POLL_INTERVAL_MINUTES` | integer (minutes) | `2` | `Settings` | Default poll interval while waiting for Jenkins completion. | | `MAX_WAIT_MINUTES` | integer (minutes) | `0` | `Settings` | Default maximum wait time. `0` means no limit. | | `GET_JOB_ARTIFACTS` | boolean | `true` | `Settings` | Default artifact download toggle. | | `JENKINS_ARTIFACTS_MAX_SIZE_MB` | integer (MB) | `500` | `Settings` | Maximum total artifact size processed for AI context. | ```bash JENKINS_URL=https://jenkins.example.com JENKINS_USER=svc-jji JENKINS_PASSWORD=your-api-token JENKINS_SSL_VERIFY=true WAIT_FOR_COMPLETION=true POLL_INTERVAL_MINUTES=2 MAX_WAIT_MINUTES=30 GET_JOB_ARTIFACTS=true JENKINS_ARTIFACTS_MAX_SIZE_MB=500 ``` Effect: These defaults apply only to `POST /analyze`. If `WAIT_FOR_COMPLETION=true` but no Jenkins URL is available after request merge, the server skips the waiting phase. Artifact processing runs only when `GET_JOB_ARTIFACTS=true` and the Jenkins build exposes artifacts. ### Repository Context and Peer Analysis Default repository cloning and multi-model review settings. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `TESTS_REPO_URL` | string | `unset` | `Settings` | Default tests repository URL. `url:ref` syntax is accepted. | | `ADDITIONAL_REPOS` | string | `""` | `Settings` | Default additional repo list in `name:url,name:url` format. Each URL may also use `url:ref`. | | `PEER_AI_CONFIGS` | string | `""` | `Settings` | Default peer analysis list in `provider:model,provider:model` format. | | `PEER_ANALYSIS_MAX_ROUNDS` | integer | `3` | `Settings` | Maximum peer debate rounds. Allowed range: `1`-`10`. | ```bash TESTS_REPO_URL=https://github.com/acme/tests:release-1.2 ADDITIONAL_REPOS=infra:https://github.com/acme/infra:main,product:https://github.com/acme/product PEER_AI_CONFIGS=cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro PEER_ANALYSIS_MAX_ROUNDS=3 ``` Effect: `TESTS_REPO_URL` is cloned into the analysis workspace. `ADDITIONAL_REPOS` entries are cloned into named subdirectories. Malformed repo or peer strings fail when the request resolves them. ### Jira Analysis and Issue Toggles Default Jira connection settings for analysis-time enrichment and Jira issue operations. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `JIRA_URL` | string | `unset` | `Settings` | Jira base URL. | | `JIRA_EMAIL` | string | `unset` | `Settings` | Jira Cloud email. When present, Jira auth resolves in Cloud mode. | | `JIRA_API_TOKEN` | string | `unset` | `Settings` | Jira Cloud API token. | | `JIRA_PAT` | string | `unset` | `Settings` | Jira Server/DC personal access token. | | `JIRA_PROJECT_KEY` | string | `unset` | `Settings` | Default Jira project key for search and issue targeting. | | `JIRA_SSL_VERIFY` | boolean | `true` | `Settings` | TLS verification for Jira requests. | | `JIRA_MAX_RESULTS` | integer | `5` | `Settings` | Maximum Jira results returned per search. | | `ENABLE_JIRA` | boolean | `unset` | `Settings` | Analysis-time Jira enrichment toggle. | | `ENABLE_JIRA_ISSUES` | boolean | `unset` | `Settings` | Jira preview/create toggle. `false` disables Jira issue endpoints. | ```bash JIRA_URL=https://acme.atlassian.net JIRA_EMAIL=triage@example.com JIRA_API_TOKEN=your-jira-token JIRA_PROJECT_KEY=PROJ JIRA_SSL_VERIFY=true JIRA_MAX_RESULTS=5 ENABLE_JIRA=true ENABLE_JIRA_ISSUES=true ``` Effect: Analysis-time Jira enrichment runs only when `ENABLE_JIRA` is not `false` and Jira URL, credentials, and project key are all present. Jira issue preview/create is controlled independently by `ENABLE_JIRA_ISSUES`. Auth resolution is: - with `JIRA_EMAIL`: Cloud mode, token preference `JIRA_API_TOKEN` then `JIRA_PAT` - without `JIRA_EMAIL`: Server/DC mode, token preference `JIRA_PAT` then `JIRA_API_TOKEN` ### GitHub Issue Creation Default GitHub credential and feature toggle settings. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `GITHUB_TOKEN` | string | `unset` | `Settings` | Default server GitHub token. | | `ENABLE_GITHUB_ISSUES` | boolean | `unset` | `Settings` | GitHub preview/create toggle. `false` disables GitHub issue endpoints. | ```bash GITHUB_TOKEN=ghp_your_token ENABLE_GITHUB_ISSUES=true ``` Effect: `ENABLE_GITHUB_ISSUES=false` blocks GitHub issue preview/create. When the toggle is not `false`, `/api/capabilities` reports server token presence separately through `server_github_token`. ### Report Portal and Public URLs Default Report Portal connection settings and absolute-link base URL. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `REPORTPORTAL_URL` | string | `unset` | `Settings` | Report Portal base URL. | | `REPORTPORTAL_API_TOKEN` | string | `unset` | `Settings` | Report Portal API token. | | `REPORTPORTAL_PROJECT` | string | `unset` | `Settings` | Report Portal project name. | | `REPORTPORTAL_VERIFY_SSL` | boolean | `true` | `Settings` | TLS verification for Report Portal requests. | | `ENABLE_REPORTPORTAL` | boolean | `unset` | `Settings` | Explicit Report Portal toggle. `false` disables the integration. | | `PUBLIC_BASE_URL` | string | `unset` | `Settings` | Trusted external base URL used when building absolute links. Trailing `/` is stripped. | ```bash REPORTPORTAL_URL=https://rp.example.com REPORTPORTAL_API_TOKEN=rp-token REPORTPORTAL_PROJECT=e2e REPORTPORTAL_VERIFY_SSL=true ENABLE_REPORTPORTAL=true PUBLIC_BASE_URL=https://jji.example.com ``` Effect: Report Portal is enabled only when `ENABLE_REPORTPORTAL` is not `false` and URL, token, and project are all set. `PUBLIC_BASE_URL` controls absolute `result_url` values and is required for `POST /results/{job_id}/push-reportportal`. When `PUBLIC_BASE_URL` is unset, the server returns relative URLs. > **Note:** Request headers do not change `PUBLIC_BASE_URL`. When unset, `base_url` is `""` and `result_url` is relative. ### Authentication and Secret Handling Bootstrap admin authentication, cookie security, and at-rest encryption. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `ADMIN_KEY` | string | `""` | `Settings` | Bootstrap admin secret. `POST /api/auth/login` accepts it only with username `admin`. Bearer `Authorization: Bearer ` also authenticates as admin. | | `SECURE_COOKIES` | boolean | `true` | `Settings` | `Secure` attribute for `jji_session` and `jji_username` cookies. | | `JJI_ENCRYPTION_KEY` | string | `unset` | `process env` | Secret used to encrypt stored sensitive values and HMAC-hash stored admin API keys. | | `XDG_DATA_HOME` | path | `~/.local/share` | `process env` | Base directory for the fallback encryption key file when `JJI_ENCRYPTION_KEY` is unset. | ```bash ADMIN_KEY=change-this-admin-secret SECURE_COOKIES=true JJI_ENCRYPTION_KEY=change-this-encryption-secret XDG_DATA_HOME=/var/lib/jji ``` Effect: Request auth order is `jji_session` cookie, then Bearer token, then `jji_username` cookie. Admin login sets: - `jji_session`: `HttpOnly`, `SameSite=Strict`, `max-age=8h` - `jji_username`: `SameSite=Lax`, `max-age=1 year` If `JJI_ENCRYPTION_KEY` is unset, the server creates and reuses `$XDG_DATA_HOME/jji/.encryption_key` or `~/.local/share/jji/.encryption_key` with mode `0600`. > **Warning:** Changing `JJI_ENCRYPTION_KEY` invalidates stored API key hashes and can leave previously stored encrypted request values undecryptable. ### Storage and Process Runtime Database location, bind port, reload mode, and log level. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `DB_PATH` | path | `/data/results.db` | `process env` | SQLite database path. | | `PORT` | integer | `8000` | `process env` | Application port. Valid range: `1`-`65535`. | | `DEBUG` | boolean | `false` | `process env` | Enables `uvicorn` reload when the app is started through `jenkins_job_insight.main:run`. | | `LOG_LEVEL` | string | `INFO` | `process env` | Process-wide log level. | ```bash DB_PATH=/var/lib/jji/results.db PORT=8000 DEBUG=false LOG_LEVEL=INFO ``` Effect: `PORT` is used both for server binding and for the app's internal self-references. Invalid `PORT` values stop startup. ### Provider CLI Passthrough and Container Dev Mode Provider-specific auth variables and container-only dev-mode switches. | Name | Type | Default | Loaded by | Description | | --- | --- | --- | --- | --- | | `ANTHROPIC_API_KEY` | string | `unset` | `process env` | Claude CLI API key. | | `CLAUDE_CODE_USE_VERTEX` | boolean/string | `unset` | `process env` | Enables Claude CLI Vertex mode when set. | | `CLOUD_ML_REGION` | string | `unset` | `process env` | Claude Vertex region. | | `ANTHROPIC_VERTEX_PROJECT_ID` | string | `unset` | `process env` | Claude Vertex project ID. | | `GEMINI_API_KEY` | string | `unset` | `process env` | Gemini CLI API key. | | `CURSOR_API_KEY` | string | `unset` | `process env` | Cursor CLI API key. | | `DEV_MODE` | boolean | `unset` | `process env` | Container entrypoint dev mode. Starts the Vite dev server and adds `uvicorn --reload` behavior. | ```bash ANTHROPIC_API_KEY=your-anthropic-key # or Claude via Vertex: CLAUDE_CODE_USE_VERTEX=1 CLOUD_ML_REGION=us-east5 ANTHROPIC_VERTEX_PROJECT_ID=my-gcp-project GEMINI_API_KEY=your-gemini-key CURSOR_API_KEY=your-cursor-key DEV_MODE=true ``` Effect: These variables are not parsed by `Settings`; they are consumed by installed provider CLIs or by `entrypoint.sh`. In the container, `DEV_MODE=true` starts the frontend dev server on port `5173` and appends `--reload --reload-dir /app/src` to `uvicorn`. ## Docker Compose ### `docker-compose.yaml` Service: `jenkins-job-insight` Repository-provided container settings for the combined API and web UI service. | Name | Type | Default | Description | | --- | --- | --- | --- | | `services.jenkins-job-insight.build.context` | path | `.` | Build context. | | `services.jenkins-job-insight.build.dockerfile` | path | `Dockerfile` | Dockerfile path. | | `services.jenkins-job-insight.container_name` | string | `jenkins-job-insight` | Container name. | | `services.jenkins-job-insight.ports` | list | `["8000:8000"]` | Publishes the combined web UI and API port. | | `services.jenkins-job-insight.volumes` | list | `["./data:/data"]` | Persists SQLite data on the host. | | `services.jenkins-job-insight.env_file` | list | `[".env"]` | Injects environment variables from `.env`. | | `services.jenkins-job-insight.restart` | string | `unless-stopped` | Restart policy. | | `services.jenkins-job-insight.healthcheck.test` | array | `["CMD","curl","-f","http://localhost:8000/health"]` | Health check command. | | `services.jenkins-job-insight.healthcheck.interval` | duration | `30s` | Health check interval. | | `services.jenkins-job-insight.healthcheck.timeout` | duration | `10s` | Health check timeout. | | `services.jenkins-job-insight.healthcheck.retries` | integer | `3` | Health check retry count. | | `services.jenkins-job-insight.healthcheck.start_period` | duration | `10s` | Health check start period. | ```yaml services: jenkins-job-insight: build: context: . dockerfile: Dockerfile container_name: jenkins-job-insight ports: - "8000:8000" volumes: - ./data:/data env_file: - .env restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s ``` Effect: The service stores its database under the host `./data` directory and exposes both the React UI and API on port `8000`. > **Note:** `docker-compose.yaml` includes commented optional settings for: > - `5173:5173` frontend HMR port publishing > - `./src:/app/src` and `./frontend:/app/frontend` source mounts > - `~/.config/gcloud:/home/appuser/.config/gcloud:ro` for Claude Vertex credentials ## CLI Configuration File ### File Location and Sections Named server profiles for the `jji` CLI. | Name | Type | Default | Description | | --- | --- | --- | --- | | ``$XDG_CONFIG_HOME/jji/config.toml`` | path | `~/.config/jji/config.toml` | Main CLI config file location. | | `[default].server` | string | `""` | Default server profile name. | | `[defaults]` | mapping | `{}` | Shared values merged into every server profile. | | `[servers.]` | mapping | required per profile | Named server profiles. Each profile requires `url`. | ```toml [default] server = "prod" [defaults] ai_provider = "claude" ai_model = "claude-opus-4-1" [servers.prod] url = "https://jji.example.com" username = "alice" no_verify_ssl = false ``` Effect: `[defaults]` values are merged first, then `[servers.]` overrides them. `[defaults].server` is not supported. `servers..url` and `[default].server` must be non-empty trimmed strings. ### Connection Profile Fields Fields used to connect the CLI to a JJI server. | Name | Type | Default | Description | | --- | --- | --- | --- | | `url` | string | none | Base URL of the JJI server. Required for each profile. | | `username` | string | `""` | Username cookie sent by the CLI for user-scoped actions such as comments and reviews. | | `no_verify_ssl` | boolean | `false` | Disable TLS verification for CLI HTTP requests. | | `api_key` | string | `""` | Admin API key sent as a Bearer token by the CLI. | ```toml [servers.prod] url = "https://jji.example.com" username = "alice" no_verify_ssl = false api_key = "admin-api-key" ``` Effect: If `--server` or `JJI_SERVER` is a full URL, the CLI does not inherit any other values from `config.toml`. ### Analysis Default Fields Client-side defaults that `jji analyze` can send when you omit CLI flags. | Name | Type | Default | Description | | --- | --- | --- | --- | | `jenkins_url` | string | `""` | Default Jenkins URL. | | `jenkins_user` | string | `""` | Default Jenkins username. | | `jenkins_password` | string | `""` | Default Jenkins password or API token. | | `jenkins_ssl_verify` | boolean | `unset` | Default Jenkins TLS verification. | | `tests_repo_url` | string | `""` | Default tests repo URL. | | `ai_provider` | string | `""` | Default AI provider. | | `ai_model` | string | `""` | Default AI model. | | `ai_cli_timeout` | integer | `0` | CLI sentinel for "use server default". | | `jira_url` | string | `""` | Default Jira URL. | | `jira_email` | string | `""` | Default Jira email. | | `jira_api_token` | string | `""` | Default Jira API token. | | `jira_pat` | string | `""` | Default Jira PAT. | | `jira_project_key` | string | `""` | Default Jira project key. | | `jira_ssl_verify` | boolean | `unset` | Default Jira TLS verification. | | `jira_max_results` | integer | `0` | CLI sentinel for "use server default". | | `enable_jira` | boolean | `unset` | Default Jira enrichment toggle. | | `github_token` | string | `""` | Default GitHub token. | | `peers` | string | `""` | Default peer configs in `provider:model,provider:model` format. | | `peer_analysis_max_rounds` | integer | `0` | CLI sentinel for "use server default". | | `additional_repos` | string | `""` | Default additional repo list in `name:url,name:url` format. | | `wait_for_completion` | boolean | `unset` | Default wait toggle. | | `poll_interval_minutes` | integer | `0` | CLI sentinel for "use server default". | | `max_wait_minutes` | integer | `0` | CLI sentinel for "use server default". | ```toml [defaults] jenkins_url = "https://jenkins.example.com" tests_repo_url = "https://github.com/acme/tests" ai_provider = "claude" ai_model = "claude-opus-4-1" wait_for_completion = true poll_interval_minutes = 2 max_wait_minutes = 30 peers = "cursor:gpt-5.4-xhigh,gemini:gemini-2.5-pro" peer_analysis_max_rounds = 3 additional_repos = "infra:https://github.com/acme/infra:main" ``` Effect: The CLI sends populated values to the server only when they are set in the profile and not overridden by CLI flags. > **Note:** In `config.toml`, empty strings and `0` values are treated as unset. `0` is the sentinel for `ai_cli_timeout`, `jira_max_results`, `peer_analysis_max_rounds`, `poll_interval_minutes`, and `max_wait_minutes`. ### Issue Helper Fields Client-side defaults for issue-related CLI commands. | Name | Type | Default | Description | | --- | --- | --- | --- | | `jira_token` | string | `""` | Default Jira token for issue-related CLI commands that accept `--jira-token`. | | `jira_security_level` | string | `""` | Default Jira security level name for restricted Jira issues. | | `github_repo_url` | string | `""` | Default GitHub repository URL for CLI commands that accept `--github-repo-url`. | ```toml [servers.prod] url = "https://jji.example.com" jira_token = "jira-user-token" jira_security_level = "Internal" github_repo_url = "https://github.com/acme/tests" ``` Effect: These fields are CLI-side defaults only. They do not change the server's own environment configuration. ### CLI Environment Variables Global CLI environment variables. | Name | Type | Default | Description | | --- | --- | --- | --- | | `JJI_SERVER` | string | `unset` | Server profile name or full URL. | | `JJI_USERNAME` | string | `""` | Username sent as the CLI's `jji_username` cookie. | | `JJI_API_KEY` | string | `""` | Admin API key sent as a Bearer token. | | `JJI_NO_VERIFY_SSL` | boolean | `unset` | Disable CLI TLS verification. | ```bash export JJI_SERVER=prod export JJI_USERNAME=alice export JJI_API_KEY=admin-api-key export JJI_NO_VERIFY_SSL=false jji health ``` Effect: `JJI_SERVER` can be either a profile name or a full URL. Full URLs bypass profile inheritance. > **Note:** `jji analyze` also accepts these client-side environment defaults: `JENKINS_URL`, `JENKINS_USER`, `JENKINS_PASSWORD`, `TESTS_REPO_URL`, `JIRA_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN`, `JIRA_PAT`, `JIRA_PROJECT_KEY`, and `GITHUB_TOKEN`. ## Per-Request Override Behavior > **Note:** For `POST /analyze` and `POST /analyze-failures`, omitted JSON fields do not override server defaults. Some request models have JSON defaults, but the server applies those defaults as overrides only when the field is actually present in the request body. > **Note:** `POST /results/{job_id}/preview-github-issue`, `POST /results/{job_id}/preview-jira-bug`, `POST /results/{job_id}/create-github-issue`, and `POST /results/{job_id}/create-jira-bug` do not use the analysis override merge described below. ### Common Analysis Override Fields Fields accepted by both `POST /analyze` and `POST /analyze-failures`. | Name | Applies to | Type | Default When Omitted | Description | | --- | --- | --- | --- | --- | | `tests_repo_url` | both | string or `null` | server default | Override tests repo URL. `url:ref` syntax is accepted. | | `ai_provider` | both | string or `null` | `AI_PROVIDER` | Override the primary AI provider. | | `ai_model` | both | string or `null` | `AI_MODEL` | Override the primary AI model. | | `enable_jira` | both | boolean or `null` | server default | Override Jira enrichment for this request. | | `ai_cli_timeout` | both | integer or `null` | `AI_CLI_TIMEOUT` | Override AI CLI timeout in minutes. | | `jira_url` | both | string or `null` | `JIRA_URL` | Override Jira URL. | | `jira_email` | both | string or `null` | `JIRA_EMAIL` | Override Jira email. | | `jira_api_token` | both | string or `null` | `JIRA_API_TOKEN` | Override Jira API token. | | `jira_pat` | both | string or `null` | `JIRA_PAT` | Override Jira PAT. | | `jira_project_key` | both | string or `null` | `JIRA_PROJECT_KEY` | Override Jira project key. | | `jira_ssl_verify` | both | boolean or `null` | `JIRA_SSL_VERIFY` | Override Jira TLS verification. | | `jira_max_results` | both | integer or `null` | `JIRA_MAX_RESULTS` | Override Jira search result limit. | | `raw_prompt` | both | string or `null` | `unset` | Append request-specific AI instructions. | | `github_token` | both | string or `null` | `GITHUB_TOKEN` | Override the GitHub token used during analysis-time GitHub operations. | | `peer_ai_configs` | both | array or `null` | server default | Override peer analysis configs. Omit or send `null` to inherit; send `[]` to disable. | | `peer_analysis_max_rounds` | both | integer | server default | Override max peer rounds. Allowed range: `1`-`10`. Applied only when the field is present. | | `additional_repos` | both | array or `null` | server default | Override additional repos. Omit or send `null` to inherit; send `[]` to disable. | ```json { "tests_repo_url": "https://github.com/acme/tests:release-1.2", "ai_provider": "gemini", "ai_model": "gemini-2.5-pro", "enable_jira": false, "raw_prompt": "Focus on networking regressions first.", "peer_ai_configs": [], "additional_repos": [ { "name": "infra", "url": "https://github.com/acme/infra", "ref": "main" } ] } ``` Effect: Omitted fields inherit the server defaults. `peer_ai_configs=[]` disables peer analysis for that request. `additional_repos=[]` disables additional repo cloning for that request. ### Jenkins-Only Override Fields Fields accepted only by `POST /analyze`. | Name | Applies to | Type | Default When Omitted | Description | | --- | --- | --- | --- | --- | | `jenkins_url` | `POST /analyze` | string or `null` | `JENKINS_URL` | Override Jenkins URL. | | `jenkins_user` | `POST /analyze` | string or `null` | `JENKINS_USER` | Override Jenkins username. | | `jenkins_password` | `POST /analyze` | string or `null` | `JENKINS_PASSWORD` | Override Jenkins password or API token. | | `jenkins_ssl_verify` | `POST /analyze` | boolean or `null` | `JENKINS_SSL_VERIFY` | Override Jenkins TLS verification. | | `jenkins_artifacts_max_size_mb` | `POST /analyze` | integer or `null` | `JENKINS_ARTIFACTS_MAX_SIZE_MB` | Override artifact size cap. | | `get_job_artifacts` | `POST /analyze` | boolean or `null` | `GET_JOB_ARTIFACTS` | Override artifact download behavior. | | `wait_for_completion` | `POST /analyze` | boolean | server default | Override wait behavior. Applied only when the field is present. | | `poll_interval_minutes` | `POST /analyze` | integer | server default | Override poll interval. Applied only when the field is present. | | `max_wait_minutes` | `POST /analyze` | integer | server default | Override wait timeout. Applied only when the field is present. `0` means no limit. | ```json { "jenkins_url": "https://jenkins.internal.example.com", "jenkins_user": "svc-jji", "jenkins_password": "your-api-token", "jenkins_ssl_verify": false, "get_job_artifacts": true, "jenkins_artifacts_max_size_mb": 250, "wait_for_completion": true, "poll_interval_minutes": 5, "max_wait_minutes": 0 } ``` Effect: `wait_for_completion`, `poll_interval_minutes`, and `max_wait_minutes` override only when the JSON field is present. Sending `max_wait_minutes: 0` explicitly sets unlimited waiting for that request. ### Direct Failure Input Source Input-source fields accepted by `POST /analyze-failures`. | Name | Applies to | Type | Default When Omitted | Description | | --- | --- | --- | --- | --- | | `failures` | `POST /analyze-failures` | array or `null` | `unset` | Raw failures to analyze directly. | | `raw_xml` | `POST /analyze-failures` | string or `null` | `unset` | JUnit XML input. Maximum length: `50,000,000` characters. | ```json { "raw_xml": "...", "ai_provider": "claude", "ai_model": "claude-opus-4-1" } ``` Effect: Exactly one of `failures` or `raw_xml` is required. When `raw_xml` is supplied, the response includes `enriched_xml`. ## Feature Toggle and Capability Reporting ### `GET /api/capabilities` Returns server-level feature toggles and credential presence. | Name | Type | Default | Description | | --- | --- | --- | --- | | `None` | n/a | n/a | This endpoint takes no query parameters or request body. | | Response Field | Type | Description | | --- | --- | --- | | `github_issues_enabled` | boolean | GitHub issue toggle state. | | `jira_issues_enabled` | boolean | Jira issue toggle state. | | `server_github_token` | boolean | Whether a server GitHub token is configured. | | `server_jira_token` | boolean | Whether a server Jira token (`JIRA_API_TOKEN` or `JIRA_PAT`) is configured. | | `server_jira_email` | boolean | Whether a server Jira email is configured. | | `server_jira_project_key` | string | Configured Jira project key, or `""`. | | `reportportal` | boolean | Actual Report Portal enabled state. | | `reportportal_project` | string | Configured Report Portal project, or `""`. | ```json { "github_issues_enabled": true, "jira_issues_enabled": true, "server_github_token": true, "server_jira_token": false, "server_jira_email": false, "server_jira_project_key": "PROJ", "reportportal": true, "reportportal_project": "e2e" } ``` Effect: This endpoint reports server configuration only. It does not apply per-request analysis overrides. `reportportal` reflects actual enablement after validating Report Portal URL, token, and project. ## Related Pages - [Running Your First Analysis](quickstart.html) - [CLI Command Reference](cli-command-reference.html) - [REST API Reference](rest-api-reference.html) - [Improving Analysis with Repository Context](improving-analysis-with-repository-context.html) - [Managing Admin Users and API Keys](managing-admin-users-and-api-keys.html) ---