Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
60 changes: 58 additions & 2 deletions .github/workflows/claude-auto-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -535,17 +535,66 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# a codex-owned output dir under RUNNER_TEMP and git safe.directory,
# commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the pre-existing
# workspace directories, codex's files are world-readable under its
# umask, and the land step reclaims .git — the one place codex-owned
# dirs (objects/ fan-out) would block runner's writes. (Group kept as
# a faithful copy of the example; if codex ever drops a 600 file, the
# land step needs a chmod -R a+rX sweep, not this group.)
- name: Create codex user
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image. Not g+w on the temp root:
# codex's runner-group membership IS effective (its processes
# start post-usermod), so that would hand it rename/delete over
# the runner's step scripts and the per-step GITHUB_ENV/OUTPUT/
# PATH files. A codex-owned subdir grants only the output-file
# write; 755 keeps $OUT readable for the runner-side land step.
sudo install -d -o codex -g codex -m 755 "$RUNNER_TEMP/codex"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex fix
id: codexfix
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
allow-bot-users: "claude"
prompt-file: .codex-prompt.md
output-file: ${{ runner.temp }}/codex-final.md
output-file: ${{ runner.temp }}/codex/codex-final.md

- name: Land codex fix
id: codexland
Expand All @@ -556,11 +605,18 @@ jobs:
PR: ${{ inputs.pr_number }}
ROUND: ${{ steps.gate.outputs.round }}
MENTION: ${{ steps.gate.outputs.mention }}
OUT: ${{ runner.temp }}/codex-final.md
OUT: ${{ runner.temp }}/codex/codex-final.md
START_SHA: ${{ steps.base.outputs.sha }}
HEAD_BRANCH: ${{ steps.gate.outputs.head_branch }}
run: |
set -euo pipefail
# Reclaim .git before writing objects as runner: any objects/XX
# fan-out dir codex created (any object-writing git command) is
# codex-owned, and runner's codex-group membership is inert this
# job — the commit below (or the hand-back backstop's fetch)
# would intermittently fail with "insufficient permission for
# adding an object".
sudo chown -R runner "$GITHUB_WORKSPACE/.git"
if [ -n "$(git status --porcelain)" ]; then
# || true inside the substitution: without it a missing/unreadable
# $OUT fails the pipeline and set -e kills the step before the
Expand Down
60 changes: 58 additions & 2 deletions .github/workflows/claude-auto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,61 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# a codex-owned output dir under RUNNER_TEMP and git safe.directory,
# commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the pre-existing
# workspace directories, codex's files are world-readable under its
# umask, and the land step reclaims .git — the one place codex-owned
# dirs (objects/ fan-out) would block runner's writes. (Group kept as
# a faithful copy of the example; if codex ever drops a 600 file, the
# land step needs a chmod -R a+rX sweep, not this group.)
- name: Create codex user
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image. Not g+w on the temp root:
# codex's runner-group membership IS effective (its processes
# start post-usermod), so that would hand it rename/delete over
# the runner's step scripts and the per-step GITHUB_ENV/OUTPUT/
# PATH files. A codex-owned subdir grants only the output-file
# write; 755 keeps $OUT readable for the runner-side land step.
sudo install -d -o codex -g codex -m 755 "$RUNNER_TEMP/codex"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex CI fix
id: codexfix
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
# workflow_run's actor is the CI run's actor: normally the machine
Expand All @@ -372,7 +421,7 @@ jobs:
# other codex steps.
allow-bot-users: "claude"
prompt-file: .codex-prompt.md
output-file: ${{ runner.temp }}/codex-final.md
output-file: ${{ runner.temp }}/codex/codex-final.md

- name: Land codex fix
id: codexland
Expand All @@ -382,11 +431,18 @@ jobs:
REPO: ${{ github.repository }}
PR: ${{ steps.gate.outputs.pr }}
ATTEMPT: ${{ steps.gate.outputs.attempt }}
OUT: ${{ runner.temp }}/codex-final.md
OUT: ${{ runner.temp }}/codex/codex-final.md
START_SHA: ${{ steps.base.outputs.sha }}
HEAD_BRANCH: ${{ inputs.head_branch }}
run: |
set -euo pipefail
# Reclaim .git before writing objects as runner: any objects/XX
# fan-out dir codex created (any object-writing git command) is
# codex-owned, and runner's codex-group membership is inert this
# job — the commit below (or the hand-back backstop's fetch)
# would intermittently fail with "insufficient permission for
# adding an object".
sudo chown -R runner "$GITHUB_WORKSPACE/.git"
if [ -n "$(git status --porcelain)" ]; then
# || true inside the substitution: without it a missing/unreadable
# $OUT fails the pipeline and set -e kills the step before the
Expand Down
47 changes: 45 additions & 2 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,47 @@ jobs:
# SAME markers the @auto loop consumes. The verdict is thereby
# deterministic rather than prompt-enforced. Inline comments are a v1
# non-goal; findings carry file:line references in the body instead.
#
# codex-action's default safety-strategy (drop-sudo) chmods root-owned
Comment thread
ransomr marked this conversation as resolved.
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup is the action's examples/unprivileged-user.yml MINUS
# its workspace-write grants (the chown/g+rwX/g+s sweep and the — in
# any case inert — runner->codex group add): this run's profile is
# :read-only, so codex never writes the checkout, and reading it needs
# no grant (checkout files are world-readable; /home/runner traversal
# comes from the runner-group line), while the recursive sweep is pure
# latency over a fetch-depth-0 checkout of a large caller repo. The
# sibling workflows' codex steps keep the full recipe — they land
# workspace edits; this one only reads. Plus two grants the example
# never needs (it passes no explicit output-file and its demo prompt
# never runs git): a codex-owned output dir under RUNNER_TEMP and git
# safe.directory, commented inline.
- name: Create codex user
Comment thread
ransomr marked this conversation as resolved.
if: steps.trig.outputs.ok == 'true' && steps.engine.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G runner codex
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image. Not g+w on the temp root:
# codex's runner-group membership IS effective (its processes
# start post-usermod), so that would hand it rename/delete over
# the runner's step scripts and the per-step GITHUB_ENV/OUTPUT/
# PATH files. A codex-owned subdir grants only the output-file
# write; 755 keeps $OUT readable for the runner-side post step.
sudo install -d -o codex -g codex -m 755 "$RUNNER_TEMP/codex"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

