Skip to content

fix(triage): split Models inference token from the org GITHUB_TOKEN#1284

Merged
mstykow merged 1 commit into
mainfrom
fix/scancode-triage-split-inference-token
Jul 15, 2026
Merged

fix(triage): split Models inference token from the org GITHUB_TOKEN#1284
mstykow merged 1 commit into
mainfrom
fix/scancode-triage-split-inference-token

Conversation

@mstykow

@mstykow mstykow commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • The weekly ScanCode triage cron ran green but did no work: on the scheduled run every GitHub Models call returned 403 Forbidden, so all candidates fell back to "needs manual check" and nothing was triaged. The manual workflow_dispatch run days earlier worked.
  • Root cause: the repo is org-owned and getprovenant 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 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.
  • Fix: split the two credentials. Models inference reads a dedicated MODELS_TOKEN (a personal fine-grained PAT with only Models: read); the gh CLI keeps using the built-in org GITHUB_TOKEN for issue fetch/create, which it is already entitled to do.
  • Hardening so this can't silently recur: call_model now includes the HTTP status + response body in the error (GitHub puts the real reason there) instead of discarding it via error_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

  • Covers: silent failure of the ScanCode weekly triage workflow (403 on scheduled GitHub Models inference)

Scope and exclusions

  • Included: xtask/src/bin/scancode-triage.rs (token split + error surfacing + loud-failure exit) and .github/workflows/scancode-triage.yml (env wiring, permissions/comment cleanup).
  • Explicit exclusions: no change to triage judgment, prompts, or the tool sandbox. Removed the now-unused models: read workflow permission since GITHUB_TOKEN no longer performs inference.

Operational prerequisite

  • A repo Actions secret MODELS_TOKEN must exist: a personal fine-grained PAT, resource owner = a user with a personal GitHub Models entitlement, permission Models: Read only (no repo access needed). Already created for getprovenant/provenant.

How to verify

  • Failure path (no valid Models token): 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 shows model request failed (401 Unauthorized): ... (status + body) and the process exits non-zero after printing the report.
  • Happy path: same command with 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

  • N/A (maintainer tooling, no ScanCode analog).

Follow-up work

  • None.

Expected-output fixture changes

  • None.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes the root cause of silent triage failures on scheduled runs by splitting GitHub Models inference auth from the org GITHUB_TOKEN. It also hardens the error path so any LLM call failure (partial or total) causes a non-zero exit with a diagnostic message.

  • Token split: models_token() resolves MODELS_TOKEN first (a personal fine-grained PAT carrying a user-level Models entitlement), falling back to GITHUB_TOKEN/GH_TOKEN for local dev. The workflow wires MODELS_TOKEN from a repo secret and drops the now-unused models: read permission from GITHUB_TOKEN.
  • Explicit error tracking: Replaces the old string-sentinel approach (LLM_ERROR_NOTE) with a Triaged { row, llm_error: bool } struct; llm_error is set by the call site, not inferred from row text, eliminating the false-positive risk from model-generated content that happens to contain the sentinel string.
  • Loud failure: call_model now surfaces HTTP status + response body in the error (previously discarded by error_for_status()); and llm_errors > 0 (any candidate failing, not just all) exits non-zero after printing the report.

Confidence Score: 5/5

Safe 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: llm_errors > 0 catches any per-candidate failure, and llm_error: bool is set by the call site rather than inferred from row text. The call_model error path now correctly reads the response body before checking the status, so the diagnostic message is always populated. The workflow change is minimal — removes an unnecessary permission and adds a single env var. No logic or data correctness is at risk.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/scancode-triage.yml Splits token wiring: removes models: read from GITHUB_TOKEN permissions and introduces MODELS_TOKEN env var (personal PAT) for inference; GH_TOKEN/GITHUB_TOKEN remain for gh CLI REST calls. Clean and correct.
xtask/src/bin/scancode-triage.rs Adds Triaged struct with explicit llm_error: bool flag (replacing string-match sentinel), counts LLM errors across all candidates and exits non-zero if any occur, surfaces HTTP status + body in call_model errors, and renames gh_token() to models_token() with MODELS_TOKEN-first lookup.

Reviews (2): Last reviewed commit: "fix(triage): split Models inference toke..." | Re-trigger Greptile

Comment thread xtask/src/bin/scancode-triage.rs Outdated
Comment thread xtask/src/bin/scancode-triage.rs Outdated
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>
@mstykow
mstykow force-pushed the fix/scancode-triage-split-inference-token branch from 083393d to 005a217 Compare July 15, 2026 08:08
@mstykow
mstykow merged commit 33d8734 into main Jul 15, 2026
19 checks passed
@mstykow
mstykow deleted the fix/scancode-triage-split-inference-token branch July 15, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant