Skip to content

Commit 7bca9ac

Browse files
Bordaclaude[bot]codex
committed
refine(codemap): anchor paths at git root, add guards
- Anchor every codemap index and log path at the git toplevel instead of the process working directory, across the provider and all four consumer plugins: queries from a repository subdirectory previously compared subdirectory-relative paths against root-relative index entries (permanent stale, self-heal every call) or derived a different index path than the writer (false no_index), and log shards split across two directories so session joins returned nothing. Provider-side this covers the staleness git anchoring, the memoized single git SHA query, the stale_undetermined verdict when git fails inside a repository, and the log-root resolver; consumer-side it rewrites cc_develop's resolver/scanner/gate and skill bash blocks, cc_foundry's six agent files, and cc_oss's review-mode context block. - Report the index file a query actually loaded as index.index_path, captured at load time rather than recomputed at emission; the codex-rig adapter records it per query, tolerates absence as null for older providers, and reports disagreement with the probe's resolver-derived path as evidence under index_path_divergence instead of reconciling it (structural-context artifact schema version 3). - Compose coexisting codex-rig caveats into stale+degraded instead of letting a stale index mask a coverage gap, and let a targetless standard batch omit target-requiring queries so analysis without --target can report an honest status. - Add fail-closed cache freshness to cc_oss's codemap cache via an index_stamp field (size plus mtime), and read the index under a shared rwgate lease in check-index-currency while raising the helper size ceilings from 50 MB to the engine's 512 MB — the old ceiling reported no_index for any index above 50 MB, measured against this repository's own 131 MB index. - Canonicalize module names from the index instead of sed-based path guessing: resolve_centrality.py gains --modules-only and ordered_modules(), the cc_oss review and dispatch modes query it, and consumer project-name sanitization is dropped for the provider's raw-basename rule. - Add guard infrastructure: new check_codemap_guard.py (MANIFEST-managed vs registry-declared vs provider-CLI taxonomy, wired into audit_static.py and a new check-codemap-guard pre-commit hook), a canonical codemap-context snippet for cc_foundry, and check_cli_flag_drift.py extended to validate flags in a script's own docstring Usage block against its argparse surface with origin tracking. - Align the shared codemap-gates contract with reality: the build route is codemap-py index (the scan-index alias leases in-engine and is a deprecated shim, not ungated as four prose sites claimed), consumer wrappers drop their now-redundant override clauses, and the inject-preamble hook's stale ungated-scan comment and model-facing directive are corrected. - Consolidate the five codemap-py hooks onto a shared _hookutil module for project_name and session_key so path derivation cannot silently diverge between the writer and reader of a session sentinel. - Port setup_scan_env.sh to stdlib-only setup_scan_env.py (Windows-safe, no python3-on-PATH dependency, shared format_scan_args() quoting) with the .sh kept as a deprecated exec shim, and convert claude-skills dispatcher invocations to bare PATH-literal codemap-py form. - Gate new codemap-py code on complexity limits (C901, PLR0911/0912/0915) from the root pyproject.toml via a negated per-file-ignore, refactor check_currency under the gate, and enumerate the six pre-existing offenders as accepted debt. - Correct the codex usage-aggregation docstring to what the README literally claims and record the audit of all 401 captured real turns (exactly one usage event per turn, so max() and sum() are indistinguishable on real data), and add benchmarks/conftest.py so doctest collection can import _bench_common. - Record per-skill codemap route selection in the codex-rig contract with a drift test, and bump plugin versions (codemap-py 0.30.0, codex-rig 0.7.5, cc_foundry 0.46.1, cc_develop 0.22.1, cc_oss 0.28.1, cc_research) with matching CHANGELOG and README updates. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com>
1 parent 67b5006 commit 7bca9ac

166 files changed

Lines changed: 7993 additions & 1441 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/codex-harness.sh

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,51 @@ case "${1:-}" in
2828
esac
2929