# Our trig gate did the real authorization; allow-bot-users lifts the
# action's own actor guard for the reviewer-bot trigger the Claude
# path already accepts (the dev agent's @review hand-back).
Expand All @@ -628,11 +669,13 @@ jobs:
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
Comment thread
ransomr marked this conversation as resolved.
codex-user: codex
model: ${{ inputs.codex_model }}
effort: ${{ inputs.codex_effort }}
permission-profile: ":read-only"
allow-bot-users: "claude"
output-file: ${{ runner.temp }}/codex-review.json
output-file: ${{ runner.temp }}/codex/codex-review.json
output-schema: |
Comment thread
ransomr marked this conversation as resolved.
Outdated
{"type": "object", "additionalProperties": false,
"required": ["verdict", "review"],
Expand Down Expand Up @@ -685,7 +728,7 @@ jobs:
GH_TOKEN: ${{ secrets.MARVIN_TOKEN || github.token }}
REPO: ${{ github.repository }}
NUM: ${{ github.event.issue.number || github.event.pull_request.number }}
OUT: ${{ runner.temp }}/codex-review.json
OUT: ${{ runner.temp }}/codex/codex-review.json
run: |
set -euo pipefail
verdict=$(jq -r '.verdict // empty' "$OUT" 2>/dev/null || true)
Expand Down
59 changes: 57 additions & 2 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -588,12 +588,61 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# a codex-owned output dir under RUNNER_TEMP and git safe.directory,
# commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the pre-existing
# workspace directories, codex's files are world-readable under its
# umask, and the land step reclaims .git — the one place codex-owned
# dirs (objects/ fan-out) would block runner's writes. (Group kept as
# a faithful copy of the example; if codex ever drops a 600 file, the
# land step needs a chmod -R a+rX sweep, not this group.)
- name: Create codex user
if: steps.engine.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image. Not g+w on the temp root:
# codex's runner-group membership IS effective (its processes
# start post-usermod), so that would hand it rename/delete over
# the runner's step scripts and the per-step GITHUB_ENV/OUTPUT/
# PATH files. A codex-owned subdir grants only the output-file
# write; 755 keeps $OUT readable for the runner-side land step.
sudo install -d -o codex -g codex -m 755 "$RUNNER_TEMP/codex"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex
id: codexrun
if: steps.engine.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
# Authorization: codex-action's own built-in write-access check on
Expand All @@ -607,7 +656,7 @@ jobs:
# never pass steps.trig, so none reach this step — matching the
# Claude path, which sets no allowed_bots on this verb either.
prompt-file: .codex-prompt.md
output-file: ${{ runner.temp }}/codex-final.md
output-file: ${{ runner.temp }}/codex/codex-final.md

# Land the codex work: commit + push (as the machine account, so the
# push triggers CI), expose branch_name so the Open-or-adopt step below
Expand All @@ -625,10 +674,16 @@ jobs:
IS_PR: ${{ (github.event.issue.pull_request != '' || github.event.pull_request != '') && 'true' || 'false' }}
BRANCH: ${{ steps.codexprep.outputs.branch }}
START_SHA: ${{ steps.codexprep.outputs.sha }}
OUT: ${{ runner.temp }}/codex-final.md
OUT: ${{ runner.temp }}/codex/codex-final.md
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
run: |
set -euo pipefail
# Reclaim .git before writing objects as runner: any objects/XX
# fan-out dir codex created (any object-writing git command) is
# codex-owned, and runner's codex-group membership is inert this
# job — the commit below would intermittently fail with
# "insufficient permission for adding an object".
sudo chown -R runner "$GITHUB_WORKSPACE/.git"
# Untracked files count: new source files are legitimate codex
# output (the prompt file and common test/venv scratch are kept
# out via .git/info/exclude, written by the compose step).
Expand Down
Loading