Skip to content

ci: smoke the pinned Codex CLI surface, and bump it off the alpha line - #923

Open
tend-agent wants to merge 7 commits into
mainfrom
fix/issue-922
Open

ci: smoke the pinned Codex CLI surface, and bump it off the alpha line#923
tend-agent wants to merge 7 commits into
mainfrom
fix/issue-922

Conversation

@tend-agent

@tend-agent tend-agent commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

codex_version is pinned at 0.131.0-alpha.22 in codex/action.yaml while latest is 0.147.0. The weekly rule governing it asked for a confirmation nothing in this repo could produce — "bump only to a release confirmed to run under codex exec" — because there is no OPENAI_API_KEY in the tend environment (gh secret list --env tend returns only CLAUDE_CODE_OAUTH_TOKEN and TEND_BOT_TOKEN), no harness: codex override in .config/tend.yaml, and no codex step in ci.yaml. The bump condition was unsatisfiable, so the pin drifted by default rather than by decision.

Both proposals in #922 need that same missing secret, so neither is actually the cheaper one. What doesn't need it is the part of the confirmation that is mechanically checkable — and it turns out to be the part where a silent break would land.

Solution

A test-codex-surface job in ci.yaml, shaped after the existing test-proxy smoke: read the pin out of the action with yq -e, install it, and assert the surface the action depends on.

  • Every flag the Run Codex step passes — --model, --sandbox, --output-last-message, --config. Matching on --flag < pins each to an option that still takes a value, so a release that keeps the name but drops the argument fails too.
  • The Installed plugin root: prefix that codex plugin add prints and the action parses with awk to set CLAUDE_PLUGIN_ROOT, plus that both trees skills reach for through that variable land under it — skills/ and scripts/. Only skills/ is declared in plugin.json, so scripts/ materializing is a packaging side effect a release could stop honouring while every skill shelling out to ${CLAUDE_PLUGIN_ROOT}/scripts/ breaks.

No credential is involved: codex --version, codex exec --help, codex plugin marketplace add, and codex plugin add are all local. So the job runs on every PR, which also guards the other direction — a change to .agents/plugins/marketplace.json or the plugin layout that Codex can no longer install now fails here rather than in an adopter's job, which nothing catches today.

The bump target moves to latest, and the pin moves with it. The premise behind the prerelease pin has expired: codex plugin add was alpha-only when the pin was set, but stable 0.147.0 carries it. So the running-tend weekly rule now reads track latest (dropping to alpha only for a fix not yet released), the two codex/action.yaml comments that still said latest was 0.130.0 and lacked the subcommand are corrected, and codex_version is bumped 0.131.0-alpha.220.147.0. The bump ships here rather than separately because this job is its confirmation — the gate and the first bump it gates are the same review, and the job would otherwise land green against a pin nobody had re-examined.

The rule also states plainly what stays unverified without a key — sandbox behavior under a real session, and whether the agent's final message actually reaches --output-last-message — with an instruction to skim the codex CHANGELOG for those paths, mirroring the claude_version rule.

Testing

The job's script body ran end-to-end in this session with no OpenAI credential present, against the old pin, the new one, and the current alpha. Beyond that, --strict-config turns out to validate -c overrides, which reaches further than --help matching does: it confirms model_reasoning_effort is still a recognized field at 0.147.0 rather than a silently-ignored one.

Surface probe
Version Flags plugin add parse skills/ + scripts/
0.131.0-alpha.22 (old pin) pass pass pass
0.147.0 (new pin, latest) pass pass pass
0.148.0-alpha.5 (alpha) pass pass pass

codex exec --help at 0.147.0, identical to the old pin for every flag the action passes:

  -c, --config <key=value>
  -m, --model <MODEL>
  -s, --sandbox <SANDBOX_MODE>
  -o, --output-last-message <FILE>

--sandbox still accepts danger-full-access, which is the action's default: [possible values: read-only, workspace-write, danger-full-access].