3030
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
31-
TMP_PARENT="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
31+
32+
# Git Bash on windows runners: bash keeps POSIX paths, but the child Python is a native
33+
# Windows build that cannot resolve them. Convert at every bash/native boundary.
34+
NATIVE_WINDOWS_PATHS=0
35+
case "$(uname -s)" in
36+
MINGW*|MSYS*|CYGWIN*)
37+
if command -v cygpath >/dev/null 2>&1; then
38+
NATIVE_WINDOWS_PATHS=1
39+
fi
40+
;;
41+
esac
42+
43+
to_child_path() {
44+
if [[ "$NATIVE_WINDOWS_PATHS" -eq 1 ]]; then
45+
cygpath -w "$1"
46+
else
47+
printf '%s\n' "$1"
48+
fi
49+
}
50+
51+
to_host_path() {
52+
if [[ "$NATIVE_WINDOWS_PATHS" -eq 1 ]]; then
53+
cygpath -u "$1"
54+
else
55+
printf '%s\n' "$1"
56+
fi
57+
}
58+
59+
TMP_PARENT="$(to_host_path "${RUNNER_TEMP:-${TMPDIR:-/tmp}}")"
3260
TMP_HOME="$(mktemp -d "$TMP_PARENT/codex-offline-home.XXXXXX")"
3361
TMP_BIN="$TMP_HOME/bin"
3462
RESULTS_DIR="${CODEX_HARNESS_RESULTS_DIR:-$ROOT/.github/codex-harness-results}"
3563
REAL_GIT="$(command -v git || true)"
64+
PYTHON="$(command -v python3 || command -v python || true)"
3665

3766
if [[ -z "$REAL_GIT" ]]; then
3867
echo "missing-command:git" >&2
3968
exit 2
4069
fi
4170

71+
if [[ -z "$PYTHON" ]]; then
72+
echo "missing-command:python3" >&2
73+
exit 2
74+
fi
75+
4276
cleanup() {
4377
rm -rf "$TMP_HOME"
4478
}
@@ -107,23 +141,50 @@ esac
107141
EOF
108142
chmod +x "$TMP_BIN/git"
109143

144+
# env -i also strips the Windows variables a native Python needs to seed hash randomization
145+
# (SystemRoot) and to spawn cmd/pwsh (ComSpec, PATHEXT); without them the child aborts with
146+
# _Py_HashRandomization_Init. Windows spells them mixed-case and bash is case-sensitive, so read
147+
# both spellings and emit both for SystemRoot. Empty on POSIX, where the env line is unchanged.
148+
CHILD_ENV=()
149+
if [[ "$(uname -s)" == MINGW* || "$(uname -s)" == MSYS* || "$(uname -s)" == CYGWIN* ]]; then
150+
WIN_SYSROOT="${SYSTEMROOT:-${SystemRoot:-}}"
151+
WIN_COMSPEC="${COMSPEC:-${ComSpec:-}}"
152+
if [[ -n "$WIN_SYSROOT" ]]; then
153+
CHILD_ENV+=("SystemRoot=$WIN_SYSROOT" "SYSTEMROOT=$WIN_SYSROOT")
154+
fi
155+
if [[ -n "$WIN_COMSPEC" ]]; then
156+
CHILD_ENV+=("COMSPEC=$WIN_COMSPEC")
157+
fi
158+
if [[ -n "${PATHEXT:-}" ]]; then
159+
CHILD_ENV+=("PATHEXT=$PATHEXT")
160+
fi
161+
fi
162+
110163
set +e
111164
CALIBRATION_OUTPUT="$(env -i \
112-
HOME="$TMP_HOME" \
113-
CODEX_HOME="$TMP_HOME/.codex" \
114-
TMPDIR="$TMP_PARENT" \
165+
${CHILD_ENV[@]+"${CHILD_ENV[@]}"} \
166+
HOME="$(to_child_path "$TMP_HOME")" \
167+
CODEX_HOME="$(to_child_path "$TMP_HOME/.codex")" \
168+
TMPDIR="$(to_child_path "$TMP_PARENT")" \
115169
PATH="$TMP_BIN:$PATH" \
116170
CI="true" \
117171
CODEX_OFFLINE_HARNESS="1" \
118-
"$ROOT/plugins/codex-rig/runtime/calibration/run.py" --layout plugin --root "$ROOT" 2>&1)"
172+
"$PYTHON" "$(to_child_path "$ROOT/plugins/codex-rig/runtime/calibration/run.py")" \
173+
--layout plugin --root "$(to_child_path "$ROOT")" 2>&1)"
119174
CALIBRATION_EXIT=$?
120175
set -e
121176

