feat: add Codex host support#120
Conversation
Add host-aware skill generation so the shared templates produce Codex-native skill files under .agents alongside the existing Claude output.
Teach the install and development helpers how to wire Codex skill directories, and prefer .agents paths when locating the browse binary.
Extend the skill generation and validation checks so Codex outputs are exercised in tests and CI.
Document the Codex install path, contributor workflow, and repository metadata for the new host.
Co-Authored-By: GPT-5-Codex <noreply@openai.com>
Make ~/.codex/skills the canonical Codex install root, teach browse discovery to prefer it consistently, and update generated docs/tests so the bootstrap path works out of the box.
andrewjordancampbell
left a comment
There was a problem hiding this comment.
Code Review: feat: add Codex host support
Findings
1. pr-body.md committed to repo root
- Severity: Non-blocking
- Location:
pr-body.md(new file, 56 lines) - Description: This file is a copy of the PR description committed as a standalone file in the repo root. PR descriptions are GitHub artifacts preserved in PR history — committing them as files creates maintenance burden and clutters the repo root.
- Suggested Fix: Remove
pr-body.mdfrom the commit. The PR description on GitHub is the canonical source.
2. --host as last CLI argument produces unclear error
- Severity: Non-blocking
- Location:
scripts/gen-skill-docs.ts— HOST argument parsing - Description: If a user runs
bun run gen:skill-docs --host(without a value),process.argv[HOST_ARG_INDEX + 1]evaluates toundefined. The validation catches this, but the error message readsInvalid --host value: undefined— a dedicated check for a missing value would be clearer. - Suggested Fix: Check for
undefinedbefore the general validation and throw--host requires a value: "claude" or "codex".
3. setup auto mode silently no-ops when neither host is detected
- Severity: Non-blocking
- Location:
setup— auto mode case branch - Description: When
--host autois used and neither$HOME/.codexnor$HOME/.claudedirectories exist, the script completes silently without creating any symlinks and without warning the user. - Suggested Fix: Add a fallback warning when no host environment is detected.
4. link_codex_global() doesn't validate SKILL.md presence
- Severity: Non-blocking
- Location:
setup—link_codex_global()function - Description:
link_claude_local()andlink_claude_global()both check[ -f "$skill_dir/SKILL.md" ] || continuebefore creating symlinks, butlink_codex_global()only checks[ -d "$skill_dir" ] || continue. Non-skill directories matching thegstack*glob would be symlinked. - Suggested Fix: Add the same
[ -f "$skill_dir/SKILL.md" ] || continueguard tolink_codex_global().
5. find-browse TypeScript uses existsSync but bash uses -x (executable check)
- Severity: Non-blocking
- Location:
browse/src/find-browse.tsvsbrowse/bin/find-browse - Description: The bash script checks executability (
-x), while the TypeScript implementation only checks existence (existsSync). A non-executable binary file would be returned by TypeScript but skipped by bash. - Suggested Fix: Use
fs.accessSync(candidate, fs.constants.X_OK)in the TypeScript implementation, or document this as intentional.
6. Invocation rewriting transforms content inside code blocks
- Severity: Non-blocking
- Location:
scripts/gen-skill-docs.ts—replaceInvocations() - Description: The regex operates on full template content including fenced code blocks. A literal
/browseinside a code example would be transformed to$gstack-browse. Currently doesn't cause issues in existing templates, but is fragile for future template authors. - Suggested Fix: Consider skipping replacement inside fenced code blocks, or accept as intentional since Codex skills should always show
$gstack-*syntax.
7. Test coverage gap: no explicit test for invocation syntax transformation
- Severity: Non-blocking
- Location:
test/gen-skill-docs.test.ts - Description: Tests verify Codex output doesn't contain
.claude/skillspaths, but there's no explicit test verifying/browseis transformed to$gstack-browse. This is a core feature of the Codex generation pipeline. - Suggested Fix: Add a test verifying
$gstack-*syntax appears and/skillsyntax does not in Codex output.
Summary
Overall assessment: Ready to merge. No blocking issues found.
This is a well-architected PR that adds Codex as a first-class host without duplicating workflow logic. The key design decisions are sound:
- Single template source with host-aware placeholder resolution keeps the two hosts in sync
- Relative symlinks in
.agents/skills/gstack/correctly point back to repo root (3 levels up) - Path precedence (
.codex>.agents>.claude) is consistently implemented across bash and TypeScript - CI validation runs both
--dry-runand--host codex --dry-runto catch staleness - Path isolation is enforced — tests verify no
.claude/skillspaths leak into Codex output replaceInvocations()uses proper regex with word boundaries to avoid false matches- Output directories are created with
mkdirSync({ recursive: true })before writing
The 7 non-blocking findings are minor improvements around edge case handling, test coverage, and a stray pr-body.md file. None affect correctness for the intended use cases.
|
7K+ lines, massive and stale. Closing. |
|
Follow-up hardening is now pushed. The main issue I found was install/discovery mismatch for Codex, not the overall direction. This update makes Re-verified:
|
…iting "landed" (#61) The daemon used to classify a sub-agent result based ONLY on exit code: exit 0 = "landed", non-zero/timed_out = "blocked". But the /land-and-deploy sub-agent (Kimi / Claude / Codex) gracefully declines to merge in real scenarios — failing CI, no PR found, pre-merge gate fails — and still exits 0 (subprocess ran cleanly, verdict lives in prose). Observed in production on 2026-05-19 (mitosis-control-plane release-queue): Queue says GitHub reality PR garrytan#120 landed OPEN, e2e failing PR garrytan#122 landed MERGED ✓ PR garrytan#124 landed MERGED ✓ PR garrytan#129 landed OPEN, e2e failing 50% false-positive rate. The queue records are stuck on "landed" so discoverQueuedRecords (which filters status === "queued") never re-fans out for them — silently abandoned. Fix: after the land sub-agent returns exit 0, ask GitHub for the authoritative PR state. New helper `verifyPrMerged(prNumber, repoIdentity, cwd)` runs `gh pr view <pr> --repo <owner/name> --json state -q .state` and returns { merged: true } only on MERGED. Anything else (OPEN, CLOSED, gh exited non-zero, repoIdentity unparseable) returns { merged: false, reason: "..." } and the daemon writes "blocked" with a useful lastError. The repoIdentity is parsed via a strict ^github.com/owner/repo$ regex so a planted record can't sneak shell-specials through to gh — anything that doesn't match the regex gets rejected before any subprocess runs. Network/auth failures (gh exited non-zero) are reported as not-merged rather than thrown — better to block the record and retry than crash the daemon and leak the lock. The remediation for any blocked record is the existing retryReleaseQueueRecord CLI. Injection seam: opts.verifyMerged?: typeof verifyPrMerged so tests can control the response. Defaults to the real verifyPrMerged. Scope: pre-existing bug, not a regression from PR #57. PR garrytan#120 was marked landed at 12:30Z on 2026-05-19, hours before PR #57's work began. PR #57's deploy verification surfaced it by running through a real PR (garrytan#129) with failing e2e. Tests: 60/60 release-daemon.test.ts (5 new regressions covering the production bug pattern, the happy path, network failures, identity parsing, and the positive parse case). Full orchestrator suite: 1525 pass / 16 fail, all 16 pre-existing on origin/main (confirmed via git stash). Remediation for stranded records on production right now (PR#120, PR#129 on mitosis-control-plane): once this lands, the daemon needs manual revival of those two records. The retryReleaseQueueRecord CLI only handles blocked→queued, not landed→queued. Suggest a one-shot audit script or extending the retry CLI to support landed records whose GitHub state disagrees. Out of scope for this PR. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
.agents/skills/gstack*/SKILL.mdfrom the same.tmpltemplates as Claude~/.codex/skills/and browse resolves there firstAGENTS.md,agents/openai.yaml) and generated support links under.agents/skills/gstackWhat Changed
scripts/gen-skill-docs.ts--host codex.agents/skills/name,description)$gstack-*for Codex{{SKILL_ROOT}},{{LOCAL_SKILL_ROOT}},{{REVIEW_ROOT}})setupnow supports--host claude|codex|autobin/dev-setupandbin/dev-teardowncover the Codex support tree alongside Claude dev mode~/.codex/skills/, backed by the generated.agents/skills/treebrowse/src/find-browse.tsandbrowse/bin/find-browsenow prefer.codex, then.agents, then legacy Claude pathsskill:check, so new skills automatically get Codex coverageAGENTS.mdagents/openai.yamlTesting
Pre-Landing Review
Pre-Landing Review: No issues found.
Notes
.agents/skills/*/SKILL.md..claude/skillspaths.bin,browse,review,setup, etc.) so helper-doc lookups and browse setup work after./setup --host codex..agents/skills/remains the generated source tree in the repo;~/.codex/skills/is the active installed skill location.