The pipeline
One pass, ten stages, no loops. Each stage narrows the log; the last four are the only ones that ever see fewer lines than the job actually printed.
-
1 Acquire
providers/Fetch the failed run and its job logs over the provider API, or read a saved log from disk.
tuned by
provider, gitlab, github -
2 Segment
providers/*/segmenter.pyCut the raw log into named sections. Each CI system frames a job differently; this is the last place that difference is visible.
tuned by
— -
3 Map phases
core/phases.pyLabel each section with a phase: provision, prepare, fetch, script, post.
tuned by
phases -
4 Attribute
core/attribution.pyDecide which phase actually failed, from job metadata and log structure. Pure, deterministic, no model.
tuned by
— (deliberately) -
5 Denoise
core/denoise.pyStrip ANSI colour, collapse progress bars, fold repeated lines, drop configured noise.
tuned by
denoise -
6 Extract
core/extract.pyRun every matcher over the blamed section and keep the windows they anchor. Every gap is marked.
tuned by
extraction.matchers -
7 Budget
core/budget.pyShed the lowest-ranked windows (fallback, then wrapper, then tool), then trim what survives to the token budget.
tuned by
llm.max_input_tokens -
8 Explain
llm/One optional model call, given only the budgeted evidence. Answers why, never where.
tuned by
llm -
9 Redact
core/redact.pyScrub secrets. Runs twice — on the prompt before it leaves the process, and on the finished report.
tuned by
redaction -
10 Render
render/Terminal, report.md, report.json, and optionally one idempotent note on the MR/PR.
tuned by
output
Stages 5–7 are how a 40,000-line log becomes a few dozen lines worth reading — Evidence covers those. To see the same ten stages as a dataflow graph, with the module boundaries drawn in, go back to the Concepts overview.
You can watch the whole thing run with ci-doctor analyze -v: it logs the section count, the attribution verdict and rule, and a
denoise → extract → fit line with the surviving line count at
each step.