Skip to content

codex-in-claude v0.13.0

Choose a tag to compare

@github-actions github-actions released this 15 Jul 13:58
· 139 commits to main since this release
f28d8a3

A review-honesty and rate-limit-recovery release. codex_review_changes no longer reports an
unreviewed working tree — the all-untracked shape most agent work takes — as a high-confidence
pass, and codex_status reads live rate-limit quota from the codex app-server again after
codex 0.144 moved it off the codex exec stream. Both are breaking on the agent-visible
surface: the result fingerprint moves twice (codex-in-claude/0.1/schema-44schema-46) and
RESULT_FORMAT twice (24), so pre-1.0 this is a minor release and clients that cache by
fingerprint re-fetch the contract.

Changed

  • codex_review_changes no longer reports an unreviewed tree as a high-confidence pass
    (#319, breaking). A working tree whose only changes were untracked (new) files — the most
    common shape of agent work — used to short-circuit to verdict: "pass", confidence: "high"
    with no model call, indistinguishable from a genuinely clean review. Now:

    • The result carries top-level review_status (completed | not_run) and a coverage object
      (status complete | partial; pathspec-scoped untracked_files_detected/included/omitted
      counts, null outside working_tree scope; a closed omission_reasons set of untracked_omitted
      / truncated / redacted). Untracked files are inventoried with git ls-files --others — a
      count only, no contents read, so the blind spot is disclosed at zero egress.
    • A review that never ran the model returns verdict: "unknown", confidence: "low",
      review_status: "not_run"never pass. A model pass over partial coverage (omitted
      untracked files, a truncated diff, or a redacted file) is surfaced as unknown/low with the
      caveat prefixed to summary; concrete fail/concerns findings are always retained.
    • A new untracked input (explicit_only default | include | exclude) on
      codex_review_changes, codex_review_changes_async, and codex_dry_run. explicit_only
      preserves #74 (only untracked files named in paths are reviewed); include reviews every
      non-ignored untracked file (opt-in egress — it sends their contents); exclude includes none.
    • codex_dry_run now reports would_call_model and the same coverage object, and its
      prompt_bytes is 0 when the previewed call would send nothing — matching the paid path
      instead of reporting the size of a prompt never sent (#320).
    • Git invocations in the diff-gathering path now run with -c core.fsmonitor=false, so a
      working-tree review of an untrusted repo cannot execute a repo-configured fsmonitor program in
      the server process.
    • Hardening (from an implementation review): the untracked inventory is stream-counted in
      bounded chunks (an untrusted workspace with arbitrarily many untracked files cannot exhaust
      memory); an invalid untracked policy reaching the core is rejected as invalid_arguments
      rather than silently behaving like exclude; the coverage counts come from a single
      enumeration so detected == included + omitted can't be violated under concurrent mutation,
      and Coverage now validates that invariant; review_status/would_call_model are required
      (no unsafe positive default); and the empty-review repair hint is tailored to the active
      untracked policy.

    Bumps FINGERPRINT (schema-44schema-45) and RESULT_FORMAT (23); clients that
    cache by fingerprint re-fetch the contract, and cross-release job replay of a review result
    written by an older version is refused rather than misread.

  • codex_delegate_dry_run's worktree preview counts in bounded memory (#323, #326). All three
    counts in worktree.plan() — untracked files, tracked files/bytes (git ls-tree -r --long), and
    uncommitted tracked files (git diff --numstat) — previously materialized their whole git listing
    in memory. The untracked count now delegates to the shared gitdiff.count_untracked inventory
    (the same NUL-delimited, fsmonitor-hardened enumeration codex_review_changes/codex_dry_run
    use), and the other two stream through a new shared _core/gitproc.run_lines runner (bounded
    per-line reader, concurrent capped stderr drain, process-group kill/reap on timeout or consumer
    failure — lifecycle guarantees ported from the diff streamer), so a repo with a pathological
    number of tracked, changed, or untracked files is counted without exhausting memory. Reported
    counts and failure semantics are unchanged — a git failure surfaces as a structured
    worktree_error (or, for numstat, still degrades to 0) instead of a silently-authoritative
    0 — so no fingerprint change. (The newline over-count originally filed as #323 did not
    reproduce: git C-quotes control characters, newline included, by default, so plan()'s non--z
    line-count was already correct.)

Fixed

  • codex_status reports live rate-limit quota again on codex 0.144+ (#321, breaking).
    codex 0.144 removed the token_count event that carried the quota block on the codex exec
    stream, so rate_limit had gone permanently unknown while the note told you to "run any Codex
    call to populate it" — advice that could never work. The data had moved to the app-server
    protocol, not disappeared. Now:

    • codex_status fetches quota live from codex app-server (account/rateLimits/read) — a
      read-only call with no model-token spend — reusing the hardened one-shot client that backs
      codex_transfer. The read is ephemeral: nothing is persisted, so codex_status stays a
      genuinely read-only call and no stale cache can mislead a spend decision. rate_limit.source
      is app_server_live.
    • Windows are re-slotted by duration, not by the app-server's slot order: primary is the
      shorter/rolling window (historically 5-hour), secondary the longer (weekly). The 0.144
      app-server reports only the windows that currently bind an account and may place the weekly
      window in the primary slot with no secondary — so a naive field rename would have kept the
      bug. An absent window is no longer treated as "unobserved," so a single healthy window now
      correctly reports available instead of a permanent unknown.
    • New rate_limit.status value unavailable (this codex/account exposes no quota data) and
      rate_limit.source value app_server_live; codex_status's meaning changes from a cached
      paid-run snapshot to a live read (breaking under the versioning rules — a closed-schema
      output meaning changed, and meta.rate_limit is now null on current CLIs). A read that finds
      the method missing, the protocol drifted, or a malformed result is surfaced as unavailable
      (never as a plausible "no quota") with a note that the plugin may need an update — a loud
      signal, not another silent unknown. A committed real-shape fixture plus an integration test
      against the live app-server guard against the next such drift.
    • Untrusted app-server output is hardened: planType is length-bounded and
      rateLimitReachedType is accepted only from the known enum (an unknown value is dropped, never
      trusted as a false exhausted); a cached reason code degrades to unknown once every window
      has reset; a pathological numeric field (e.g. a 400-digit usedPercent) degrades to absent
      instead of raising; two windows are duration-sorted so primary is always the shorter horizon;
      and the read response is correlated on an unpredictable request id so a prequeued/unsolicited
      message can't be trusted as quota.
    • The dead exec-stream quota parser (normalize.parse_rate_limit), the per-run capture, and the
      snapshot cache (CODEX_IN_CLAUDE_RATE_LIMIT_FILE) are removed.

    Bumps FINGERPRINT (schema-45schema-46) and RESULT_FORMAT (34) for the added
    enum values and the changed meaning of the rate_limit block.