Skip to content

finding: raw CI job logs ARE reachable from an agent seat via the GitHub MCP server, so ci-failure.mjs's "not a foundation any seat-facing tool can stand on" is true only of direct egress #10141

Description

@os-steve

Recording, not claiming. Found while implementing #10128 (needed the incident's own job logs as a test corpus); filed unassigned for triage routing.

What scripts/pm/ci-failure.mjs says today

Its header records a 2026-08-19 measurement in an agent container and builds the whole tool's design on it:

GET /repos/{o}/{r}/actions/jobs/{job}/logs -> 302 to productionresultssa8.blob.core.windows.net -> CONNECT 403
[...] Raw logs are therefore NOT a foundation any seat-facing tool can stand on

What was measured today (2026-08-20), in this container

Half of it reproduces exactly. Direct egress is still refused:

curl -sSL -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/repos/objectstack-ai/objectstack/actions/jobs/96319093505/logs
curl: (56) CONNECT tunnel failed, response 403

The other half does not. The GitHub MCP server's get_job_logs with return_content: true returned real log content for the same jobs — 96306174446, 96308505284, 96319093505 — including the vitest output, the ##[error] lines and the turbo task summary. It answers a tail window (tail_lines, default 500), not the whole archive, and it lands in the caller's context rather than on disk.

So the accurate statement is narrower than the one in the header: direct fetches of the log archive are denied by this session's egress policy, while a proxied path exists and works. That is a difference in kind, not in degree — "no seat can read raw logs" and "a seat can read the tail of raw logs through one specific tool" support different tool designs.

Why it may matter

  1. The header is a load-bearing self-description. A future author reading it will not try the reachable path, and finding: retrieving a failed CI job's actual assertion takes a manual log-zip dance — script it #9777's whole "walk the surfaces that DO answer" architecture is justified by the sentence above.
  2. It is adjacent to the two open ci-failure.mjs cards — scripts/pm/ci-failure.mjs's --self-test runs nowhere in CI, unlike its four sibling PM tools #9898 (--self-test runs nowhere in CI) and ci-failure.mjs's transport probe has the same false green as #9946 — it classifies from /rate_limit alone, then walks repo-scoped Actions paths #9966 (transport probe carries a false green) — and any of the three touching that header should agree on what it claims.

Leads, not decisions

  • Narrow the header's claim to what was measured (direct egress denied), and say what else was tried.
  • Decide separately whether ci-failure.mjsshould reach for the MCP path. Arguments against are real: it would make a seat-facing CLI depend on an MCP server being present, it returns only a tail, and the annotation-based walk already retrieves the assertion in the cases that matter. "Reachable" is not "worth depending on" — that is the decision this issue is asking someone to take deliberately rather than by omission.
  • Whichever way it goes, the measurement should be recorded so the next reader is not told a reachable path is closed.

Refs: #9777 (the tool), #9898, #9966, #10128 (where this surfaced).

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions