204 lines
9.5 KiB
Markdown
204 lines
9.5 KiB
Markdown
# PR#88: A Runner on the Workstation Adds a Review by the Codex CLI
|
|
|
|
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
|
|
|
|
## Related Links
|
|
|
|
- [PR#87](2026-10-11-PR#87-codex-eval.md): the measurement on 10 hs.hsadmin.ng PRs.
|
|
- Werkbaum: `#model.codex.gate`.
|
|
|
|
## Problem
|
|
|
|
On 10 hs.hsadmin.ng PRs, the Codex CLI with `gpt-6-astra` rated all 5 critical PRs as critical
|
|
and found two real RBAC defects that the model review by PR-Agent had not found.
|
|
werkrevisor does not use it yet.
|
|
|
|
## Non-Goals
|
|
|
|
- No Codex CLI on mih09: the subscription login stays on the workstation of the user.
|
|
|
|
## Specification
|
|
|
|
### Feature: a runner on the workstation reviews ready PRs with the Codex CLI
|
|
|
|
#### Scenario#88.01: a repository without a Codex mode gets no review
|
|
|
|
So that the subscription quota goes only to repositories that chose the review.
|
|
|
|
- **Given** a repository whose `.werkrevisor.toml` on the default branch has no `[codex]` section or `mode = "off"`
|
|
- **When** the runner runs
|
|
- **Then** it runs no Codex review, writes no comment and sets no status
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_a_repository_without_a_codex_mode_gets_no_review](../../tests/test_codexrunner.py)
|
|
- [test_config_layers.LayeringTest.test_the_repository_chooses_the_codex_mode](../../tests/test_config_layers.py)
|
|
|
|
#### Scenario#88.02: a PR waits for the result of werkrevisor
|
|
|
|
So that no quota goes to a PR that is not ready.
|
|
|
|
- **Given** a PR with a "WIP: " title, or a PR without a result comment of werkrevisor for its head
|
|
- **When** the runner runs
|
|
- **Then** it runs no Codex review
|
|
- **and** in `gate` mode, a PR without "WIP: " gets the status `ai-review/codex` `pending`
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_a_pr_waits_for_werkrevisor_and_its_wip_end](../../tests/test_codexrunner.py)
|
|
|
|
#### Scenario#88.03: a RED head gets no Codex review
|
|
|
|
So that no quota goes to a head that needs a new commit anyway.
|
|
|
|
- **Given** a head that werkrevisor rated RED
|
|
- **When** the runner runs
|
|
- **Then** it runs no Codex review
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_a_red_head_gets_no_codex_review](../../tests/test_codexrunner.py)
|
|
|
|
#### Scenario#88.04: `advise` comments once and holds nothing
|
|
|
|
So that a repository can see the Codex review before it lets it hold the merge.
|
|
|
|
- **Given** a repository with `mode = "advise"` and a ready PR with a result of werkrevisor
|
|
- **When** the runner runs twice
|
|
- **Then** one result comment and one line comment per finding in the diff exist, and no status is set
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_advise_comments_once_and_sets_no_status](../../tests/test_codexrunner.py)
|
|
|
|
#### Scenario#88.05: `gate` releases the merge only when nothing is open
|
|
|
|
So that a finding or a critical PR is not merged unseen.
|
|
|
|
- **Given** a repository with `mode = "gate"` and a Codex review of the head
|
|
- **When** the runner runs again
|
|
- **Then** `ai-review/codex` is `success` only when every Codex line comment is resolved
|
|
and, for a critical PR or a finding outside the diff, a human reviewer approved the head after the review
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_gate_passes_a_pr_without_findings](../../tests/test_codexrunner.py)
|
|
- [test_codexrunner.CodexRunnerTest.test_gate_holds_until_each_line_comment_is_resolved](../../tests/test_codexrunner.py)
|
|
- [test_codexrunner.CodexRunnerTest.test_a_critical_pr_waits_for_a_later_human_approval](../../tests/test_codexrunner.py)
|
|
- [test_codexrunner.CodexRunnerTest.test_a_finding_outside_the_diff_needs_a_human](../../tests/test_codexrunner.py)
|
|
- [test_codexrunner.CodexRunnerTest.test_an_unchanged_status_is_not_repeated](../../tests/test_codexrunner.py)
|
|
|
|
#### Scenario#88.06: a failed Codex run is retried, then reported
|
|
|
|
So that a broken run never passes as a review without findings.
|
|
|
|
- **Given** two failed Codex runs for a head, each with an error or without a valid answer
|
|
- **When** the third run fails too
|
|
- **Then** the runner writes an error comment
|
|
- **and** in `gate` mode the status `ai-review/codex` is `error` until a human reviewer approves the head
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_a_failed_run_is_retried_and_then_reported](../../tests/test_codexrunner.py)
|
|
- [test_codex.CodexTest.test_only_a_complete_answer_counts_as_a_review](../../tests/test_codex.py)
|
|
|
|
#### Scenario#88.07: no token and no login reaches the PR
|
|
|
|
So that a prompt injection in a PR cannot publish a secret through the answer of Codex.
|
|
|
|
- **Given** an answer of Codex that contains the Gitea token or a value of the Codex login
|
|
- **When** the runner posts it
|
|
- **Then** neither value appears in a comment
|
|
|
|
##### Verified by
|
|
|
|
- [test_codexrunner.CodexRunnerTest.test_no_token_or_login_reaches_the_pr](../../tests/test_codexrunner.py)
|
|
- [test_codex.CodexTest.test_the_login_values_are_secrets](../../tests/test_codex.py)
|
|
|
|
#### Scenario#88.08: werkrevisor does not wait for Codex comments
|
|
|
|
So that a Codex review in `advise` mode never stops a merge.
|
|
|
|
- **Given** a PR without findings of werkrevisor, whose only unresolved line comment comes from the Codex review
|
|
- **When** werkrevisor reviews the PR
|
|
- **Then** the merge of the PR is scheduled
|
|
|
|
##### Verified by
|
|
|
|
- [test_hints_hold_merge.HintsHoldTheMergeTest.test_a_codex_line_comment_holds_no_merge](../../tests/test_hints_hold_merge.py)
|
|
|
|
#### Scenario#88.09: Codex cannot read the secrets of the workstation
|
|
|
|
So that a prompt injection in a PR cannot make Codex quote a token from the home directory.
|
|
|
|
- **Given** a home directory with `~/.config/ai-coding-agents/secrets.env`
|
|
- **When** the runner starts Codex
|
|
- **Then** Codex can read only its login, the worktree with its repository and the programs on the `PATH`
|
|
|
|
##### Verified by
|
|
|
|
- [test_codex.CodexTest.test_the_jail_hides_the_home_except_the_login_and_the_programs](../../tests/test_codex.py)
|
|
|
|
## Solution
|
|
|
|
### Decisions
|
|
|
|
- The Codex review runs on the workstation, as a second stage after werkrevisor:
|
|
a runner asks Gitea for PRs whose head has a result of werkrevisor but no Codex review,
|
|
runs the Codex CLI on them and writes its result into the PR as `ai-reviewer`.
|
|
Reason: the login of the subscription must not reach a shared server or a build container;
|
|
a Codex review exists only while the workstation runs.
|
|
- `.werkrevisor.toml` chooses per repository whether and how the Codex review acts, with `[codex] mode`:
|
|
`off`, the default, means no Codex review;
|
|
`advise` writes comments only and holds nothing;
|
|
`gate` lets the merge wait for the Codex review of the head, makes its findings line comments that hold the merge,
|
|
and asks for the approval of a human when Codex rates the PR as critical or reports a finding outside the diff.
|
|
Repositories that want a Codex review start with `advise`.
|
|
Reason: in the measurement Codex rated 8 of 10 PRs as critical, 2 of them against the classification of the user,
|
|
and with `gate` a PR waits as long as the workstation is off.
|
|
|
|
- In `gate` mode the runner sets its own commit status `ai-review/codex`:
|
|
`pending` while the review runs or while a finding is open, `success` when no finding is open,
|
|
and for a critical PR or a finding outside the diff `success` only after the approval of a human for the head.
|
|
The branch protection of a `gate` repository requires this status, so the automerge waits for it.
|
|
Reason: Werkator sets `ai-review/gate` from the exit code of werkrevisor on mih09,
|
|
and nothing restarts that review when the runner is done;
|
|
werkrevisor only accepts `[codex]` and ignores the line comments of Codex.
|
|
|
|
### Implementation
|
|
|
|
- `werkrevisor/codex.py` holds the review by the Codex CLI: checkout, prompt, JSON schema, run and the check of the answer;
|
|
`tools/codex-eval` of PR#87 uses it too; `tests/test_codex_eval.py` now tests the tool itself.
|
|
- `werkrevisor/codexrunner.py` and `tools/codex-runner` look once at the open PRs of `config/codex-runner.toml` and exit;
|
|
the units in `tools/systemd/` start it every 5 minutes.
|
|
- Markers in the result comment hold the head, the criticality and the number of findings outside the diff,
|
|
so a later run decides the status without a new Codex run.
|
|
- `codex.run` starts Codex in `bwrap` with an empty home directory, except the login,
|
|
the worktree with its bare repository, the `node_modules` of the CLI and the program directories on the `PATH`.
|
|
Without `bwrap` no review runs.
|
|
A probe on 2026-10-11 showed: inside, `secrets.env` is missing, and `curl` ends with exit 6,
|
|
since the sandbox of Codex already blocks the network of its commands.
|
|
- Values of the Codex login from 8 characters on are secrets; shorter ones are words like `chatgpt`.
|
|
- `[codex] mode` in `.werkrevisor.toml`; werkrevisor accepts it and skips line comments with the Codex marker.
|
|
- `GiteaClient` lists open PRs and the statuses of a commit.
|
|
- README: `[codex]` and "Codex runner"; the PR workflow diagram shows the optional Codex review.
|
|
- Werkbaum: `#model.codex.gate`.
|
|
- Version 0.34.0.
|
|
|
|
## Open Questions
|
|
|
|
- [x] How do the criticality and the findings of Codex affect the merge?
|
|
Per repository, `off`, `advise` or `gate`; see "Decisions" under "Solution".
|
|
|
|
## Additional Changes
|
|
|
|
- The four tests of `tests/test_codex_eval.py` from PR#87 moved to `tests/test_codex.py` with the code they test;
|
|
`tests/test_codex_eval.py` now tests `tools/codex-eval` itself.
|
|
|
|
## Follow-up PRs
|
|
|
|
- Switch on `advise` in the `.werkrevisor.toml` of the first repositories; each change needs the approval of the human.
|
|
- Install the systemd units on the workstation.
|