122177
if [[ -n "$CALIBRATION_OUTPUT" ]]; then
123178
printf '%s\n' "$CALIBRATION_OUTPUT"
124179
fi
125180

126-
RESULT_PATH="$(printf '%s\n' "$CALIBRATION_OUTPUT" | awk '/\/result\.json$/ { path = $0 } END { print path }')"
181+
# A native Windows Python prints a backslashed path, so accept either separator (the separator
182+
# itself stays required — it is what keeps prose lines from matching), then bring it back to a
183+
# POSIX path for the bash-side -f/dirname/cp below.
184+
RESULT_PATH="$(printf '%s\n' "$CALIBRATION_OUTPUT" | awk '$0 ~ "[/\\\\]result\\.json$" { path = $0 } END { print path }')"
185+
if [[ -n "$RESULT_PATH" ]]; then
186+
RESULT_PATH="$(to_host_path "$RESULT_PATH")"
187+
fi
127188
if [[ -z "$RESULT_PATH" || ! -f "$RESULT_PATH" ]]; then
128189
echo "missing-result-artifact: expected calibration run to print a result.json path" >&2
129190
exit 1
@@ -137,7 +198,7 @@ for artifact in result.json behavioral.json recommendations.md checks.txt leaks.
137198
done
138199

139200
STATUS="$(
140-
python3 - "$RESULT_PATH" "$RESULTS_DIR/summary.md" <<'PY'
201+
"$PYTHON" - "$(to_child_path "$RESULT_PATH")" "$(to_child_path "$RESULTS_DIR/summary.md")" <<'PY'
141202
import json
142203
import sys
143204
from pathlib import Path

.github/workflows/ci-harness.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,17 @@ on:
1919
permissions:
2020
contents: read
2121

22+
defaults:
23+
run:
24+
shell: bash
25+
2226
jobs:
2327
offline-harness:
24-
runs-on: ubuntu-latest
28+
runs-on: ${{ matrix.os }}
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
2533
timeout-minutes: 10
2634
env:
2735
CODEX_HARNESS_RESULTS_DIR: ".github/codex-harness-results"

.github/workflows/ci-manifests.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: CI manifests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
paths:
8+
- "plugins/**"
9+
- "benchmarks/**"
10+
- "pyproject.toml"
11+
12+
defaults:
13+
run:
14+
shell: bash
15+
16+
jobs:
17+
manifests:
18+
name: manifest ${{ matrix.manifest }} (${{ matrix.os }})
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
24+
manifest: ["provider-parity-methodology", "codex-integration", "codex-agentic"]
25+
timeout-minutes: 10
26+
env:
27+
UV_PYTHON: "3.12"
28+
steps:
29+
- name: 📥 Checkout
30+
uses: actions/checkout@v4
31+
32+
# The job-level UV_PYTHON is uv's own equivalent of --python, so no uv run below
33+
# repeats the version; the action input installs that interpreter.
34+
- name: 📦 Install uv and Python
35+
uses: astral-sh/setup-uv@v5
36+
with:
37+
python-version: ${{ env.UV_PYTHON }}
38+
39+
# Manifests are gitignored generated artifacts, so a fresh checkout has none to
40+
# compare against: build first, then --check that a rebuild is byte-identical.
41+
# The builders form a chain — codex-agentic hashes codex-integration.json, and both
42+
# Codex builders read provider-parity-methodology.json — so a job builds every
43+
# manifest its own target depends on before verifying that target alone.
44+
# Bytes are Python-version independent but embed an OS-resolved scan root, so the
45+
# matrix covers operating systems, not interpreter versions.
46+
- name: 🔨 Build provider-parity-methodology
47+
run: uv run --only-group test python benchmarks/build-provider-parity-methodology-manifest.py
48+
49+
- name: 🔨 Build codex-integration
50+
if: matrix.manifest != 'provider-parity-methodology'
51+
run: uv run --only-group test python benchmarks/build-codex-integration-manifest.py
52+
53+
- name: 🔨 Build codex-agentic
54+
if: matrix.manifest == 'codex-agentic'
55+
run: uv run --only-group test python benchmarks/build-codex-agentic-manifest.py
56+
57+
- name: 🔒 Verify ${{ matrix.manifest }} rebuilds byte-identically
58+
run: uv run --only-group test python "benchmarks/build-${{ matrix.manifest }}-manifest.py" --check

