The Configure OCR step derived llm.protocol from llm_use_anthropic alone,
so it always wrote openai or anthropic. Anyone on the Responses API was
sent to /v1/chat/completions no matter what OCR_LLM_PROTOCOL they set.
Add an optional llm_protocol input (anthropic, openai, openai-responses).
When it is set, or when OCR_LLM_PROTOCOL is inherited from the job
environment, that value is written and llm.use_anthropic is mirrored
from it, the same way `ocr config set llm.protocol` mirrors the boolean.
An unknown value fails the step with the accepted list. The explicit
protocol joins the checkpoint fingerprint, appended only when set so
existing checkpoints stay valid.
Fixes#1134
* feat(action): opt-in resolution of the bot's own outdated review threads (#567)
Adds a resolve_outdated input, default off, that resolves review threads
GitHub has already marked outdated. No model is involved in the decision:
the signal is GitHub's server-computed isOutdated, and five vetoes sit on
top of it, each of which can only refuse to resolve a thread.
Three modes: 'false' (no GraphQL calls at all), 'report' (log the verdict
for every thread, mutate nothing), 'true'.
* fix(action): address review on resolve_outdated
- Veto by path instead of disabling the whole run: a finding with a path but
no line is ordinary output and now costs only its own path, while a finding
with no path at all still disables the run.
- Normalize paths before the overlap veto, so ./src/a.js and src/a.js no
longer compare unequal and resolve a thread over a live finding.
- Resolve the authenticated login once per run rather than once per caller.
- Validate resolve_outdated in the Validate inputs step, so a typo fails
before the LLM runs rather than silently falling back to off.
- Correct the permission documentation from measurement, and show the
contents: write scope in a job that never checks out the pull request.
- Split the overloaded unverified reason, fix the documented preview count,
list the missing outputs, and replace the nested ternary with a Map.
* feat(action): add optional pr_number input and workflow_run fallback
The pull request number was read from the event payload in two places: the
fetch step's `github.event.pull_request.number || github.event.issue.number`,
and `context.issue.number` in the range and posting steps. Neither resolves on
`workflow_run`, whose payload names the pull request only under
`workflow_run.pull_requests[0]`. The review therefore ran to completion and the
posting step then requested /repos/OWNER/REPO/issues//comments, got a 404, and
the findings were lost.
"Resolve PR refs" now resolves the number once — the new optional `pr_number`
input, then the event payload, then `workflow_run.pull_requests[0].number` —
publishes it as $PR_NUMBER, and fails there when nothing resolves or the value
is not a PR number, before OCR is installed or any LLM quota is spent. The head
fetch, the checkpoint resolver and the posting step all read that one value;
runPostReviewComments takes it as an option and still falls back to
context.issue.number when a caller omits it.
Behavior is unchanged for pull_request, pull_request_target and issue_comment:
an unset input falls through to the existing resolution.
Closes#1150
* style(action): use strict equality for the pr_number override
The review guidelines rule out loose equality, so spell out the null and undefined checks that `!= null` covered.
* feat(action): expose effort and max_tokens_budget as inputs
- Add validated, case-insensitive effort and base-10 max_tokens_budget
workflow inputs, forwarded to ocr review as --effort and
--max-tokens-budget; empty values omit the flags so CLI defaults apply
- Reject an explicit effort input on ocr older than v1.10.0, where the
flag first shipped, instead of dying on an unknown flag
- Join both axes to the checkpoint config fingerprint
Closes#1147
Signed-off-by: thxCode <thxcode0824@gmail.com>
* feat(action): opt-in live review progress via stream_progress
- add stream_progress input (true|false, case-insensitive, default
'false'), validated and normalized in the Validate inputs step
- the default keeps the original behavior: --audience agent in the
review args, stderr captured to /tmp/ocr-stderr.log with no live tee
- stream_progress=true drops --audience agent so human-audience routing
sends [ocr] progress lines to stderr while the result JSON stays on
stdout; a FIFO feeds a background tee so progress streams live into
the workflow log and is still captured to /tmp/ocr-stderr.log for
artifacts and the posting step
- the tee runs as a real background job and is awaited before the log
file is read, so the capture is fully flushed; OCR_EXIT_CODE is
unaffected in either mode
Task 2 of action-review-controls.
Signed-off-by: thxCode <thxcode0824@gmail.com>
* feat(action): first-class llm_reasoning_effort input
- Merge llm_reasoning_effort into the effective llm.extra_body via node
in the Configure step, riding the existing extra_body merge so any
published CLI supports it; an explicit reasoning_effort key in
llm_extra_body wins
- Reject llm_reasoning_effort on the anthropic protocol, and reject a
malformed or non-object llm_extra_body with an actionable ::error::
- Fingerprint the normalized effort, max_tokens_budget, and
llm_reasoning_effort values instead of the raw inputs, so equivalent
spellings keep the checkpoint
- Keep the last 20000 characters of stderr in the unparseable-result
summary so streamed progress cannot push the error report past
GitHub's comment limit
- Document the new action inputs on the CI/CD docs page (en/zh/ja/ko/ru)
Closes#1149
Signed-off-by: thxCode <thxcode0824@gmail.com>
---------
Signed-off-by: thxCode <thxcode0824@gmail.com>
* fix(agent): scale subtask timeout linearly with effort review rounds
Previously the timeout applied a flat 1.5x multiplier whenever
ReviewRounds > 1, giving both medium (2 rounds) and high (3 rounds)
the same deadline. Replace this with a linear formula:
timeout = base × ReviewRounds(), producing 10/20/30 min defaults
for low/medium/high effort respectively.
* chore: bump default --timeout from 10 to 15 minutes
The base unit of 10 min was too tight for real-world reviews.
Raising it to 15 min gives linear scaling of 15/30/45 min
for low/medium/high effort respectively.
Update CLI flags, action.yml, docs, and skill files.
* test: update action-contract assertions for 15-min default timeout
On every push the reusable Action re-reviews the whole merge-base range, so
later pushes on a large PR cost the same as the first one. This adds an opt-in
`checkpoint_range` input that records the last completely reviewed head in the
sticky summary and narrows the next run to checkpoint..head.
Every gate fails closed to a full review: force-push, base change, config or
rule change, incomplete prior run, or any resolver error.
The config fingerprint hashes one environment variable per axis instead of a
single "|"-joined string. The joined form let a value containing the separator
shift the field boundaries, so `route_severity_below=low|a` with an empty
`route_categories` hashed identically to `low` with `route_categories=a|` —
a checkpoint surviving a config change that should have invalidated it. It also
made the digest depend on how the YAML happened to be wrapped.
`llm_extra_headers`, `llm_auth_header` and `llm_timeout` are axes too: extra
headers can point the same `llm_model` string at a different backend model or
provider, and a timeout change shifts which runs finish. Header values can be
credentials, so that axis goes in as a digest rather than raw.
The author check is pinned to `github-actions` when `github_token` is the
default one, which always belongs to that app. Unpinned, `dependabot[bot]`,
`renovate[bot]` and any linter App all read as ours. A caller-supplied token
keeps the wider bot check, since an installation token cannot ask GitHub which
app it is.
A version `ocr version` does not print yields an empty fingerprint rather than
a `spec:` fallback: `ocr_version` defaults to "latest", so the spec is a
constant that distinguishes nothing. Empty matches no stored fingerprint and
blocks the checkpoint write — don't narrow, don't advance.
The contract suite asserts examples/github_actions/README.md's documented
defaults, so a README change has to be able to trigger it. Expressing that
inside ci.yml meant inverting paths-ignore into a three-stage paths filter,
which also pulled the whole matrix — Go race tests, native Windows,
cross-compile — on a markdown-only change.
Move the job into its own workflow with a narrow positive paths filter and
restore ci.yml's original paths-ignore. The job keeps the self-hosted runner
and the node:24 container it was given in review, so the queueing behaviour
does not change.
The workflow assertions in action-contract.test.js now read the new file;
their regexes are unchanged. Since that file holds a single job, the
checkout SHA-pin and container-image assertions become structurally precise
instead of incidentally so: previously they searched all of ci.yml, so a
floating tag on this job could still pass as long as some pinned action
existed elsewhere in the file.