What the moving parts are called, what each one decides, and where it
lives in the code. Read this section once and the configuration
reference stops being a list of unexplained knobs.
The one rule everything else follows. Deterministic code
decides
where the job failed; the LLM only explains why. Phase
attribution runs before any model is called, so a loud but harmless
cache warning can never be blamed over the real exit code 1. If the classifier is wrong, that is a bug with a failing test — not a
prompt to tune.
Where each module sits, and what actually flows between them. Note the
two dotted edges: they are the only ones a model touches, and they carry
evidence that has already been selected and redacted.
Solid edges are deterministic and offline. The dotted pair is the one
optional model call.
The same code as layers
Three bands. Providers know their CI system and nothing else; core knows
the domain and no provider; outputs render what core decided. The seam
through the middle is the only way data crosses.
With no model, the deterministic report is the output — not a
stub.
Why the middle band is sealed.grep -ri gitlab ci_doctor/core/
must return nothing, and CI fails if it does. That is what makes adding a
CI system a matter of dropping in a directory rather than threading a third
case through the classifier. The code map walks it file by file.
The invariants
These are enforced by tests, not convention. The first three are
load-bearing:
The LLM never selects the failure phase. Attribution
is deterministic and auditable.
No provider identifiers in core/. Checked
by grep in CI.
Always exit 0. ci-doctor can never change
your pipeline's outcome or mask the real failure.
Nothing reaches the public internet by default — no telemetry, no
update checks, no runtime downloads.
Never emit unredacted log content: redaction runs on the prompt and the report.
Every truncation is visible (… [N lines elided] …),
never a silent cut.