.github/workflows/ci-tests.yml

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,58 @@ defaults:
1515
shell: bash
1616

1717
jobs:
18+
codex-rig:
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: ["windows-latest"]
24+
timeout-minutes: 10
25+
env:
26+
UV_PYTHON: "3.12"
27+
steps:
28+
- name: 📥 Checkout
29+
uses: actions/checkout@v4
30+
31+
# The job-level UV_PYTHON is uv's own equivalent of --python, so no uv run below
32+
# repeats the version; the action input installs that interpreter.
33+
- name: 📦 Install uv and Python
34+
uses: astral-sh/setup-uv@v5
35+
with:
36+
python-version: ${{ env.UV_PYTHON }}
37+
38+
- name: 🪟 Verify native Codex Rig entrypoints
39+
shell: pwsh
40+
run: |
41+
$env:PYTHONDONTWRITEBYTECODE = "1"
42+
uv run --only-group test python plugins/codex-rig/scripts/build_package.py --check
43+
uv run --only-group test python plugins/codex-rig/scripts/validate_package.py
44+
uv run --only-group test python plugins/codex-rig/shared/collect_pr.py --help
45+
1846
tests:
1947
runs-on: ${{ matrix.os }}
2048
strategy:
2149
fail-fast: false
2250
matrix:
2351
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
2452
python-version: ["3.10", "3.11", "3.12", "3.13"]
25-
timeout-minutes: 10
53+
timeout-minutes: 25
2654

2755
steps:
2856
- name: 📥 Checkout
2957
uses: actions/checkout@v4
3058

31-
- name: 🐍 Set up Python
32-
uses: actions/setup-python@v5
33-
with:
34-
python-version: ${{ matrix.python-version }}
35-
3659
- name: 🟢 Set up Node
3760
uses: actions/setup-node@v4
3861
with:
3962
node-version: "20"
4063

41-
- name: 📦 Install uv
64+
# The action's python-version input sets UV_PYTHON, uv's equivalent of --python,
65+
# so the pytest run below does not repeat the matrix version.
66+
- name: 📦 Install uv and Python
4267
uses: astral-sh/setup-uv@v5
43-
44-
- name: 🔒 Verify deterministic benchmark manifests
45-
run: |
46-
uv run --only-group test --python ${{ matrix.python-version }} python benchmarks/build-provider-parity-methodology-manifest.py --check
47-
uv run --only-group test --python ${{ matrix.python-version }} python benchmarks/build-codex-integration-manifest.py --check
48-
uv run --only-group test --python ${{ matrix.python-version }} python benchmarks/build-codex-agentic-manifest.py --check
68+
with:
69+
python-version: ${{ matrix.python-version }}
4970

5071
- name: 🧪 Run plugin tests (Python bin/ + JS hooks)
51-
run: uv run --only-group test --python ${{ matrix.python-version }} pytest -W error::DeprecationWarning
52-
53-
- name: 🪟 Verify native Codex Rig entrypoints
54-
if: runner.os == 'Windows'
55-
shell: pwsh
56-
run: |
57-
$env:PYTHONDONTWRITEBYTECODE = "1"
58-
uv run --only-group test --python ${{ matrix.python-version }} python plugins/codex-rig/scripts/build_package.py --check
59-
uv run --only-group test --python ${{ matrix.python-version }} python plugins/codex-rig/scripts/validate_package.py
60-
uv run --only-group test --python ${{ matrix.python-version }} python plugins/codex-rig/shared/collect_pr.py --help
72+
run: uv run --only-group test pytest -W error::DeprecationWarning --duration=100

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,13 @@ repos:
259259
pass_filenames: false
260260
always_run: true
261261

262+
- id: check-codemap-guard
263+
name: 🗺️ unmanaged codemap index-guard copy
264+
language: system
265+
entry: python3 plugins/cc_foundry/bin/check_codemap_guard.py
266+
pass_filenames: false
267+
always_run: true
268+
262269
- id: check-bash-persistence
263270
name: 🫙 shell var persistence across Bash blocks
264271
language: system

AGENTS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ Simplicity and reliability come first. Understand the affected flow and root cau
1818

1919
Verification is part of implementation. Work is not complete until relevant checks pass and failures, residual risks, and deliberately deferred scope are reported accurately.
2020

