Skip to content

feat(repo): Codex and Copilot CLI harness profiles, native review mode - #116

Open
napetrov wants to merge 40 commits into
intel:mainfrom
napetrov:feat/harness-profiles-codex-copilot
Open

napetrov wants to merge 40 commits into
intel:mainfrom
napetrov:feat/harness-profiles-codex-copilot

Conversation

@napetrov

Copy link
Copy Markdown
Contributor

Summary

repo run and repo reach had one built-in harness, Claude Code. This adds the Codex CLI and the Copilot CLI, including each CLI's own review mode, so the same task can be compared across guidance arms × harness × model.

 dokimi/repos/
 ├── profiles/
 │   ├── claude-code.yaml
+│   ├── codex.yaml            # codex exec --json … -- {prompt}
+│   ├── codex-review.yaml     # codex exec review --commit HEAD (native prompt)
+│   ├── copilot.yaml          # copilot -p {prompt} --output-format json
+│   └── copilot-review.yaml   # copilot -p "/review {prompt}"
 ├── events.py                 # trace: + codex-json, copilot-json
+├── cli_traces.py             # the two JSON converters -> events.jsonl
+├── answers.py                # answer_format: codex-review -> findings block
 ├── runner.py                 # answer_format applied; bin_companions mounted
 └── reach.py                  # refuses native_prompt; now writes config_files

harness_profile.v1 is widened in place with three optional keys:

native_prompt: true          # the harness's own prompt; invoke has no {prompt}
answer_format: codex-review  # "- [P1] title — path:12-14" -> ```json [{file, line, severity, rule, message}]
bin_companions: [codex-code-mode-host]   # mounted beside /eval/harness/codex
repo run cell (codex-review)
  agent container: /eval/harness/codex + codex-code-mode-host
    codex exec review --commit HEAD --json      # task prompt not passed
  convert("codex-json") -> events.jsonl
  convert_answer("codex-review") -> answer.txt  # text kept, ```json block appended
  grade                                         # review grader unchanged

Things that only show up when running the real CLIs:

  • codex review --commit refuses a prompt.
  • Codex 0.160 runs every tool call through codex-code-mode-host beside its binary, so a single-file mount leaves it with no tools.
  • An empty Copilot --available-tools list leaves every tool on, so none names a tool that doesn't exist.
  • npm installs both CLIs as Node launchers; --bin has to name the native binary.

Not here: hosted PR reviewers (Copilot code review, @codex review, CodeRabbit). They are not CLIs and need a separate PR-replay adapter.

Evidence

  • Before: repo run --harness codex fails with no harness profile 'codex' (built-in: claude-code). A profile with a Codex provider in config_files probed via repo reach hit wss://api.openai.com with a 401, because reach never wrote the file.
    After: live runs on oneDAL review_seeded_fc (fork dev/agent-evals c4e2667f5, image agent-benchmark/onedal:a28e8a6c33a7, gpt-6-luna through a LiteLLM proxy, guidance:raw, 1 rep):

    harness recall events notes
    codex 0.50 12 command, 9 read
    codex-review 0.375 7 command, 8 read same recall as an out-of-tree harness on 10-04
    copilot-review 0.625 5 command, 14 read, 13 tool 11 turns

    The first codex-review run, without bin_companions, answered "the repository command tool failed to start" and got recall 0.0.

  • Live repo reach on oneDAL main 7f6d9f86 (3 scenarios, gpt-6-luna):

                           cwd=.  cwd=cpp/daal  read=cpp/daal
    codex   AGENTS.md        L       L             L
            cpp/AGENTS.md    .       L             .
            cpp/daal/AGENTS  .       L             .
    copilot AGENTS.md        L       L             .   <- model refused to list tokens (prose answer)
            cpp/AGENTS.md    .       L             .
            cpp/daal/AGENTS  .       L             .
    

    Both CLIs load AGENTS.md from the git root down to cwd, and reading a file in a subtree does not pull in that subtree's AGENTS.md. In read= Codex skipped the read, because the reach ask says "use the Read tool" and Codex has no tool by that name. Rewording the ask is left for a follow-up: it would change Claude's recorded ask too.

  • Tests: tests/test_repo_cli_harnesses.py (29 tests) covers:

    • argv for each profile;
    • BYOK env pass-through;
    • refused native profiles;
    • shell_reads, including pipes, redirects and sed -i;
    • both converters, on shapes taken from real traces;
    • codex_review_findings;
    • a repo run cell that grades a native review answer;
    • bin_companions mounted, or a stop before any agent runs.

    test_repo_reach.py adds a test that a profile's config_files reach the probe.

  • make ci-local: every row passes except test. There, every PR-touched test passes, but 1–3 unrelated tests fail at random, a different one each run: test_process Popen ResourceWarning, test_container_agent "AF_UNIX path too long". This happens under 224 xdist workers on a shared box. pytest -n 32 gives 5208 passed, 2 skipped. Those two files alone: 149 passed.

