← Files Compound EngineeringARCHIVED FILE

docs/solutions/developer-experience/codex-local-skill-development-workflow.md

4.66 KB · Oct 2, 2026 · 00:33 UTC

↓ Download file

---
title: "Codex local skill development from any worktree"
date: 2026-07-16
last_updated: 2026-09-02
category: developer-experience
module: codex-local-development
problem_type: developer_experience
component: development_workflow
severity: medium
root_cause: missing_tooling
resolution_type: tooling_addition
related_components:
  - tooling
  - documentation
applies_when:
  - "Testing Compound Engineering skill changes from any checkout or linked worktree in Codex"
  - "Switching between unreleased local skill content and the official marketplace-backed plugin"
  - "Verifying which worktree supplies the active Compound Engineering skills"
tags:
  - codex
  - local-development
  - worktrees
  - skill-symlink
  - plugin-cache
  - installation-modes
  - config-reconciliation
---

# Codex local skill development from any worktree

The workflow itself (`bun run codex:dev -- local|status|refresh|remote|remove`) is documented in AGENTS.md "Codex Local Plugin Development" and implemented in `src/dev/codex-dev.ts`. This doc keeps the two empirical Codex facts that shaped its design and that neither the official docs nor the code make obvious.

## Why a symlink and not a local marketplace

Treating the checkout as a marketplace (`codex plugin marketplace add "$PWD"` then `codex plugin add compound-engineering@...`) installs a *cached copy*. Later edits are not live, and the manifest stays at its release-owned version while branch contents change, so version equality cannot tell you which files were loaded. A `$CODEX_HOME/skills/compound-engineering-local -> <worktree>/skills` symlink exercises the exact branch, including uncommitted and untracked skill directories. Cursor does not need this: `cursor-agent --plugin-dir "$PWD"` accepts the checkout directly.

## Codex fact 1: marketplace `source.path` resolves from the marketplace root, which for a personal marketplace is `$HOME`

The Codex plugin docs define a local marketplace `source.path` relative to that marketplace's root. A personal marketplace lives at `$HOME/.agents/plugins/marketplace.json`, so a dot-relative checkout path starts from `$HOME`, not from the directory containing `marketplace.json`. Getting this wrong points the plugin at a path that does not exist. (Codex docs: Build skills / Build plugins on learn.chatgpt.com.)

Version-specific observations on Codex CLI 0.144.5, not documented guarantees: plugin installs are copied into a cache; re-running `codex plugin add` refreshes that cache even when the manifest version is unchanged, so remove/re-add is not required; direct skill sources are live and edits are detected without restart; nested symlink collections under `$CODEX_HOME/skills` are discovered. Start a new session only when switching between local, remote, and absent modes.

## Codex fact 2: an enabled plugin can be in `config.toml` yet absent from `codex plugin list`

In the observed state, the official plugin remained enabled in the active profile's `config.toml` while `codex plugin list --available --json` omitted it. Inventory is lossy. That is why the workflow parses the resolved `$CODEX_HOME/config.toml` independently and treats the union of the two sources as the truth: a valid link plus either an inventoried plugin or a hidden enabled entry is `mixed`; a hidden entry without the local link is `drifted`, not `absent`. `local` and `remove` remove the official plugin ID when config shows it enabled even if inventory omitted it. Trusting inventory alone would leave both surfaces active and duplicate skill names loaded from a live directory and a cached plugin simultaneously.

`CODEX_HOME` is part of the target: the workflow uses the inherited value (fallback `$HOME/.codex`), and each Orca account had its own Codex home, so the relevant `config.toml` was under that profile rather than `~/.codex/config.toml`. Launch Codex with the same `CODEX_HOME` used for the switch.

## Deliberate limits

Local mode is skill-only. Validation refuses manifests that add apps, hooks, or MCP servers (or a default hook manifest) because a skills symlink cannot reproduce them; extend the workflow or test through a full plugin install at that point. The workflow manages only the exact named symlink and never overwrites a regular file, unrelated symlink, or broken symlink at that path (precedent: [preserve user content across destructive paths](../best-practices/preserve-user-content-across-all-destructive-paths.md), issue #1048).

## Related

- [Native plugin install strategy](../integrations/native-plugin-install-strategy.md) for the broader platform installation model and how installed Codex skills are invoked.
- [Preserve user content across destructive paths](../best-practices/preserve-user-content-across-all-destructive-paths.md)

SHA-256: 4826980afe993b5b6fbd8547d49a0f750ea21d721d6a93155c3384b71d62c1ea