fix(triage): split Models inference token from the org GITHUB_TOKEN#1284
Conversation
Greptile SummaryThis PR fixes the root cause of silent triage failures on scheduled runs by splitting GitHub Models inference auth from the org
Confidence Score: 5/5Safe to merge — the change is narrowly scoped to credential routing and error surfacing in a maintenance tool, with no impact on production data paths. The two concerns raised in earlier review threads (partial LLM failure masking as a green run, and the string-sentinel false-positive) are both directly addressed: No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "fix(triage): split Models inference toke..." | Re-trigger Greptile |
The weekly ScanCode triage ran green but did no work: on the scheduled run every GitHub Models call returned 403, so all candidates fell back to "needs manual check" and nothing was triaged. The manual (dispatch) run days earlier worked. Root cause: the repo is org-owned and the org has no GitHub Models entitlement. A dispatch run resolves inference against the triggering user's personal entitlement; a scheduled run is attributed to the org, which has none -> 403. A single fine-grained PAT cannot fix this: its Models permission resolves against its one resource owner, so it can carry either a personal Models entitlement (user owner) or org repo access (org owner), never both. Split the two credentials: - Models inference reads a dedicated MODELS_TOKEN (a personal PAT with only Models: read), falling back to GITHUB_TOKEN/GH_TOKEN/`gh auth token` for local runs. - The gh CLI keeps using the built-in org GITHUB_TOKEN for issue fetch/create, which it is already entitled to do. Also harden the failure surface so this can't silently recur: - call_model now includes the HTTP status and response body in the error instead of discarding it via error_for_status(); GitHub returns the actual reason (e.g. "no access to model") in the body. - triage_one_issue now reports LLM failure explicitly (a Triaged struct) rather than inferring it from row text, and any candidate that hit an LLM error makes the run exit non-zero (after emitting the report). This catches partial outages (e.g. a token that fails partway through) and avoids miscounting a verdict that merely quotes "LLM error". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Maxim Stykow <maxim.stykow@gmail.com>
083393d to
005a217
Compare
Summary
403 Forbidden, so all candidates fell back to "needs manual check" and nothing was triaged. The manualworkflow_dispatchrun days earlier worked.getprovenanthas no GitHub Models entitlement. A dispatch run resolves inference against the triggering user's personal entitlement; a scheduled run is attributed to the org, which has none → 403. A single fine-grained PAT can't fix this — its Models permission resolves against its one resource owner, so it can carry a personal Models entitlement (user owner) or org repo access (org owner), never both.MODELS_TOKEN(a personal fine-grained PAT with onlyModels: read); theghCLI keeps using the built-in orgGITHUB_TOKENfor issue fetch/create, which it is already entitled to do.call_modelnow includes the HTTP status + response body in the error (GitHub puts the real reason there) instead of discarding it viaerror_for_status(); and a run where every candidate hits an LLM error now exits non-zero (after emitting the report) instead of masquerading as a green "no findings" run.Issues
Scope and exclusions
xtask/src/bin/scancode-triage.rs(token split + error surfacing + loud-failure exit) and.github/workflows/scancode-triage.yml(env wiring, permissions/comment cleanup).models: readworkflow permission since GITHUB_TOKEN no longer performs inference.Operational prerequisite
MODELS_TOKENmust exist: a personal fine-grained PAT, resource owner = a user with a personal GitHub Models entitlement, permissionModels: Readonly (no repo access needed). Already created forgetprovenant/provenant.How to verify
MODELS_TOKEN=bogus SCANCODE_TRIAGE_MODEL=openai/gpt-4.1 GH_TOKEN=$(gh auth token) GITHUB_TOKEN=$(gh auth token) ./target/debug/scancode-triage --days 8 --binary /bin/true --repo-root . --max-issues 1— observe the evidence cell now showsmodel request failed (401 Unauthorized): ...(status + body) and the process exits non-zero after printing the report.MODELS_TOKEN=$(gh auth token)(a personal token with Models access) — the model authenticates, a real verdict row is produced, and it exits 0.Intentional differences from Python
Follow-up work
Expected-output fixture changes