Merge Danger

Door: two-way

New profiles and optional schema keys only. Profiles that already exist are left as they were. schema_compat passes against the base.

Blast Radius: repo reach

Every reach run now writes the profile's config_files. For claude-code that adds the no-background hook settings.json to each scenario's config dir. Reach scenarios run with tools none/read, so the Bash hook never fires. Only repo run / repo reach read the new keys.

🤖 Generated with Claude Code

Add built-in harness_profile.v1 files codex, codex-review, copilot and
copilot-review, so repo run and repo reach can compare guidance arms under
codex exec, codex exec review --commit HEAD, copilot -p and Copilot's
/review agent, not only Claude Code.

- trace: codex-json and copilot-json convert the CLIs' JSON event streams
  to events.jsonl (cli_traces.py); Codex read events come from shell
  commands that only print files.
- native_prompt: a profile may run the harness's own prompt; reach refuses it.
- answer_format: codex-review appends a findings block parsed from Codex's
  review text (answers.py), so review graders read it unchanged.
- bin_companions: helper files mounted beside the executable (Codex's
  codex-code-mode-host, without which no tool call runs in the container).
- repo reach now writes the profile's config_files, as repo run does.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@codecov-commenter

codecov-commenter commented Oct 19, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 90.64748% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.65%. Comparing base (5064dc2) to head (25dd17d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
dokimi/dokimi/repos/cli_traces.py 88.88% 23 Missing and 22 partials ⚠️
dokimi/dokimi/repos/runner.py 80.00% 2 Missing and 1 partial ⚠️
dokimi/dokimi/repos/harness_profile.py 90.47% 1 Missing and 1 partial ⚠️
dokimi/dokimi/repos/transcript.py 96.55% 2 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #116      +/-   ##
==========================================
+ Coverage   91.64%   91.65%   +0.01%     
==========================================
  Files         222      226       +4     
  Lines       27409    27990     +581     
  Branches     4508     4635     +127     
==========================================
+ Hits        25118    25654     +536     
- Misses       1620     1645      +25     
- Partials      671      691      +20     
Files with missing lines Coverage Δ
dokimi/dokimi/repos/answers.py 100.00% <100.00%> (ø)
dokimi/dokimi/repos/events.py 90.67% <100.00%> (-1.59%) ⬇️
dokimi/dokimi/repos/reach.py 96.61% <100.00%> (+0.06%) ⬆️
dokimi/dokimi/repos/harness_profile.py 94.61% <90.47%> (+0.59%) ⬆️
dokimi/dokimi/repos/transcript.py 96.55% <96.55%> (ø)
dokimi/dokimi/repos/runner.py 94.18% <80.00%> (+0.45%) ⬆️
dokimi/dokimi/repos/cli_traces.py 88.88% <88.88%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Trace conversion can distort evaluation results, and reach configuration produces invalid JSON on Windows.

4 open issues
What changed in this PR

Adds Codex and Copilot CLI harnesses so repository evaluations can compare guidance across agents, including native review modes.

Changes:

  • Adds four harness profiles and optional profile settings.
  • Converts CLI traces and native review answers into evaluation artifacts.
  • Applies profile configuration during reach probes, with tests and documentation.
File Description
dokimi/​tests/​test_repo_reach.py Tests reach configuration generation.
dokimi/​tests/​test_repo_cli_harnesses.py Tests profiles, converters, and runner integration.
dokimi/​dokimi/​schemas/​harness_profile.v1.json Adds optional harness capabilities.
dokimi/​dokimi/​repos/​runner.py Mounts companion binaries and converts answers.
dokimi/​dokimi/​repos/​reach.py Writes configuration and rejects native prompts.
dokimi/​dokimi/​repos/​profiles/​copilot.yaml Adds Copilot execution profile.
dokimi/​dokimi/​repos/​profiles/​copilot-review.yaml Adds Copilot review profile.
dokimi/​dokimi/​repos/​profiles/​codex.yaml Adds Codex execution profile.
dokimi/​dokimi/​repos/​profiles/​codex-review.yaml Adds Codex native review profile.
dokimi/​dokimi/​repos/​harness_profile.py Parses and validates new settings.
dokimi/​dokimi/​repos/​events.py Dispatches new trace formats.
dokimi/​dokimi/​repos/​cli_traces.py Normalizes Codex and Copilot events.
dokimi/​dokimi/​repos/​answers.py Converts native review findings.
dokimi/​docs/​repo-evaluation-contract.md Documents profiles and evaluation behavior.
dokimi/​docs/​architecture.md Describes new modules and harnesses.
dokimi/​changelog.d/​20261019_050000_nikolay.a.petrov_codex_copilot_profiles.md Records harness additions and reach fix.

Review effort: Balanced

Comment thread dokimi/dokimi/repos/reach.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py
Copilot AI balanced review requested due to automatic review settings October 19, 2026 08:02
…d, cd commands, and incomplete commands

On Windows, str(Path) contains backslashes that produce invalid JSON escape
sequences when inserted into configuration files. Use Path.as_posix() for both
config_dir and support_dir to ensure forward slashes.

shell_reads() now splits only on unquoted shell operators (&&, ||, ;, newline),
preserving quoted arguments like sed -n '1p;2p'. Previously, it split all
semicolons regardless of quoting context, breaking multi-range sed commands.

Track directory changes from cd commands in shell scripts so subsequent file
reads are resolved to the correct path. For example, 'cd sub && cat AGENTS.md'
now correctly reports reading 'sub/AGENTS.md'.

Preserve incomplete commands (item.started without item.completed) in Codex
JSON traces. When an agent times out mid-command, record the command with
rc: null per contract §4.1, instead of losing it entirely. Correlate started
and completed items by command text to update rc on completion.

Add regression tests for all three fixes.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/tests/test_repo_reach.py Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 09:11
Create tests/__init__.py and normalize test module imports to use explicit
'tests.' prefix. This resolves the mypy error where artifact_fixtures was
found under two module names. All imports of test fixtures now consistently
use the 'tests.' prefix (repo_eval_fixture, artifact_fixtures, test_process).

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Fix remaining imports from conftest, test_novelty_analyze, and test_repo_reach
to use explicit 'tests.' prefix for consistency now that tests is a proper package.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Fix remaining import from test_novelty_gate in test_novelty_items.py
to use explicit 'tests.' prefix for consistency.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The test-package conversion leaves sibling imports unresolved, breaking test collection and execution.

6 open issues

Review effort: Balanced

Comment thread dokimi/tests/__init__.py Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 09:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
This reverts commit 10c94f3.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
… prefix"

This reverts commit 191a7f1.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…licts"

This reverts commit be51812.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 10:28
…ay uv.lock

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 27911d6. The profiles, schema widening and mounting logic hold up, but the trace converters have several wrong-result cases, and the Codex findings parser is fragile. Four things are worth fixing before merge; the inline comments have the inputs and outputs.

What I ran, on a Linux review host at 27911d6:

  • make ci-local: 19 passed, 0 failed, 4 skipped (docker/semgrep/jobs that do not run on a PR); changed-line coverage gate passes.
  • Reproductions for the code findings, with no model or network calls.
  • A mutation run over the new tests: 36 of 83 mutants of cli_traces.py pass the suite; two of the severity-mapping mutants in answers.py pass too.

I agree with Copilot that Codex commands are matched by text, not item.id; it still holds (two overlapping make build items give rc [None, 0, 1]).

Checked, no issues

  • Schema: the harness_profile.v1.json change only widens the trace enum and adds optional keys, so no version bump is needed; schema_compat passes against the base; bin_companions names reject .. and /.
  • Docs: flags, keys, section numbers and the profile table match the code; the changelog fragment is present; no tests/__init__.py or uv.lock remains after the reverts.
  • Mounts and reach: a missing companion fails with a clear error, a symlinked --bin finds the companion beside the real file, and repo reach refuses native_prompt profiles before --dry-run.

Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/cli_traces.py Outdated
Comment thread dokimi/dokimi/repos/answers.py Outdated
Comment thread dokimi/dokimi/repos/answers.py

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at ba0b4a3. The fixes since 1f8d127 hold: the brace-group cd, the _LONG_VALUE table, the token sum and the severity mapping all give the right result on the cases from my threads. Three points are left, two worth fixing before merge and one nit; the inline comments have the details.

What I ran, on a Linux review host at ba0b4a3:

  • make ci-local: 26 passed, 0 failed, 4 skipped (docker, semgrep and jobs that do not run on a PR); 6,481 tests passed, 1 skipped; changed-line coverage 94% (490 lines).
  • Reproductions for the code findings, with no model or network calls.
  • A mutation run over the code and tests changed since 1f8d127 (5 mutants; 1 passes test_repo_cli_harnesses.py).

Checked, no issues

  • Brace groups: { cd x; } & cat f, { cd x; } 2>&1 | cat; cat f and { cd x; cat f; } & cat g give f, f and x/f, g.
  • Long options: every _LONG_VALUE entry run as <cmd> <option> 5 a gives ['a'].
  • Tests: failed = not result.ok fails the exit-0 failed-review test; dropping scenario.cwd from the convert() call in reach.py fails the new cwd=sub test; replacing the token sum with an assignment (input or output) fails test_codex_usage_is_summed_and_a_missing_or_zero_usage_is_not_a_measured_zero.
  • Docs: the contract table, architecture.md and copilot-review.yaml agree that copilot-review is a prompt variant; the schema change only widens, schema_compat passes, and the changelog fragment is present.

Comment thread dokimi/dokimi/repos/profiles/codex.yaml Outdated
Comment thread dokimi/tests/test_repo_cli_harnesses.py
Comment thread dokimi/changelog.d/20261019_050000_nikolay.a.petrov_codex_copilot_profiles.md Outdated
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit per nit, up to f038189, which fix the nit from my review:

  • f038189: dokimi/changelog.d/20261019_050000_nikolay.a.petrov_codex_copilot_profiles.md:3: replaced "Copilot's /review agent" with "copilot -p with a /review-prefixed prompt".

make ci-local at f038189: 26 passed, 0 failed, 4 skipped.

Not approving yet. Still open: 2 risk finding(s) from my review.

…l.parent

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@vchamarthi

Copy link
Copy Markdown
Contributor

@exai-bot /review

@exai-bot exai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

AI-generated review of 31719ab3f2cc, requested by @vchamarthi, using the default review skill. It can be wrong: verify a finding before acting on it. React with a thumbs up or down on a finding to tell the bot whether it helped.
Model us.anthropic.claude-sonnet-5-5, effort default, 19 of 94 turns, 1.8 of 48 minutes, 794k in / 12k out tokens.

This PR adds Codex and Copilot CLI harness profiles, including a native-review variant for each. It also adds two JSON trace converters, an answer_format step that appends a findings block for Codex review text, and bin_companions mounting. repo reach now writes a profile's config_files. The change is careful: format drift in review text suppresses the findings block rather than reading as a clean review, and a missing companion binary stops the cell before any agent runs. I found one minor issue, in how shell activity is read as evidence, and one nit. I could not run anything.

2 findings are on the changed lines, as comments below.

For a human reviewer

  • The PR describes copilot-review as Copilot's native review mode. The profile only sends /review <task prompt> as plain text under -p and does not set native_prompt. Check that the docs and the comparison framing say so.
  • The Tests (py3.13) check failed. The author attributes this to unrelated flaky tests under xdist. Confirm with a re-run.

Not verified

I ran nothing. The real Codex and Copilot output shapes, the codex exec review behaviour, the shell-parser quoting and heredoc corner cases, and the reach table all rest on the author's live runs and tests. I also read only the first 20,000 characters of tests/test_repo_cli_harnesses.py.

def unwrap_shell(command: str) -> str:
"""The script of ``/usr/bin/bash -lc '<script>'`` (how Codex reports a command), else ``command``."""
m = _BASH_WRAP.match(command.strip())
if not m:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor (plausible, correctness): Shell-derived read events miss piped/grep reads and count failed reads

  • Problem: shell_reads records a read only for a bare cat/head/tail/nl/sed -n with no pipe or redirect. cat AGENTS.md | head -50, grep -n . AGENTS.md, rg x AGENTS.md, python -c ... and awk produce no read event.
  • Problem: codex_json also emits the derived read events at item.started, before the exit code is known. A failed cat missing.md therefore still counts as a read.
  • Why it matters: repo reach classify tells fetched (read by a tool) from loaded (auto-loaded) using transcript.reads(). A Codex or Copilot run that fetched a subtree AGENTS.md through a pipe or grep is classed loaded, which is the distinction the probe exists to make. Codex's none tool mode still leaves shell access, per the profile's own comments, so those reads are possible in reach scenarios.
  • Fix: document the limit for these profiles next to the reach matrix. Optionally skip derived reads when the completed command has a non-zero exit_code.

Suggested fix:

Defer derived read events to item.completed and skip them when exit_code != 0. For reach, consider treating any shell command that names a probed path as a read.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bc66cd0. Verified: a derived read was emitted at item.started, so a failed 'cat missing.md' counted. Derived reads are now dropped when the command completes with a non-zero exit_code (test_codex_failed_command_does_not_count_as_a_read fails on the old code). Pipe/grep/rg reads remain unseen on purpose; the limit is now documented next to the fetched class in the reach section of repo-evaluation-contract.md.

import re
from collections.abc import Callable
from typing import Any

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (plausible, maintainability): Answer-format list is repeated in the schema and in code

ANSWER_FORMATS, _CONVERTERS and the answer_format enum in harness_profile.v1.json each list the formats. Adding a format to one but not the others fails late, after the agent has run, as a ValueError that run_cell records as a host-error failure. A small test that the three lists agree would catch this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in bc66cd0: test_answer_format_lists_agree asserts ANSWER_FORMATS, _CONVERTERS and the schema enum match, so drift fails in CI instead of after an agent run.

@napetrov

Copy link
Copy Markdown
Contributor Author

On the exai-bot review question about copilot-review: it is documented as a prompt variant, not Copilot's review agent — docs/repo-evaluation-contract.md:569 (profiles table) and the header comment of profiles/copilot-review.yaml both say that under -p the leading /review reaches the model as plain text, with the same system prompt and tools as copilot. py3.13 failure was the timing flake fixed on main by #147; main is merged in at bc66cd0.

…l files

cat sub/AGENTS.md sub/CLAUDE.md prints AGENTS.md and exits 1; dropping both reads made
reach class a fetched file as loaded. Only a one-part command naming one file is dropped.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
@xaleryb

xaleryb commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Re-ran at bc66cd0, on Linux, the original inputs of every earlier thread, plus real codex-cli 0.163 and Copilot CLI 1.0.100 against a local stub, and the full suite with ruff, mypy and bandit.

Still open:

  • codex.yaml / codex-review.yaml: a config_files provider without env_key and with CODEX_API_KEY=k sends no Authorization header. With env_key = "CODEX_API_KEY" it sends Bearer k. codex exec with OPENAI_API_KEY=x gets 401 "Missing bearer". Expected: the comment names env_key and says exec reads neither OPENAI_* name.

Pushed:

  • f2146f7: cat sub/AGENTS.md sub/CLAUDE.md prints AGENTS.md and exits 1. bc66cd0 dropped both reads, so reach classed the file loaded instead of fetched. Now only a failed one-part command that names one file loses its read. The contract line now says this, and limits it to Codex (Copilot still counts failed reads).

Left for the author: the comment wording above.

@napetrov

Copy link
Copy Markdown
Contributor Author

On the codex.yaml / codex-review.yaml wording (xaleryb): fixed in c1c400e. Both profile comments and the contract's Codex routing paragraph now say that codex exec reads neither OPENAI_API_KEY nor OPENAI_BASE_URL, that a config_files provider must set env_key = "CODEX_API_KEY" (without it Codex sends no Authorization header, 401 'Missing bearer'), and that nothing reads endpoint_env. Test: test_the_codex_profiles_tell_a_provider_in_config_files_to_name_its_key_variable. I did not re-run real codex against a stub; the env_key behaviour is taken from your measurement.

…ith env_key

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at c1c400e. Both open threads are fixed, and the new _failed_read logic gives the right result on the cases I tried. Two points are left: one test gap worth closing before merge, and one stale doc sentence.

What I ran, on a Linux review host at c1c400e:

  • make ci-local: 25 passed, 1 failed, 6 skipped; 6,811 tests passed, 1 skipped; changed-line coverage 94% (502 lines). The one failure is pip-audit (virtualenv 21.14.7, CVE-2026-113722, fix in 21.14.9). The PR does not touch the lock files, so it comes from the base, not from this PR; main fixed it in #152.
  • Reproductions for the code findings, with no model or network calls.
  • A mutation run over the code and tests changed since f038189 (_failed_read, the harness_mounts companion lookup, the env_key doc test, the format-list test).

Findings that do not anchor inline:

  1. Nit: docs/preparing-a-repository.md:207-210 still says only claude-code is built in. The sentence reads "Only the claude-code profile is built in; for Codex or Copilot, pass a harness_profile.v1 file" and "tool-use counts ... today means claude-stream-json". The PR adds built-in codex, codex-review, copilot and copilot-review profiles and the codex-json / copilot-json traces; the contract and architecture.md say so. A repository owner following this page writes a profile that already exists. Suggestion: say that claude-code, codex, codex-review, copilot and copilot-review are built in, and that the convertible traces are claude-stream-json, codex-json and copilot-json. (outside this diff)

Checked, no issues

  • Failed reads: cat a with rc 1 drops its read on both the started and the no-started path; cat a missing, cd x && cat f, cat a b and cat a; cat a keep theirs. Dropping len(reads) == 1, dropping the one-part check, or removing either branch's check each fails test_codex_failed_command_does_not_count_as_a_read or ..._keeps_the_reads_it_may_have_made.
  • Companion lookup: replacing real.parent with opts.harness_bin.parent in harness_mounts fails test_harness_mounts_takes_the_companion_from_beside_the_link_target.
  • Profile docs: env_key = "CODEX_API_KEY" is in both Codex profiles and in the contract; deleting it from any of the three fails the new test. The comment that nothing reads endpoint_env is right (grep finds only the parser).
  • Format lists: extending ANSWER_FORMATS or the schema enum alone fails test_answer_format_lists_agree.
  • Schema and merge: the schema change only widens; schema_compat against the base passes; ruff and mypy are clean on repos/; the changelog fragment is present and does not clash with main's fragments.

Comment thread dokimi/tests/test_repo_cli_harnesses.py
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit per nit, up to a22d109, which fix the nit from my review:

  • a22d109: dokimi/docs/preparing-a-repository.md:207: lists the built-in claude-code, codex, codex-review, copilot and copilot-review profiles and the claude-stream-json, codex-json and copilot-json traces.

make ci-local at a22d109: 25 passed, 1 failed (pip-audit, from the base), 6 skipped.

Not approving yet. Still open: the risk from my review.

@napetrov

Copy link
Copy Markdown
Contributor Author

Items from the review at c1c400e: the missing/null exit_code test is in 9667bb4 (replied on the thread); the stale "only claude-code is built in" sentence was fixed in a22d109. The pip-audit failure is from the base (#152), not this PR.

…s read

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ers and parse_profile

Behavior-preserving; the new functions are back under the complexity cap and
parse_profile is back at its baseline.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactor and the new test hold, and the open thread is closed. Two test gaps are worth closing before merge, and there is one nit.

What I ran, on a Linux review host at 0100221:

  • make ci-local: 28 passed, 1 failed, 7 skipped; 7,345 tests passed, 1 skipped; changed-line coverage 95% (555 lines). The one failure is pip-audit (gitpython 3.2.0, CVE-2026-114145, fix in 3.2.1). The PR does not touch the lock files, so it comes from the base.
  • A differential run of adfe925 and 0100221 against a22d109: 132,893 inputs (random Codex and Copilot streams, generated shell scripts through shell_reads, _split_script and _sed_files, and parse_profile inputs) give identical transcripts, events and ProfileError messages. The refactor changes no behaviour.
  • A mutation run over the code touched by adfe925 (55 mutants; 23 pass test_repo_cli_harnesses.py, test_repo_reach.py and test_repo_reach_edges.py). The two that matter are inline.

Findings that do not anchor inline:

  1. Nit: docs/preparing-a-repository.md:270 still says only claude-code is built in. The checklist item reads "dokimi repo reach (only claude-code is built in; others need a harness_profile.v1 file)". The PR now ships codex, codex-review, copilot and copilot-review, and line 208 says so. Lines 193-195 say "no loading behavior was measured" for Codex and Copilot, while the profiles' guidance.notes record measurements. Suggestion: change the checklist item to "dokimi repo reach (claude-code, codex, copilot and copilot-review are built in; codex-review runs its own prompt and is refused; others need a harness_profile.v1 file)". (outside this diff)

Checked, no issues

  • Thread on the unknown exit code: rc not in (None, 0) changed to rc != 0 fails both parametrizations of test_codex_command_with_an_unknown_exit_code_keeps_its_read (2 failed).
  • Refactor: _Splitter, _Walk, _SedScan, the by_id pop and the Copilot start None check behave as before. ruff is clean on repos/; mypy reports nothing under repos/.
  • Profiles and schema: all five built-in profiles load and build argv for none, read and all; the schema change only widens; schema_compat passes; the changelog fragment is present; no new doc page, so no nav change.

event["output"], event["output_truncated"] = clip_output(str(item["aggregated_output"]))
derived = event_reads.get(id(event), [])
if _failed_read(str(event["text"]), derived, event["rc"]): # drop the read events derived from it
dropped = {id(e) for e in derived}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test pins that a failed read drops only its own read events.

_codex_command_finished removes the reads derived from the failed command (dropped = {id(e) for e in derived}). Mutation check: changing the line to dropped = {id(e) for e in out.events if e["kind"] == "read"} removes every read in the trace, and the three files test_repo_cli_harnesses.py, test_repo_reach.py and test_repo_reach_edges.py stay green (225 passed).

Case it lets through: the agent runs cat AGENTS.md (exit 0), then cat missing.md (exit 1). With the mutant the trace has no read of AGENTS.md, so repo reach classes the file loaded instead of fetched. The failed-read tests in this file (test_codex_failed_command_does_not_count_as_a_read and its neighbours) never put a successful read before the failing command.

Test to add: an item.started / item.completed pair for cat AGENTS.md with exit_code 0, then one for cat missing.md with exit_code 1; assert the reads are ["AGENTS.md"].

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in d479a0f: test_codex_failed_command_drops_only_its_own_reads (test_repo_cli_harnesses.py). cat AGENTS.md exits 0, then cat missing.md exits 1; the reads stay ["AGENTS.md"] and the rcs are [0, 1]. It fails with the dropped set widened to every read event.

continue
shell = data.get("shellExecution") or {}
code = shell.get("exitCode") if isinstance(shell, dict) else None
event["rc"] = code if isinstance(code, int) else (None if data.get("success") is None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The success fallback for a Copilot command's exit code is not tested.

When shellExecution.exitCode is absent, rc comes from data["success"]. Every tool.execution_complete in test_repo_cli_harnesses.py carries shellExecution.exitCode (lines 351-352), so the fallback never runs. Mutation check: swapping the branches (1 if data.get("success") else 0) and, separately, returning 0 for a missing success both leave the three files green (225 passed). The first inverts the result: a successful command is recorded as rc: 1, a failed one as rc: 0.

Test to add: one tool.execution_complete with "success": true and no shellExecution, one with "success": false, and one with neither, asserting rc 0, 1 and None.

Related, at _CopilotRun.apply: rec.get("exitCode") not in (0, None) changed to != 0 also survives. This is the Copilot twin of the None case that test_codex_command_with_an_unknown_exit_code_keeps_its_read now pins for Codex. A result record without exitCode would set is_error and no test fails. Add a {"type": "result"} record with no exitCode and assert is_error is false.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in d479a0f: test_copilot_command_exit_code_falls_back_to_success (success true gives rc 0, success false gives rc 1, neither gives None) and test_copilot_result_without_an_exit_code_is_not_an_error (a result record with no exitCode leaves is_error false). They fail with the swapped success branches and with exitCode != 0 respectively.

Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
@xaleryb

xaleryb commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Re-checked at 0100221 on Linux: the original inputs of every earlier point (ours and every open thread) now pass, including real codex-cli 0.163 and Copilot CLI 1.0.100 against a local stub; adfe925 is behaviour-preserving on a 60k-input differential run against 9667bb4; the full suite, ruff, mypy and bandit are clean at the head and at the head merged into main.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit per nit, up to 25dd17d, which fix the nit from my review:

  • 25dd17d: dokimi/docs/preparing-a-repository.md:270: the checklist item now lists claude-code, codex, copilot and copilot-review as built in and says codex-review is refused.

make ci-local at 25dd17d: 28 passed, 1 failed, 7 skipped.

Not approving yet. Still open: the two risks from my review.

@napetrov

Copy link
Copy Markdown
Contributor Author

Review-body items from 1f8d127 and b4457f9: contract §9 now names bin_companions beside the mounted executable (d479a0f); token accumulation over several turn.completed is pinned by test_codex_usage_is_summed_and_a_missing_or_zero_usage_is_not_a_measured_zero (earlier commit); the duplicated SUPPORT_HOST and the _PRINTERS spacing are already fixed at the head (reach.py imports SUPPORT_HOST from runner.py).

… fallback; name bin_companions in contract §9

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants