jevtriage

September 21, 2026 · View on GitHub

Triage gate for pull requests. A GitHub Action and small Python CLI that asks TypeSafe Jev (System One) one Choice question:

VerdictMeaning
readyLooks safe to merge with normal review depth
needs_reviewNeeds human eyes (unclear risk, large or ambiguous change)
riskyElevated risk (security-sensitive, destructive, or high blast radius)

The official ChoiceAnswer.confidence is a gate: ready only exits 0 when confidence ≥ threshold (default 0.8). Low confidence fails closed to needs_review (exit 1).

v0.1 is a drop-in Action. Copy one workflow YAML, set TYPESAFE_API_KEY, pin a model, get a verdict on every PR.

Not these nearby tools

ToolNiche
HexyeDEV/JevPRGitHub App + policy router (LOW / NORMAL / SPECIALIST → approve or assign reviewers). A review-routing service, not a CI gate.
1jehuang/jev-pr-labelerSemantic labels (type:, area:, size:) via OpenRouter. Taxonomy, not merge readiness.
jevtriage (this repo)Triage gate: ready / needs_review / risky + confidence fail + marketplace-friendly Action + CLI exit codes.

Pair with jevcheck contracts if you want upgrades to fail when this question flips. jevcheck is not a v0.1 dependencyfixtures/pr-triage.contract.json and fixtures/replay-ready.json are the replay shape.

Install

pip install jevtriage
# tests (clone extras):
pip install "jevtriage[dev]"
export TYPESAFE_API_KEY=...          # live Jev only; never commit this
export TYPESAFE_DEFAULT_MODEL=jev-1.13.0

Auth is TYPESAFE_API_KEY only (typesafe-sdk). Unit tests mock the client. jevtriage 0.1.0 is on PyPI. The Action installs that pin (jevtriage==0.1.0); keep the Action tag in sync, or fall back to pip install "${{ github.action_path }}" for a local checkout.

GitHub Action

Marketplace-friendly root action.yml:

# .github/workflows/pr-triage.yml
name: pr-triage
on: pull_request
permissions:
  contents: read
  pull-requests: write
  issues: write
jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - uses: sathariels/jevtriage@v0.1.0
        id: triage
        env:
          TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
        with:
          model: jev-1.13.0
          apply-labels: true
      - run: echo "${{ steps.triage.outputs.verdict }} ${{ steps.triage.outputs.confidence }}"

Prefer sathariels/jevtriage@v0.1.0. Use @main only if you want unreleased tip-of-tree. The step fails on exit 1 or 2 — that is the gate. Use continue-on-error: true only if you want labels/comments after a non-ready verdict.

This repository’s example workflow runs the same Action with answers: fixtures/replay-single-ready.json so default CI never calls TypeSafe.

Inputs

InputDefaultNotes
modelTYPESAFE_DEFAULT_MODELRequired pin. latest / preview / empty rejected
min-confidence0.8Official ChoiceAnswer.confidence floor for exit 0
allow-unpinnedfalseProduction stay false
apply-labelsfalseWrites jev:ready, jev:needs-review, jev:risky
github-token${{ github.token }}PR fetch + optional labels
pull-requestevent PR numberUsed to fetch title/body/diff
repository${{ github.repository }}owner/name
title / body / diff-pathemptyOverrides; skip GitHub fetch when you pass them
answers / caseemptyFixture replay (CI / no key)
max-diff-chars32000Unified diff cap (~20–40k)
python-version3.12

Outputs

OutputSource
verdictChoiceAnswer.choice (ready / needs_review / risky)
confidenceofficial ChoiceAnswer.confidence in [0, 1]
modelSystemOneResponse.model

CLI

# Live (needs TYPESAFE_API_KEY). Fetches the PR via GITHUB_TOKEN.
jevtriage --repo owner/name --pr 123 --model jev-1.13.0

# Local files
jevtriage --title "Fix typo" --body "nits" --diff pr.diff --model jev-1.13.0

# Replay (no key) — jevcheck-style fixture
jevtriage --answers fixtures/replay-single-ready.json --model jev-1.13.0
jevtriage --answers fixtures/replay-ready.json --case auth-bypass --model jev-1.13.0

Stdout is one JSON object (verdict, confidence, probabilities, model, usage, truncated). A one-line summary goes to stderr. When GITHUB_OUTPUT is set, the Action outputs are appended.

Environment

VariableRole
TYPESAFE_API_KEYRequired for live calls. The only auth env.
TYPESAFE_DEFAULT_MODELPin when --model / Action model is empty
TYPESAFE_BASE_URLOptional SDK API root
GITHUB_TOKEN / GH_TOKENFetch the PR; apply labels if requested
GITHUB_REPOSITORYDefault --repo
GITHUB_OUTPUTWrite Action outputs

Exit codes

CodeWhen
0Verdict is ready and confidence--min-confidence
1Verdict is needs_review, or ready/needs_review below the confidence threshold
2Verdict is risky, or a config/API/identity error

Exit 2 is shared on purpose (fail closed). Distinguish them from stdout: a successful call has verdict; a failure has "error". risky still writes verdict + Action outputs before exiting 2. Config errors (missing key, unpinned model, bad replay) also exit 2 and print {"error": "..."}.

Label apply failures are logged to stderr and do not change the verdict exit code.

What Jev sees

System One state is a JSON object (official SDK: string or object):

  • title, body (body capped at 8k chars)
  • diff — unified diff, capped at 32k chars by default, with a visible truncation note
  • files + stats when provided or when the GitHub compare payload is used
  • jevtriage_notes when anything was truncated or the diff was omitted

The only question is a Choice named triage with exactly the three criteria above. Official fields only (instructions, criteria). The gate uses official choice + confidence + probabilities — no invented confidence field.

Model pinning

Production mode rejects empty names and floating aliases (jev-latest, jev-preview, or any name containing latest or preview). A documented TypeSafe catalog pin as of 2026-09-19 is jev-1.13.0. Pass --allow-unpinned only for experiments.

The response model must match the requested pin (exact string). An opted-in alias may resolve to a concrete version.

Optional labels

With --apply-labels / apply-labels: true and GITHUB_TOKEN:

  • jev:ready
  • jev:needs-review
  • jev:risky

Other jev:* triage labels on the PR are removed so only one remains.

jevcheck (optional)

fixtures/pr-triage.contract.json is a v0.1 jevcheck contract for this exact question. fixtures/replay-ready.json is a keyed replay (docs-typo, large-ambiguous, auth-bypass). After you add jevcheck to a repo:

jevcheck eval fixtures/pr-triage.contract.json \
  --candidate-model jev-1.13.0 \
  --answers fixtures/replay-ready.json

v0.1 of jevtriage does not install or invoke jevcheck.

Develop

pip install -e ".[dev]"
pytest
python -m jevtriage --answers fixtures/replay-single-ready.json --model jev-1.13.0

This repo’s default CI runs unit tests (mocked client) and a wheel/sdist smoke on the fixture. No live TypeSafe call.

Verified System One contract: docs.typesafe.ai/sdk/python.md. Package: typesafe-sdk.

License

MIT. See LICENSE.