Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ All notable changes will be documented here. This project follows Semantic Versi
- Added the explicit `fast` profile with a single required-lens batch for lower latency and predictable calls.
- Added CI Action inputs for profile, deadline, and health-check policy.

### Fixed

- Made provider-call preflight estimates demand-driven for adversarial verification while preserving the hard, fail-closed runtime call ceiling.

## [0.2.3] - 2026-08-30

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ AgentsKit Code Review is built around a different contract:
- **Low noise by design.** Findings are challenged by independent verification votes before they survive.
- **Local first, CI ready.** Review a diff before pushing, inspect complete paths, read stdin, or comment directly on a GitHub PR.
- **Control cost and policy.** Set file budgets, concurrency, thresholds, project conventions, and blocking severity.
- **See the cost before execution.** Use `--plan --json` to inspect files, lenses, retries, concurrency, deadline, and estimated provider calls without a model request. Plans label estimates as `bounded` when `thresholds.maxPerFile` is set; otherwise they are `best-effort` because model output volume is inherently variable.
- **See the cost before execution.** Use `--plan --json` to inspect files, lenses, retries, concurrency, deadline, and estimated provider calls without a model request. Estimates are `best-effort` because model output volume is inherently variable; the runtime call ceiling remains hard and fail-closed.

## Run your first review

