fix(action): expose Engine reasoning for released review limits (CW-6869) [skip ci]

The installed v0.10.2 binary identifies source 3400ebb5c9, whose review triage caps small reviews at 4000 output tokens regardless of the environment ceiling. Live runs 40830663408 and 40831324211 exhausted all 4000 tokens as reasoning and correctly published nothing. Current Engine source already removes that fixed cap, but unreleased source is not a substitute for a checksummed release. Add a validated reasoning-effort input, applied through a fresh secret-free isolated Engine configuration. Generated workflows default to off for this released compatibility boundary; users can select an explicit level or auto. Local verification: existing Action tests 8 passed, 0 failed; syntax, generated workflow actionlint and whitespace checks pass. No new tests or code comments; next live acceptance is on the newly pinned source.
This commit is contained in:
CodeWhale Bot committed 2026-10-28 23:53:08 -07:00
1 parent e278585f67
commit 9032b4f931
4 files changed
+25 -4

No files matched your search

+3
View File
@@ -13,6 +13,8 @@ inputs:
model:
description: Exact model ID. Account mode requires provider/model from the account catalog.
required: true
reasoning-effort:
description: Optional Engine reasoning setting (auto, off, low, medium, high, max).
github-token:
description: GitHub token with contents read and pull-requests read; legacy post:true also requires pull-requests write.
default: ${{ github.token }}
@@ -61,6 +63,7 @@ runs:
CW_VERSION: ${{ inputs.version }}
CW_PROVIDER: ${{ inputs.provider }}
CW_MODEL: ${{ inputs.model }}
CW_REASONING_EFFORT: ${{ inputs.reasoning-effort }}
CW_PR_NUMBER: ${{ inputs.pr-number }}
CW_TRIGGER_PHRASE: ${{ inputs.trigger-phrase }}
CW_ALLOW_FORKS: ${{ inputs.allow-forks }}
+8
View File
@@ -38,6 +38,13 @@ Ubuntu runner provides the review tools. No server or GitHub App is required.
`--action-ref FULL_COMMIT_SHA` with a separately downloaded `setup.mjs`.
`--max-output-tokens N` changes the explicit output allowance. Reasoning
models can exhaust smaller automatic allowances before writing findings.
Generated workflows use `reasoning-effort: off`: the released v0.10.2
binary has an internal 4000-token cap for small reviews that can be consumed
entirely by hidden reasoning. `--reasoning-effort auto` restores the
Engine default; other explicit effort levels are also available. The Action
writes only this non-secret setting into its isolated run configuration.
Increasing the Action allowance does not override a released CLI's smaller
internal cap. A completed review is still required before publication.
The generated workflow is `.github/workflows/codewhale-review.yml`.
2. Store only the selected provider's API key as a repository Actions secret:
@@ -141,6 +148,7 @@ your account's machine-key route has been separately verified. The Action runs
| `version` | Required | Exact released CLI tag, never `latest` |
| `provider` | Required | Explicit BYOK provider, or `codewhale` for a verified account machine key |
| `model` | Required | Exact model ID; account mode uses `provider/model` |
| `reasoning-effort` | Engine automatic | Optional explicit reasoning level; generated v0.10.2 workflows use `off` |
| `max-chars` | 200000 | Complete diff characters per pass, maximum 8388608 |
| `max-passes` | 1 | Complete ordered passes, maximum 64 |
| `max-output-tokens` | CLI automatic | Optional per-pass ceiling, 8192–1000000 |
+8 -2
View File
@@ -42,8 +42,10 @@ export function settings(env) {
}
if (!env.GH_TOKEN) stop('configuration_missing', 'github_token_required');
if (!['true', 'false'].includes(env.CW_POST || 'true')) stop('configuration_missing', 'invalid_post');
const reasoningEffort = env.CW_REASONING_EFFORT || 'auto';
if (!['auto', 'off', 'low', 'medium', 'high', 'max'].includes(reasoningEffort)) stop('configuration_missing', 'invalid_reasoning_effort');
return {
version: env.CW_VERSION, provider, model, post: env.CW_POST !== 'false',
version: env.CW_VERSION, provider, model, reasoningEffort, post: env.CW_POST !== 'false',
maxChars: integer(env.CW_MAX_CHARS, 200000, 1, 8388608),
maxPasses: integer(env.CW_MAX_PASSES, 1, 1, 64),
timeout: integer(env.CW_TIMEOUT_SECONDS, 600, 30, 1200) * 1000,
@@ -179,7 +181,7 @@ export function run(env = process.env, execute = command) {
if (config.post && (['issue_comment', 'pull_request_review_comment'].includes(env.GITHUB_EVENT_NAME)
|| env.CW_ALLOW_FORKS === 'true')) stop('configuration_missing', 'separate_publisher_required');
receipt = { ...receipt, repository: repo, pr_number: number, version: config.version,
provider: config.provider, model: config.model, limits: { max_chars: config.maxChars,
provider: config.provider, model: config.model, reasoning_effort: config.reasoningEffort, limits: { max_chars: config.maxChars,
max_passes: config.maxPasses, timeout_seconds: config.timeout / 1000, max_output_tokens: config.outputTokens } };
// Isolate user/project configuration and retain only the selected model key.
// Never execute with unrelated runner credentials or Git configuration.
@@ -191,6 +193,10 @@ export function run(env = process.env, execute = command) {
env[config.provider === 'codewhale' ? 'CODEWHALE_API_KEY' : KEYS[config.provider]];
if (config.outputTokens) childEnv.CODEWHALE_MAX_OUTPUT_TOKENS = String(config.outputTokens);
mkdirSync(childEnv.CODEWHALE_HOME, { recursive: true, mode: 0o700 });
if (config.reasoningEffort !== 'auto') {
childEnv.CODEWHALE_CONFIG_PATH = join(root, 'review.toml');
writeFileSync(childEnv.CODEWHALE_CONFIG_PATH, `reasoning_effort = "${config.reasoningEffort}"\n`, { mode: 0o600 });
}
const checked = (program, args, reason, extra = {}) => {
const result = execute(program, args, { env: childEnv, cwd: root, ...extra });
if (result.status !== 0 || result.error) stop('failed', reason);
+6 -2
View File
@@ -16,6 +16,7 @@ const HELP = `Create a Codewhale review bot workflow in your repository.
--provider NAME ${Object.keys(KEYS).join(', ')}
--model ID Exact model ID; account mode requires provider/model
--reasoning-effort E auto, off, low, medium, high, max (default: off)
--directory PATH Destination repository (default: current directory)
--version vX.Y.Z Checksummed CLI release (default: v0.10.2)
--action-ref SHA Reviewed Action commit (default: this clean source checkout)
@@ -31,7 +32,7 @@ Automatic reviews cover same-repository PRs. Maintainers can review fork PRs
with a manual run or an @codewhale review comment; PR code is never executed.
`;
export function workflow({ provider, model, version = 'v0.10.2', actionRef, triggerPhrase = '@codewhale', appLogin, maxOutputTokens = '16384' }) {
export function workflow({ provider, model, version = 'v0.10.2', actionRef, triggerPhrase = '@codewhale', appLogin, maxOutputTokens = '16384', reasoningEffort = 'off' }) {
if (!Object.hasOwn(KEYS, provider)) throw new Error('Choose a supported --provider.');
if (!/^[A-Za-z0-9][A-Za-z0-9_./:@+-]{0,199}$/.test(model || '')
|| (provider === 'codewhale' && !/^[A-Za-z0-9_.-]+\/.+/.test(model))) throw new Error('Choose an exact --model ID.');
@@ -40,6 +41,7 @@ export function workflow({ provider, model, version = 'v0.10.2', actionRef, trig
if (!/^@[A-Za-z0-9][A-Za-z0-9-]{0,38}$/.test(triggerPhrase)) throw new Error('--trigger-phrase must be @ followed by a GitHub-style name.');
if (appLogin && !/^[a-zA-Z0-9][a-zA-Z0-9-]{0,99}\[bot\]$/.test(appLogin)) throw new Error('--app-login must include [bot].');
if (!/^[1-9][0-9]*$/.test(String(maxOutputTokens)) || Number(maxOutputTokens) < 8192 || Number(maxOutputTokens) > 1000000) throw new Error('--max-output-tokens must be between 8192 and 1000000.');
if (!['auto', 'off', 'low', 'medium', 'high', 'max'].includes(reasoningEffort)) throw new Error('Choose a supported --reasoning-effort.');
const action = `codewhale-hq/Codewhale@${actionRef}`;
const secret = KEYS[provider];
const expression = value => '${{ ' + value + ' }}';
@@ -86,6 +88,7 @@ jobs:
version: '${version}'
provider: '${provider}'
model: '${model}'
reasoning-effort: '${reasoningEffort}'
max-output-tokens: '${maxOutputTokens}'
pr-number: ${expression('inputs.pr-number')}
trigger-phrase: '${triggerPhrase}'
@@ -155,6 +158,7 @@ export function setup(args = process.argv.slice(2)) {
version: { type: 'string' }, 'action-ref': { type: 'string' }, 'trigger-phrase': { type: 'string' },
'app-login': { type: 'string' }, print: { type: 'boolean' }, help: { type: 'boolean' },
'max-output-tokens': { type: 'string' },
'reasoning-effort': { type: 'string' },
} });
if (values.help) { console.log(HELP); return; }
let actionRef = values['action-ref'];
@@ -168,7 +172,7 @@ export function setup(args = process.argv.slice(2)) {
actionRef = head.stdout.trim();
}
const yaml = workflow({ provider: values.provider, model: values.model, version: values.version,
actionRef, triggerPhrase: values['trigger-phrase'], appLogin: values['app-login'], maxOutputTokens: values['max-output-tokens'] });
actionRef, triggerPhrase: values['trigger-phrase'], appLogin: values['app-login'], maxOutputTokens: values['max-output-tokens'], reasoningEffort: values['reasoning-effort'] });
if (values.print) { process.stdout.write(yaml); return; }
const directory = realpathSync(values.directory || process.cwd());
if (spawnSync('git', ['-C', directory, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim() !== directory) throw new Error('--directory must be a Git repository root.');