Config-key probe, and how far a keyless run gets

--strict-config rejects unknown -c keys, so it can distinguish "field still exists" from "field silently ignored":

$ codex exec --strict-config -c 'model_reasoning_effortZZZ="low"' ...
Error loading config.toml: unknown configuration field `model_reasoning_effortZZZ` in -c/--config override

$ codex exec --strict-config -c 'model_reasoning_effort="low"' ...
(config loads; proceeds past config to the git-repo check)

Running the action's exact argument set with a deliberately invalid key gets all the way to the API call, so every flag and the gpt-5.5 model string were accepted and the only thing that stopped it was auth:

$ codex exec --strict-config --skip-git-repo-check --model gpt-5.5 \
    --sandbox danger-full-access --output-last-message /tmp/o.md \
    -c 'model_reasoning_effort="low"' "say hi"
ERROR: unexpected status 401 Unauthorized: Missing bearer or basic authentication in header

Not attempted, and not reachable here: a successful codex exec session — so the final message actually landing in --output-last-message, and sandbox behavior under a live model, remain unverified.

CHANGELOG skim, rust-v0.132.0rust-v0.147.0

One removal in range touches codex exec's flag surface: "Remove the deprecated codex exec --full-auto flag; use --sandbox workspace-write instead" (#36054, in rust-v0.147.0). The action never passed --full-auto — it passes --sandbox explicitly — so it is unaffected.

Nothing else in range removes or renames a flag the action passes, and plugin work across the range is additive (search, remote catalogs, manifests) rather than changing the local marketplace add / plugin add path this depends on.


Refs #922. The agent-session half stays open: it needs a maintainer to put an OPENAI_API_KEY in the tend environment, which is the single decision both of the issue's options reduce to. (The body previously said Closes #922 while also saying it didn't cover that half — corrected here so merging doesn't auto-close it.)

The weekly rule holds codex_version to a bump 'confirmed to run under
codex exec', a confirmation nothing in this repo could produce — so the
pin drifted seventeen minors by default rather than by decision.

Add a credential-free CI job that installs the pinned version and
asserts the two things a silent break would hit: the flags the action
passes to codex exec, and the 'Installed plugin root: ' line it parses
to set CLAUDE_PLUGIN_ROOT. A bump PR now verifies itself for the half
that needs no key, and a plugin-manifest change codex can no longer
install fails here rather than in an adopter's job.

Closes #922

@tend-agent tend-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The job works — I installed the pinned 0.131.0-alpha.22 and ran the script body end-to-end with no OpenAI credential: all four codex exec flags match, codex plugin marketplace add + codex plugin add succeed, the Installed plugin root: line parses, and the skill file is there. Reviewing as the author, so flagging rather than approving.

One thing the rule change probably wants to decide the other way. It codifies dist-tags.alpha as the bump target on the premise that codex_version "has no release stream to track either" — but that premise has expired. npm view @openai/codex dist-tags.latest is now 0.147.0, sixteen minors past the 0.131.0-alpha.17 that first shipped codex plugin add. I installed the stable 0.147.0 and ran this PR's entire smoke script against it: all four flags present, both plugin subcommands succeed, Installed plugin root: parses, and skills/ and scripts/ both materialize under the returned root. So there is a release stream to track, and it's the more conservative one — the new job would confirm it exactly the same way it confirms an alpha.

The knock-on is that merging as-is leaves two in-repo instructions pointing opposite ways. codex/action.yaml's codex_version description still reads "On npm this currently lives on the alpha dist-tag — latest is still 0.130.0 and lacks it, so the default pins an explicit alpha rather than tracking latest", and the Install Codex CLI step comment ends "Bump the default once a stable npm release ships PR #21396" — a condition that is now met. The next weekly run reads that alongside the new skill paragraph and gets contradicting guidance; whichever it follows, the pin stays on a prerelease line for a reason that no longer holds. The mechanical half is the inline suggestion below; the table row's "keep it on its prerelease line" and those two comments in codex/action.yaml would need to move with it. That's a policy call rather than a fix I'd push unilaterally, so leaving it for a maintainer.