Expand Down Expand Up @@ -275,7 +275,7 @@ In shortened examples, replace `...` with `npx --yes github:AgentsKit-io/code-re
| `--min-confidence <n>` | Minimum reported confidence |
| `--max-files <n>` | Positive file budget; over-budget runs are refused before the provider |
| `--max-calls <n>` | Provider-call budget; absolute ceiling `1000` |
| `--max-findings-per-file <n>` | Maximum verified findings per file; bounds adversarial verification calls |
| `--max-findings-per-file <n>` | Maximum verified findings per file; caps adversarial verification work |
| `--concurrency <n>` | Parallel model calls; default `1` for CLI providers, `4` for API providers |
| `--deadline-ms <n>` | Global run deadline; defaults to `600000` (`120000` for `fast`) |
| `--health-check <auto\|off>` | Bounded provider smoke check before model fan-out |
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ inputs:
required: false
default: '1000'
max-findings-per-file:
description: 'Maximum findings verified per file; keeps the provider-call estimate bounded.'
description: 'Maximum findings verified per file; caps adversarial verification work.'
required: false
default: '7'
profile:
Expand Down
11 changes: 6 additions & 5 deletions agents/code-review/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,15 +684,16 @@ export function createCodeReviewAgent(config: CodeReviewConfig) {
const enabledLenses = lenses.map((lens) => lens.key)
const required = [...requiredLenses]
const primaryCalls = files * (batched ? 1 : enabledLenses.length) * (1 + retries)
const maxFindingsPerFile = config.thresholds?.maxPerFile
const verificationCalls = files * (maxFindingsPerFile ?? enabledLenses.length) * auditVotes * (1 + retries)
const estimatedProviderCalls = primaryCalls + verificationCalls + (files && enabledLenses.length ? 1 : 0)
// Verification is demand-driven: reserve only the optional consolidation call here.
// The runtime counter remains the hard ceiling and fails closed if candidates exhaust it.
const consolidationReserve = files && enabledLenses.length ? 1 : 0
const estimatedProviderCalls = primaryCalls + consolidationReserve
const plan: ReviewPlan = {
profile,
batched,
files, bytes, enabledLenses, requiredLenses: required, votes: auditVotes, retries, concurrency,
estimatedProviderCalls,
providerCallEstimate: maxFindingsPerFile === undefined ? 'best-effort' : 'bounded',
providerCallEstimate: 'best-effort',
maxCalls, unreviewedFiles: all.length - files, overBudget: [], suggestions: [], deadlineMs,
}
const maxFiles = config.budget?.maxFiles
Expand All @@ -710,7 +711,7 @@ export function createCodeReviewAgent(config: CodeReviewConfig) {
plan.suggestions.push('reduce scope with --paths or an isolated context pattern')
}
if (estimatedProviderCalls > maxCalls) {
const perFile = Math.max(1, (batched ? 1 : enabledLenses.length) * (1 + retries) + (maxFindingsPerFile ?? enabledLenses.length) * auditVotes * (1 + retries))
const perFile = Math.max(1, (batched ? 1 : enabledLenses.length) * (1 + retries))
plan.overBudget.push(`${estimatedProviderCalls} estimated provider calls exceed maxCalls ${maxCalls}`)
plan.suggestions.push(`reduce scope to at most ${Math.max(1, Math.floor((maxCalls - 1) / perFile))} files or lower --votes`)
}
Expand Down
2 changes: 1 addition & 1 deletion docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ Then require the workflow check in branch protection. CLI exit codes are:

A model response that is malformed may drop one lens while other lenses continue; progress output and the final summary report successful and failed primary-lens counts. If any reviewable file cannot be ingested or has zero successful primary lenses, the pipeline stops before reporters run and exits `2`, including in advisory mode. Treat missing output or exit `2` as unavailable review, not approval.

Use `--plan --json` (or `--dry-run`) to run the source and budget preflight without a model request. The plan reports profile, batching, files, bytes, enabled and required lenses, votes, retries, concurrency, deadline, estimated provider calls, and concrete reductions when a limit would be exceeded. Estimates are `bounded` when `thresholds.maxPerFile` is set and `best-effort` otherwise, because model output volume is variable. The preflight refuses before the provider starts; `maxCalls` is capped at 1000 and unlimited mode is not supported. A required-lens failure is `INCOMPLETE` and exits `2`, including with `--no-fail`.
Use `--plan --json` (or `--dry-run`) to run the source and budget preflight without a model request. The plan reports profile, batching, files, bytes, enabled and required lenses, votes, retries, concurrency, deadline, estimated provider calls, and concrete reductions when a limit would be exceeded. Estimates are `best-effort` because verification calls depend on findings produced by the primary lenses; the runtime call ceiling remains hard and fails closed if exhausted. The preflight refuses before the provider starts when primary lens coverage itself cannot fit; `maxCalls` is capped at 1000 and unlimited mode is not supported. A required-lens failure is `INCOMPLETE` and exits `2`, including with `--no-fail`.

## Cost and latency controls

Expand Down
10 changes: 7 additions & 3 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ AgentsKit Code Review is built around a different contract:
- **Low noise by design.** Findings are challenged by independent verification votes before they survive.
- **Local first, CI ready.** Review a diff before pushing, inspect complete paths, read stdin, or comment directly on a GitHub PR.
- **Control cost and policy.** Set file budgets, concurrency, thresholds, project conventions, and blocking severity.
- **See the cost before execution.** Use `--plan --json` to inspect files, lenses, retries, concurrency, deadline, and estimated provider calls without a model request. Plans label estimates as `bounded` when `thresholds.maxPerFile` is set; otherwise they are `best-effort` because model output volume is inherently variable.
- **See the cost before execution.** Use `--plan --json` to inspect files, lenses, retries, concurrency, deadline, and estimated provider calls without a model request. Estimates are `best-effort` because model output volume is inherently variable; the runtime call ceiling remains hard and fail-closed.

## Run your first review

Expand Down Expand Up @@ -293,7 +293,7 @@ In shortened examples, replace `...` with `npx --yes github:AgentsKit-io/code-re
| `--min-confidence <n>` | Minimum reported confidence |
| `--max-files <n>` | Positive file budget; over-budget runs are refused before the provider |
| `--max-calls <n>` | Provider-call budget; absolute ceiling `1000` |
| `--max-findings-per-file <n>` | Maximum verified findings per file; bounds adversarial verification calls |
| `--max-findings-per-file <n>` | Maximum verified findings per file; caps adversarial verification work |
| `--concurrency <n>` | Parallel model calls; default `1` for CLI providers, `4` for API providers |
| `--deadline-ms <n>` | Global run deadline; defaults to `600000` (`120000` for `fast`) |
| `--health-check <auto\|off>` | Bounded provider smoke check before model fan-out |
Expand Down Expand Up @@ -654,7 +654,7 @@ Then require the workflow check in branch protection. CLI exit codes are:

A model response that is malformed may drop one lens while other lenses continue; progress output and the final summary report successful and failed primary-lens counts. If any reviewable file cannot be ingested or has zero successful primary lenses, the pipeline stops before reporters run and exits `2`, including in advisory mode. Treat missing output or exit `2` as unavailable review, not approval.

