Releases: Victor-Casado/if-ai
Release list
v1.2.0 — Every limit is a budget you can change
Five new inputs, a README rewritten around getting you running, and a rule applied throughout: a limit this Action invented is yours to change, and a limit the provider owns is not ours to impose at all.
- uses: Victor-Casado/if-ai@v1.2.0
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.OPENROUTER_API_KEY }}Nothing to migrate from v1.1.0. Every new input defaults to the value that was previously fixed.
New: budgets you can set
| Input | Default | What it bounds |
|---|---|---|
max-files |
200 |
Paid calls, which matters most in per-file mode |
timeout-seconds |
30 |
One evaluation, covering every attempt |
retries |
1 |
Retries after a rate limit or server fault. 0 guarantees a single paid call |
max-request-bytes |
2000000 |
Transport guard |
max-file-bytes |
2000000 |
Memory used to buffer one file's patch |
Change lists and commit metadata have their own ceiling, so lowering max-file-bytes to bound one patch does not make the file list unreadable.
Removed: limits the provider owns
The request size cap. The old 28,000-byte limit was never measured against anything. A probe against both providers found each accepting a 102 KB request and rejecting a 203 KB one, so the cap was turning away roughly three quarters of what they would take. Requests are now sent and the rejection is reported.
The condition's 4,000-byte limit, for the same reason: the provider sizes the whole request.
The model identifier allowlist. ^[a-zA-Z0-9/~._-]{1,100}$ guessed at provider naming and rejected 96 of OpenRouter's 447 live models, which use suffixes such as :free and :batch. Only names that are empty or carry whitespace or control characters are refused now.
Errors that name the real cause
- HTTP 400 explains that the provider rejected the request and that content larger than the model's context is the usual reason. OpenRouter reports this with a structured
detail.error_type, so that case is named directly. TypeSafe returns a bare400and gets the general wording. Only the structured field classifies a response: an error body can echo the content that was sent. - An oversized pull request no longer reports a broken checkout. A file whose patch exceeds the budget says so and points at
paths. - A stalled error body that outlasts the deadline reports a timeout rather than a status code.
Provider response bodies are still never logged or repeated in an error.
README
Rewritten around a reader who wants it working, not convinced. Three numbered steps and a complete, pasteable workflow now come first: the workflow moved from line 87 to line 22. The pricing evidence and evaluation table moved below the point where you could already have it running.
Verification
105 tests, typecheck, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed dist/index.cjs matches source.
The live smoke test ran against Jev through OpenRouter on this commit and passed in all three modes, including a per-file run that correctly identified the failing file.
Known limits
A passing check is advisory. Confidence is calibrated model certainty rather than measured accuracy, and pull request content can attempt to influence the verdict. OpenRouter's Decisions API is alpha and may change.
Installing
Use @v1.2.0, or pin the full commit SHA 72eb1ba4ebaed5ed58dc5b3c8a4a2b0ecadcb554 for an immutable install. Pinning is worth it here: this step receives your API key.
v1.1.0 — Scope a rule to the files it concerns
Adds the paths input, so a rule only sees the files it is about.
- uses: Victor-Casado/if-ai@v1.1.0
with:
condition: No existing test is deleted or weakened by this change.
min-confidence: '0.90'
mode: diff
paths: |
src/**
test/**
api-key: ${{ secrets.OPENROUTER_API_KEY }}New: paths
paths takes Git pathspecs, one per line, filtering changed files before anything is evaluated. Globs and :(exclude) both work. Up to 50 entries, 200 UTF-8 bytes each. Ignored in pr-body mode.
Why it exists. A rule about source code was being asked to reason about diffs containing no source code, and answering with low confidence because there was nothing to go on. Scoping the rule removes the question rather than tuning the threshold around it.
A filter matching nothing is a pass, not a failure. A rule scoped to src/** does not apply to a documentation-only pull request. That run reports a new status: 'skipped', calls no API, and costs nothing. A pull request with no changed files at all still errors; a filter is not allowed to disguise one as the other.
In per-file mode this is the cost control. One paid call per matching file instead of one per changed file. Filtering also happens before the 200-file limit, so a large pull request scoped to a few files is evaluated rather than rejected.
Also in this release
A guard on an event belongs in the workflow trigger, not in a job's if. GitHub evaluates the most recent check run for each name on a commit, and a job skipped by its if reports the skipped conclusion, which counts as satisfying a required status check. A job that runs on one event and skips on another for the same commit therefore replaces its own earlier verdict, including a failure. The README quickstart no longer triggers on edited while running mode: diff, which also removes a paid call that could not change the answer.
A required if-ai check does not cover fork PRs under the same-repository pattern: they are skipped, and a skipped check passes. That follows from the documented behavior, but the branch protection consequence is now written down in the README and the reference.
Verification
88 tests, typecheck, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed dist/index.cjs matches source.
New coverage: pathspec scoping in diff and per-file against real Git repositories, :(exclude) magic, a filter matching nothing returning no subjects while an empty comparison still errors with and without a filter, and input parsing for trimming, blank lines, the entry cap, and the byte cap.
The live smoke test ran against Jev through OpenRouter on this commit and passed in all three modes, including a per-file run that correctly identified the failing file.
Known limits
A passing check is advisory. Confidence is calibrated model certainty rather than measured accuracy, and pull request content can attempt to influence the verdict. Requests cap at 28,000 UTF-8 bytes and 200 changed paths, with a 30-second deadline covering one retry on a rate limit or server fault. OpenRouter's Decisions API is alpha and may change.
Installing
Use @v1.1.0, or pin the full commit SHA 6e5a7d70f91c2ee24078638d62532e035a58a718 for an immutable install.
Nothing to migrate from v1.0.1. paths is optional and defaults to evaluating every changed file.
v1.0.1 — Retry transient failures
The first release carrying the retry work. v1.0.0 tagged the same commit as v0.3.2, so this is the first tag that includes it.
- uses: Victor-Casado/if-ai@v1.0.1
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.OPENROUTER_API_KEY }}Changes
A rate limit or a server fault is now retried once. Previously a single HTTP 429 or 5xx failed the whole check, and the error told the reader to rerun later, which meant a maintainer manually re-running a job on someone else's pull request. per-file mode was the most exposed, since it issues four concurrent requests per batch.
- The existing 30-second deadline covers every attempt rather than each attempt, so the documented bound is unchanged. A retry spends the remaining budget instead of extending it.
Retry-Afteris honored, as seconds or as an HTTP date, up to 10 seconds. A provider asking for longer than that, or longer than the deadline has left, reports the real status rather than stalling and asking again into the same rate limit.- 401, 402, 403, and 422 describe the request itself and are not retried. Timeouts and transport failures are not retried either, since they describe the runner or the network rather than a provider state that clears on its own.
- Retries are logged rather than silent.
A retried request is a second paid call.
The repository also now runs if-ai on its own pull requests; see .github/workflows/if-ai.yml.
Verification
85 tests, typecheck, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed dist/index.cjs matches source.
New coverage: retry then succeed, retry then fail with the original status reported, permanent statuses not retried, the retry notice reaching the caller, the deadline guard, and Retry-After parsing for absent, blank, unparseable, numeric, negative, and HTTP-date values.
The live smoke test ran against Jev through OpenRouter on 7595fbb and passed in all three modes, including a per-file run that correctly identified the failing file. That covers the restructured request path on the happy path; it does not exercise a retry, since the provider returned no 429 or 5xx. The retry path is covered by unit tests only.
Known limits
Unchanged otherwise. A passing check is advisory, confidence is calibrated model certainty rather than measured accuracy, and PR content can attempt to influence the verdict. Requests cap at 28,000 UTF-8 bytes and 200 changed paths. OpenRouter's Decisions API is alpha and may change.
Installing
Use @v1.0.1, or pin the full commit SHA 31612abb71ad1351b2fe77caa8b11365300922e1 for an immutable install.
Public Launch
Rename the Marketplace listing to 'if-ai PR Check' for v0.3.2 (#9) Marketplace names must be unique across every action, user, and organization. The GitHub user if-ai already exists, so the bare name is rejected at publish time. Only the action.yml name field changes; the repository name and every uses: path stay the same. Record the reason in docs/maintaining.md and bump to 0.3.2. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
v0.3.2 — Marketplace listing name
Prepares the action for a GitHub Marketplace listing. No behavior change from v0.3.1, and dist/index.cjs is byte-identical to it.
- uses: Victor-Casado/if-ai@v0.3.2
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.OPENROUTER_API_KEY }}Changes
- The
nameinaction.ymlis nowif-ai PR Check. Marketplace names must be unique across every action, user, and organization, and the GitHub userif-aialready exists, so the bare name is rejected at publish time. docs/maintaining.mdrecords that constraint.- Version references updated across
package.json, the lockfile, the README, and the four examples.
The repository name and the uses: path are unchanged. Victor-Casado/if-ai still resolves, and no workflow needs editing beyond the version tag. Only the Marketplace listing title differs.
Verification
75 offline tests, TypeScript, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed bundle matches source. No live smoke test was run, since evaluation behavior is unchanged; the v0.3.0 run against Jev through OpenRouter still covers it.
Known limits
Unchanged from v0.3.0. A passing check is advisory, confidence is calibrated model certainty rather than measured accuracy, and PR content can attempt to influence the verdict. Requests cap at 28,000 UTF-8 bytes and 200 changed paths, with a 30-second deadline and no retries. OpenRouter's Decisions API is alpha and may change.
Installing
Use @v0.3.2, or pin the full commit SHA 5bfe6ed245586fa96ecd313c8178ce305586497c for an immutable install.
v0.3.1 — README failure example
Documentation only. No behavior change from v0.3.0, and dist/index.cjs is byte-identical to it.
- uses: Victor-Casado/if-ai@v0.3.1
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.OPENROUTER_API_KEY }}Changes
- Simplified the README's failure example: the job summary is shown directly rather than quoted, without the Details column, and now covers
condition-false,low-confidence, andpassedin one table. - Version references updated across
package.json, the lockfile, the README, and the four examples.
Verification
75 offline tests, TypeScript, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed bundle matches source. No live smoke test was run for this release, since evaluation behavior is unchanged; the v0.3.0 run against Jev through OpenRouter still covers it.
Known limits
Unchanged from v0.3.0. A passing check is advisory, confidence is calibrated model certainty rather than measured accuracy, and PR content can attempt to influence the verdict. Requests cap at 28,000 UTF-8 bytes and 200 changed paths, with a 30-second deadline and no retries. OpenRouter's Decisions API is alpha and may change.
Installing
Use @v0.3.1, or pin the full commit SHA 86ee83f047ccca1e59f62d9b5155d85490599848 for an immutable install.
v0.3.0 — OpenRouter by default
Install it as a workflow step:
- uses: Victor-Casado/if-ai@v0.3.0
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.OPENROUTER_API_KEY }}Changes
- OpenRouter is now the default provider.
providerdefaults toopenrouterand runstypesafe/jev-1.13through OpenRouter's alpha Decisions API. Pass anOPENROUTER_API_KEYsecret asapi-key. Direct TypeSafe access stays available withprovider: typesafeand aTYPESAFE_API_KEY. if-ai never guesses the provider from a key and never falls back to the other one. - Rewritten README. Leads with the workflow you paste in, the price, and TypeSafe's published Jev workflow-eval figures, with an explicit note that those evals are TypeSafe's own, score agreement with averaged GPT-6 Astra and Fable 5.1 judgments rather than ground truth, and do not cover pull request review.
- New input and output reference. Every input, output, mode, and limit, moved out of the README.
- Examples pinned to
actions/checkoutv7.0.1. @types/nodeheld on major 24 to match the Action's Node 24 runtime.
Verification
75 offline tests, TypeScript, Prettier, and the bundle build pass on Linux and Windows. CI verifies that the committed dist/index.cjs matches source.
The live smoke test ran against Jev through OpenRouter on this commit and passed in all three modes, including a per-file run that correctly identified the failing file. That checks routing, request shape, and response handling. It does not measure model accuracy.
Known limits
A passing check is advisory. Confidence is calibrated model certainty, not measured accuracy, and PR content can attempt to influence the verdict.
Requests cap at 28,000 UTF-8 bytes and 200 changed paths, with a 30-second deadline and no retries. Binary files, LFS pointers, submodules, and non-UTF-8 patches fail explicitly rather than being truncated. Diff modes need a full-history checkout. OpenRouter's Decisions API is alpha and may change.
Upgrading
Nothing to migrate for new installs. Use @v0.3.0, or pin the full commit SHA 1d303877be14e49a41ff6a1d96c57e61bab2c1d4 for an immutable install.
v0.2.0 — OpenRouter support
Run Jev with an OpenRouter key by setting provider: openrouter and passing the key through api-key. TypeSafe remains the default.
uses: Victor-Casado/if-ai@v0.2.0
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
provider: openrouter
api-key: ${{ secrets.OPENROUTER_API_KEY }}The OpenRouter default model is typesafe/jev-1.13. Requests use its alpha Decisions API and Jev's native confidence. This does not add support for arbitrary chat models. Existing modes, thresholds, and outputs are unchanged.
Both providers share request limits, response validation, deadlines, and sanitized errors. HTTP 402 errors now point to credits and spending limits. Documentation explains provider setup and data processing.
Validation: 74 offline tests on Linux and Windows, TypeScript and formatting checks, reproducible bundle verification, CodeQL, and live smoke tests for TypeSafe and OpenRouter. Each live run covers all three modes plus an expected per-file failure. These synthetic checks verify integration, not model accuracy.
v0.1.1
Fixes
- Keep per-file patches separate when a file becomes a directory or a directory becomes a file. Previously, a parent path could include child changes and duplicate them in the combined diff.
- Require a reviewed environment in the public fork and PR-body examples before making paid Jev calls. Configure the environment reviewers and secret before using those examples.
Contributor and repository updates
- Replace obsolete planning documents with focused usage, contribution, security, and maintenance guidance.
- Simplify response validation and test setup; enforce consistent formatting.
- Add bug and feature forms, a PR template, CODEOWNERS, and weekly dependency updates.
- Protect main with required PRs, CI, and CodeQL; protect release tags and enable immutable releases.
Validated with 48 offline tests on Linux and Windows, type and formatting checks, a reproducible bundle, and live Jev smoke tests across all three modes. The live tests include an expected per-file failure and use synthetic PR events. They do not measure model accuracy.
Use Victor-Casado/if-ai@v0.1.1, or pin this release's full commit SHA. Existing inputs and outputs are unchanged.
if-ai v0.1.0
if-ai v0.1.0
Write pull request checks in plain English using Jev. Supply a condition and a minimum confidence; the check passes only when both are satisfied.
- uses: Victor-Casado/if-ai@v0.1.0
with:
condition: This change does not remove or weaken existing tests.
min-confidence: '0.90'
mode: diff
api-key: ${{ secrets.TYPESAFE_API_KEY }}Diff modes require a full-history checkout. See the README for a complete workflow.
Included
- PR body, complete text diff, and per-file parallel evaluation.
- One check with file-specific failures. Every file must pass in per-file mode.
- Required condition and confidence threshold. False answers and low confidence fail the check.
- Boolean, confidence, status, failed-file, and detailed result outputs, plus a job summary.
- Bounded requests, response validation, and explicit failures for unsupported or oversized input. No silent truncation.
- Four workflow examples, including fork PR handling, an MIT license, and a bundled Node 24 Action.
Validation
Offline tests run on Linux and Windows. Live Jev tests exercised all three modes and an intentionally failing per-file fixture. The live harness uses synthetic PR events and actual Git commits; these checks verify integration behavior, not model accuracy. Validation details.
Initial limits
Text only, up to 200 changed paths, and a 28,000-byte request limit per evaluation. Binary files, LFS pointers, submodules, and missing content fail explicitly. Renames are evaluated as a deletion and an addition. Per-file evaluations have no cross-file context.
The selected content goes directly to TypeSafe using your API key. Confidence is model certainty, not a correctness guarantee. This is an independent project, not affiliated with TypeSafe or GitHub.