Smaller: the smoke re-implements the action's awk -F': ' '/^Installed plugin root: /' parse rather than sharing it, so a change to the action's parsing can leave this job greening a pattern production no longer uses — the failure mode the adjacent test-proxy job avoids by running shared/steps/install-proxy-uv.sh "rather than repeating it". #910 is already extracting shared/steps/install-codex-cli.sh, which would be a natural home for the install-and-parse pair.

Verification notes

@openai/codex dist-tags at review time: latest: 0.147.0, alpha: 0.148.0-alpha.5.

Full smoke script run against stable 0.147.0:

codex-cli 0.147.0
OK --model
OK --sandbox
OK --output-last-message
OK --config
Added marketplace `tend` from /home/runner/work/tend/tend.
Added plugin `tend-ci-runner` from marketplace `tend`.
Installed plugin root: <CODEX_HOME>/plugins/cache/tend/tend-ci-runner/0.1.0
ROOT OK
SKILL OK
SCRIPTS OK

codex plugin --help on 0.147.0 lists add, list, marketplace, remove — the subcommand the pin's prerelease line was chosen for is on stable.

Comment thread .claude/skills/running-tend/SKILL.md Outdated
Comment thread .github/workflows/ci.yaml
Review found the premise had expired: stable 0.147.0 carries `codex
plugin add` and passes this PR's whole smoke script, so the alpha line
is a leftover rather than a requirement. Point the weekly rule at
`latest` and fix the two action.yaml comments that still said `latest`
was 0.130.0 and lacked the subcommand.
plugin.json declares only `skills`, but skills shell out to
${CLAUDE_PLUGIN_ROOT}/scripts/ — so a packaging change that stops
materializing scripts/ would break every one of them with the job green.
@tend-agent

Copy link
Copy Markdown
Collaborator Author

On the shared-parse point: agreed in principle, and I've left it out of this PR deliberately. #910 is open and already creating shared/steps/install-codex-cli.sh — the exact file that would host the install-and-parse pair — so extracting it here would collide with that PR in the same file for the same reason. The better sequence is #910 lands, then a follow-up moves both the action step and this job onto the shared script, which is when the test-proxy parallel (running install-proxy-uv.sh rather than repeating it) actually becomes available.

Worth noting the duplication is narrower than it looks in the meantime: the job asserts the prefix contract (Installed plugin root: followed by a real directory), which is what upstream can break, rather than reproducing the action's variable handling. If the action changed its own parse the job would still be checking the thing the action needs to exist.

@tend-agent tend-agent changed the title ci: smoke the pinned Codex CLI surface so the pin can be bumped ci: smoke the pinned Codex CLI surface, and bump it off the alpha line Aug 9, 2026

@tend-agent tend-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ran the job's script body end-to-end against the newly pinned 0.147.0 with no OpenAI credential: all four flags present in codex exec --help, codex plugin marketplace add and codex plugin add both succeed, Installed plugin root: parses, and skills/ and scripts/ both land under the returned root with list-recent-runs.sh carrying its executable bit. The flag list matches the action's ARGS array exactly — --model, --sandbox, --output-last-message, and the conditional --config — with nothing else reaching codex exec. Reviewing as the author, so commenting rather than approving.

One file the retarget missed. ddb735c corrected the skill's table row and both codex/action.yaml comments, but the test-codex-surface job's own header comment still opens on the premise those corrections retired — inline below. Second inline is the skill paragraph leading with history instead of the rule.

Verification notes
codex-cli 0.147.0
OK --model
OK --sandbox
OK --output-last-message
OK --config
Added marketplace `tend` from /home/runner/work/tend/tend.
Added plugin `tend-ci-runner` from marketplace `tend`.
Installed plugin root: <CODEX_HOME>/plugins/cache/tend/tend-ci-runner/0.1.0
ROOT OK / SKILL OK / SCRIPTS OK