Use `--plan --json` (or `--dry-run`) to run the source and budget preflight without a model request. The plan reports profile, batching, files, bytes, enabled and required lenses, votes, retries, concurrency, deadline, estimated provider calls, and concrete reductions when a limit would be exceeded. Estimates are `bounded` when `thresholds.maxPerFile` is set and `best-effort` otherwise, because model output volume is variable. The preflight refuses before the provider starts; `maxCalls` is capped at 1000 and unlimited mode is not supported. A required-lens failure is `INCOMPLETE` and exits `2`, including with `--no-fail`.
Use `--plan --json` (or `--dry-run`) to run the source and budget preflight without a model request. The plan reports profile, batching, files, bytes, enabled and required lenses, votes, retries, concurrency, deadline, estimated provider calls, and concrete reductions when a limit would be exceeded. Estimates are `best-effort` because verification calls depend on findings produced by the primary lenses; the runtime call ceiling remains hard and fails closed if exhausted. The preflight refuses before the provider starts when primary lens coverage itself cannot fit; `maxCalls` is capped at 1000 and unlimited mode is not supported. A required-lens failure is `INCOMPLETE` and exits `2`, including with `--no-fail`.

## Cost and latency controls

Expand Down Expand Up @@ -964,6 +964,10 @@ All notable changes will be documented here. This project follows Semantic Versi
- Added the explicit `fast` profile with a single required-lens batch for lower latency and predictable calls.
- Added CI Action inputs for profile, deadline, and health-check policy.

### Fixed

- Made provider-call preflight estimates demand-driven for adversarial verification while preserving the hard, fail-closed runtime call ceiling.

## [0.2.3] - 2026-08-30

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion readme-standard-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@
"docs/OPERATIONS.md",
"test/cli-smoke.test.mjs"
],
"sourceHash": "sha256:d60d942075696d400f19636b9c3fa87ca09742b7b29e54028b6c74c9c036193c"
"sourceHash": "sha256:c2095d3b266dbfc347c94e4968eaf99c97161db2966353c2a1c14b93d75c6f2e"
},
"exceptions": []
}
Expand Down
39 changes: 34 additions & 5 deletions test/cli-smoke.test.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { execFileSync, spawnSync } from 'node:child_process'
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, join, resolve } from 'node:path'
import { tmpdir } from 'node:os'
import test from 'node:test'
import { codexCli } from '../dist/src/codex-adapter.js'
import { createCodeReviewAgent } from '../dist/agents/code-review/agent.js'

const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')

Expand Down Expand Up @@ -266,7 +267,7 @@ test('plan is provider-free and machine-readable', () => {
assert.equal(plan.overBudget.length, 0)
})

test('plan supports a bounded findings-per-file limit', () => {
test('plan accepts a findings-per-file limit without reserving its worst case', () => {
const run = spawnSync(process.execPath, [
'dist/src/cli.js', '--provider', 'codex-cli', '--stdin', '--dry-run', '--json', '--max-findings-per-file', '2',
], {
Expand All @@ -276,8 +277,36 @@ test('plan supports a bounded findings-per-file limit', () => {

assert.equal(run.status, 0, run.stderr)
const plan = JSON.parse(run.stdout)
assert.equal(plan.providerCallEstimate, 'bounded')
assert.equal(plan.estimatedProviderCalls, 27)
assert.equal(plan.providerCallEstimate, 'best-effort')
assert.equal(plan.estimatedProviderCalls, 15)
})

test('plan keeps verification demand-driven when primary coverage fits the budget', async () => {
const cwd = mkdtempSync(join(tmpdir(), 'agentskit-adaptive-plan-'))
try {
execFileSync('git', ['-C', cwd, 'init', '-q'])
for (let i = 0; i < 20; i++) writeFileSync(join(cwd, `file-${i}.ts`), 'export const value = 1\n')
execFileSync('git', ['-C', cwd, 'add', '.'])
execFileSync('git', ['-C', cwd, '-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-qm', 'initial'])
for (let i = 0; i < 20; i++) writeFileSync(join(cwd, `file-${i}.ts`), 'export const value = 2\n')
execFileSync('git', ['-C', cwd, 'add', '.'])
execFileSync('git', ['-C', cwd, '-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-qm', 'change'])

const plan = await createCodeReviewAgent({
source: { kind: 'git-diff', base: 'HEAD~1', cwd },
auditVotes: 3,
retries: 1,
thresholds: { maxPerFile: 7 },
budget: { maxFiles: 50, maxCalls: 1000 },
reporters: [],
}).plan()

assert.equal(plan.files, 20)
assert.equal(plan.unreviewedFiles, 0)
assert.equal(plan.providerCallEstimate, 'best-effort')
assert.equal(plan.estimatedProviderCalls, 281)
assert.equal(plan.overBudget.length, 0)
} finally { rmSync(cwd, { recursive: true, force: true }) }
})

test('fast profile batches required lenses and stays within a small call budget', () => {
Expand Down
Loading