21+
## Multi-OS Executables
22+
23+
Scripts, hooks, `bin/` entry points, and CI steps all run on Linux, macOS, and native Windows. A POSIX-only assumption is a defect to fix at the source, never a reason to skip the platform.
24+
25+
- `pathlib`; `Path(p).is_absolute()` not a leading-slash check; `PurePath(p).as_posix()` before hashing, serializing, or comparing a path — native separators change the digest.
26+
- POSIX-absolute literals are not portable fixtures: `/host/x` resolves to `D:\host\x` on Windows.
27+
- Byte-asserted or hashed writes use `newline="\n"` or bytes; text mode emits CRLF on Windows.
28+
- Sanitized subprocess `env=` keeps `SystemRoot`, `SYSTEMROOT`, `COMSPEC`, `PATHEXT`, `TEMP`, `TMP` on win32, else the child Python aborts before running; temp dirs via `os.environ.get("TMPDIR") or tempfile.gettempdir()`, never `/tmp`.
29+
- A workflow `run:` step invoking `.sh` needs explicit `shell: bash` — the Windows default shell dot-sources it and exits zero, a false green.
30+
- Symlinks, file modes, and uid checks are capabilities: degrade in production code first.
31+
- Skips are the last resort: never a blanket `skipif(sys.platform == "win32")`, always a capability probe skipping on `OSError`, with each surviving skip documented and re-audited.
32+
- Green macOS is absence of regression, not Windows support: prove Windows semantics with `PureWindowsPath` or `ntpath`, since monkeypatching `os.name` does not change `pathlib`.
33+
2134
## Benchmark Isolation
2235

2336
Benchmark task IDs, target repositories, prompt wording, expected answers, and task-specific source or symbol examples are test evidence, not production content. Never copy them into shipped plugins, Skills, templates, or user-facing docs; use neutral generic examples and encode the generalized contract in a regression test instead.

CLAUDE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ Hook ids (from `.pre-commit-config.yaml`): `ruff-check`, `ruff-format`, `eslint`
4242
- Bootstrap test tooling with `uv sync --only-group test`; benchmark-only dependencies use `uv sync --only-group bench`.
4343
- Run tests with `.venv/bin/python -m pytest <paths>`**not** `uv run pytest` or a bare `pytest`; the project venv is the pinned environment. Start focused, broaden to the affected suite before completion.
4444

45+
## Multi-OS Executables — POSIX Assumption = Defect
46+
47+
Scripts, hooks, `bin/`, CI steps all run Linux + macOS + native Windows. Fix at source; skip never.
48+
49+
- `pathlib`; `Path(p).is_absolute()` not `startswith("/")`; `PurePath(p).as_posix()` before hash/serialize/compare — separators change digests
50+
- POSIX-absolute literals unportable as fixtures: `/host/x``D:\host\x` on Windows
51+
- Byte-asserted or hashed writes: `newline="\n"` or bytes — text mode emits CRLF
52+
- Sanitized subprocess `env=` keeps `SystemRoot`, `SYSTEMROOT`, `COMSPEC`, `PATHEXT`, `TEMP`, `TMP` on win32 — else child Python aborts: `_Py_HashRandomization_Init: failed to get random numbers`; temp dir via `os.environ.get("TMPDIR") or tempfile.gettempdir()`, never `/tmp`
53+
- CI `run:` calling `.sh` needs explicit `shell: bash` — Windows pwsh dot-sources it, exits 0, runs nothing (false green)
54+
- Symlink/mode/uid = capabilities: degrade in production code first
55+
- Skip last resort: never blanket `skipif(sys.platform == "win32")` — probe capability, skip on `OSError`; document + re-audit each surviving skip
56+
- Green macOS ≠ Windows support: prove with `PureWindowsPath`/`ntpath`; monkeypatching `os.name` does not change `pathlib`
57+
4558
## Benchmark Isolation
4659

4760
Benchmark task IDs, target repositories, prompt wording, expected answers, and task-specific source or symbol examples are test evidence, not production content. Never copy them into shipped plugins, skills, templates, or user-facing docs; use neutral generic examples and encode the generalized contract in a regression test instead.

benchmarks/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ The current Codex agentic adapter uses all 16 committed BA tasks across `A_plain
109109
bash benchmarks/run-all.sh codex --agentic --dry-run
110110
```
111111

