GitLab declares ciLogs: true. getWorkflowRunLogs could not return a log.
async getWorkflowRunLogs(runId: number): Promise<string | null> {
return this.optional<string>('GET', `/projects/${this.projectId}/jobs/${runId}/trace`)
.catch(() => null)
}optional never passed { raw: true }, so the transport took its default path:
return (text.trim() ? JSON.parse(text) : undefined) as TA CI job trace is plain text. That threw on the first line — which is every line — and the .catch(() => null) reported it as "no logs available."
So buddy fix-ci against GitLab answered "Could not read the run logs; nothing to diagnose" for every run that had ever produced output, while the capability system reported the provider as able to read them.
An empty trace took a quieter route to the same place: text.trim() is falsy, so it returned undefined without throwing at all.
The fix
optional now forwards transport options, and the catch logs what it swallowed rather than discarding it — that catch is why a parse error looked like an absent log for as long as it did.
The test worth pointing at:
it('success case - a trace that happens to be JSON is still returned verbatim', ...)Parsing at all was the defect, not failing to parse.
Context
This was the last unfixed high from the sweep — the other six were the workflow-reachability family (#1418) and rules[].minimumReleaseAge (#1419).
It also sits right next to ciRuns, which I added in #1410. That capability's GitLab implementation deliberately returns jobs rather than pipelines precisely so their ids can be handed back to getWorkflowRunLogs — which, it turns out, could not have used them.
Tests
2080 → 2084. (One pre-existing perf test in package-scanner-ignorepaths-integration is timing-flaky; it fails and passes across runs on clean main too.)
Lint ✓ · typecheck ✓ · check:docs ✓ · 0 fail
🤖 Generated with Claude Code