npm view @openai/codex@0.147.0 engines is { node: '>=16' }, so the job's node-version: "20" is not a constraint on the pin; dist-tags.latest is 0.147.0 and alpha is 0.148.0-alpha.5, so the bump target is what the retargeted rule asks for.

codex plugin marketplace add prints its own Installed marketplace root: line, but it goes to a separate command's stdout and the awk pattern is ^-anchored on Installed plugin root: , so there's no collision with the parse.

Comment thread .github/workflows/ci.yaml Outdated
Comment thread .claude/skills/running-tend/SKILL.md Outdated

@tend-agent tend-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ran the job's script body end-to-end against the pinned 0.147.0 with no OpenAI credential — exit 0, including the new probe block, and test-codex-surface is green on this HEAD in CI. Both probes behave as the comment claims: the negative control returns the unknown configuration field error in ~40 ms, the positive one falls through to Not inside a trusted directory, so no network call and no measurable cost. shellcheck is clean on the extracted step body. Reviewing as the author, so commenting rather than approving.

One thing the narrowing dropped. The "does not cover" list went from four items to two, but only one of the removed pair (model_reasoning_effort) became covered — model selection is still unverified and is now unmentioned in both places. The job asserts that --model <VALUE> exists in codex exec --help; it says nothing about whether the string the action actually passes is still a model. I checked whether that gap could be closed the same way model_reasoning_effort was, and it can't: unlike -c keys, --model gets no local validation at all, so there is no offline probe to add — it can only stay on the list.

That leaves it the least-guarded item of the four, not the most. codex/action.yaml defaults model: gpt-5.5 and that default ships to every adopter, and the repo's own reason for not enumerating codex models (KNOWN_MODELS_BY_HARNESS in generator/src/tend/config.py, "Codex's catalog churns (gpt-5.1-codex was current at harness bring-up; gone by the next month)") is exactly the argument for keeping a weekly reader pointed at it. A weekly run reading either of these two texts concludes the only session-shaped gaps are sandbox behavior and --output-last-message. Inline suggestions restore it in both.

Verification notes

Probes run against 0.147.0, from a mktemp -d (non-repo) cwd, exactly as the job runs them:

$ codex exec --strict-config -c 'model_reasoning_effortZZZ="low"' x </dev/null
Error loading config.toml: unknown configuration field `model_reasoning_effortZZZ` in -c/--config override
real 0m0.037s

$ codex exec --strict-config -c 'model_reasoning_effort="low"' x </dev/null
Not inside a trusted directory and --skip-git-repo-check was not specified.
real 0m0.112s

--strict-config validates field names only — model_reasoning_effort="bogusvalue" is accepted as readily as "low", "medium", "high", "xhigh". So the assertion is scoped correctly to the key, and the error string it emits matches what the check greps for.

--model is not validated locally at any point reachable without a key:

$ codex exec --strict-config --skip-git-repo-check --sandbox danger-full-access --model totally-bogus-model-zzz x
ERROR: unexpected status 401 Unauthorized ... url: https://api.openai.com/v1/responses

Identical output for --model gpt-5.5. The 401 is the first thing that rejects either string, so a dropped model is indistinguishable from a valid one until a real session runs.

ARGS in the action's Run Codex step is --model, --sandbox, --output-last-message, and the conditional --config model_reasoning_effort=... — one -c key, so the single positive probe covers the whole config surface the action passes.

Full smoke script at 0.147.0: codex plugin marketplace add and codex plugin add both succeed, Installed plugin root: parses to a real directory, skills/triage/SKILL.md and an executable scripts/list-recent-runs.sh both land under it.

Comment thread .github/workflows/ci.yaml Outdated
Comment thread .claude/skills/running-tend/SKILL.md Outdated
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.

1 participant