112-
The deterministic review lock is `benchmarks/manifests/codex-agentic.json`; regenerate or verify it with `python3 benchmarks/build-codex-agentic-manifest.py [--check]`. The dedicated human companion records the current manifest SHA, task order, treatment contract, exact approval variable, and retry-inclusive per-cell timeout in seconds. No-model dry runs require no credentials and no paid approval. A paid run requires the exact active machine-manifest SHA and private auth source; the launcher creates a fresh timestamped run directory automatically, with an optional `CODEX_RUN_DIR` override for a new path. Final run checksums attest the result artifacts, invocation launcher, and `source.sha256`; verify the archived source bytes separately with `(cd "$RUN_DIR/.launcher/source" && shasum -a 256 -c ../source.sha256)`. Codex CLI version is recorded as observed provenance only and is not a pinned or admission requirement. Each cell has only the retry-inclusive per-cell timeout; no total-run ceiling or wall-clock environment/CLI control applies. A non-default repetition or selected scope must additionally present the resolver's scope SHA-256.
112+
The deterministic review lock is `benchmarks/manifests/codex-agentic.json`; regenerate or verify it with `uv run python benchmarks/build-codex-agentic-manifest.py [--check]`. The dedicated human companion records the current manifest SHA, task order, treatment contract, exact approval variable, and retry-inclusive per-cell timeout in seconds. No-model dry runs require no credentials and no paid approval. A paid run requires the exact active machine-manifest SHA and private auth source; the launcher creates a fresh timestamped run directory automatically, with an optional `CODEX_RUN_DIR` override for a new path. Final run checksums attest the result artifacts, invocation launcher, and `source.sha256`; verify the archived source bytes separately with `(cd "$RUN_DIR/.launcher/source" && shasum -a 256 -c ../source.sha256)`. Codex CLI version is recorded as observed provenance only and is not a pinned or admission requirement. Each cell has only the retry-inclusive per-cell timeout; no total-run ceiling or wall-clock environment/CLI control applies. A non-default repetition or selected scope must additionally present the resolver's scope SHA-256.
113113

114114
For approval UX, the matching no-model dry run prints a lowercase 16-character SHA-256 scope prefix for copyable `--paid-approval` (or its equivalent approval variable). The complete 64-character scope SHA-256 remains recorded in run metadata and provenance, and the CLI accepts that full value as well. Never mix a prefix or full scope from another dry run with the selected command; regenerate approval after any locked-source change.
115115

benchmarks/_bench_codex/runtime.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,18 @@ def _ingest_usage(result: CodexParseResult, usage: Mapping[str, Any]) -> None:
187187
"""Fold one native usage event into the turn totals and count schema drift.
188188
189189
``max()`` rather than a running sum is deliberate: ``benchmarks/README.md``
190-
records that native Codex input usage is *cumulative within a turn*, so each
191-
usage event restates the turn total and summing would multiply the reported
192-
cost. That semantic is an assumption about the provider, pinned here only by
193-
a synthetic fixture in ``tests/test_codex_runtime.py``
194-
(``test_usage_events_are_treated_as_cumulative_not_additive``) — it has not
195-
been confirmed against a captured real stream. If a future CLI emits per-event
196-
deltas instead, that fixture is the contract to revisit before changing this.
190+
records that native Codex input usage is *cumulative within a turn* — its
191+
literal claim is only that cached input is a subset of gross input; the
192+
stronger reading that each usage event restates the turn total is this
193+
module's interpretation, not the README's assertion. An audit of every
194+
captured real stream (401 turns, 2026-08-13) found exactly one usage-bearing
195+
event per turn, always terminal — so ``max()``, ``sum()`` and last-wins are
196+
indistinguishable on real data and the cumulative property is unobservable
197+
there, while the subset claim held on all 401 events. The semantic stays
198+
pinned only by a synthetic fixture in ``tests/test_codex_runtime.py``
199+
(``test_usage_events_are_treated_as_cumulative_not_additive``). If a future
200+
CLI emits several usage events per turn, that fixture is the contract to
201+
revisit before changing this.
197202
"""
198203
result.raw_usage.update(dict(usage))
199204
for attribute, value in (

0 commit comments

Comments
 (0)