Special Topic · Inside OpenAI Codex

Migration only keeps fields that map

Codex migrates from Claude Code and Cursor: detect lists the assets, import only keeps fields that map, plugins install from the user level only

THE QUESTION THIS PAGE ANSWERS

ANSWER FIRST

What is the key idea behind “Migration only keeps fields that map”?

Codex migrates from Claude Code and Cursor: detect lists the assets, import only keeps fields that map, plugins install from the user level only

DECISION RULE

Follow the handoffs, not the demo. A system becomes dependable at the boundaries between model, tools, state, permissions, and people. Read each handoff as a place where you can observe, test, and recover.

TRY NEXT

Name the input, owner, approval, and recovery action for one automated step.

WATCH FOR

A successful run that cannot explain what happened or be safely repeated.

Course goalAfter this you can name three things. Migration sources are only Claude Code and Cursor. Each field takes one of three exits: copy as-is, rewrite, or drop. Plugins install from the user level only; remote marketplaces and sessions wait on the background.
Try it first · Field by field: keep or drop
One set of belongings through the allowlist: as-is, rewrite, cannot move
Source
memory
Session
Click a card to pull it out of the belongings. Changing source, the switch, or session age reruns. Cursor has no memory; repo-scoped plugins are dropped.
Detect Map Rewrite Import
As-is 0 Rewrite 0 Cannot move 0 Background 0
Source fields
Codex landing
Logic trail · each animation step maps to a source span
  1. Pick Cla or Cur by string; anything else falls to Claude Codemigration_source.rs L59
  2. Scan user-level config first; repo scope skips plugin detectdetect/mod.rs L330
  3. Sessions filter at 30 days and 50 items; older ones dropsessions/common.rs L43
  4. memory needs the source to support it, plus its own feature flagdetect/mod.rs L59
  5. hooks keep only sync command; a prompt is skipped wholehooks_cla.rs L135
  6. If an MCP command contains ${, drop the whole servermcp.rs L208
  7. Marketplace sources keep the git family and local dirs; npm is droppedsource_cla.rs L270
  8. Docs are renamed; product names rewrite to Codex on word boundariesrewrite.rs L39
  9. Sync import returns success for Sessions immediatelyservice.rs L437
  10. Background writes the thread later and installs remote plugins later; a closed flag refuses memoryprocessor.rs L184
Hit Play. Watch each field copy as-is, rewrite, or drop.
Three exitsFields that map copy or rewrite. prompt hooks, npm marketplaces, and MCP with placeholders never appear on the success list.
Plugins install from home onlyA repo enable list is not install authority. Remote marketplaces enter a pending-install queue; they do not finish in the sync phase.
Sync first returns a receiptA session can appear on the detect list. Sync import writes nothing; the thread is left to the background.
Teaching sketch:Belongings are course samples; hooks and MCP use only harmless commands. Line numbers on the logic trail map to openai/codex commit 4f39251a01.
Idea 1 · Three exits: as-is, rewrite, drop
What problem it solves

Someone said it would migrate from Claude Code automatically. You thought that meant copying the whole config directory. Next day one hook is missing, one MCP server is missing, and memory is not on the list. The missing hook used type: prompt. The missing MCP command wrote ${API_KEY}.

Swap to another teammate. He uses Cursor. Codex can see his skills and hooks, not his memory. Cursor plugin config in the repo is dropped outright.

What the idea is

The migration crate only reads external config in. How plugins run lives next door in core-plugins. The source type has two variants: Cla is Claude Code, Cur is Cursor. A string that is not cursor falls to Claude Code. If the caller omits the source, detect goes looking in ~/.claude.

Source:codex-rs/external-agent-migration/src/migration_source.rs lines 51–67

One detect run produces at most ten kinds of entries. Each kind’s import must have a place to go. Fields pass an allowlist first. Codex recognizes 11 hook events; Claude Code emits 27. A group whose names do not map disappears whole. A single hook’s type defaults to command; a mismatch is skipped. If an MCP command or url contains ${, drop the whole server. Marketplace sources keep github, git, and local dirs. file, url, npm, settings are dropped outright.

Source:codex-rs/external-agent-migration/src/model.rs lines 53–64 · codex-rs/hooks/src/lib.rs lines 23–35 · restored-src/src/entrypoints/sdk/coreSchemas.ts lines 355–383 · codex-rs/external-agent-migration/src/hooks_cla.rs lines 131–137 · codex-rs/external-agent-migration/src/mcp.rs lines 204–210 · codex-rs/external-agent-migration/src/source_cla.rs lines 270–274

Then the docs are renamed. CLAUDE.md becomes AGENTS.md. Product names rewrite to Codex on word boundaries. Cursor only matches case-sensitive Cursor, so ordinary English cursor is left alone.

Source:codex-rs/external-agent-migration/src/rewrite.rs lines 38–49

Source fields hook / MCP / marketplace Allowlist Name, type, source Copy as-is Sync command hook Rewrite Rename CLAUDE.md, rewrite MCP to TOML Drop prompt hook, npm marketplace, ${} MCP Input is one competitor field. What happens is an allowlist judgment. Output is copy, rewrite, or drop.
Teaching diagram:Migration is not a whole-directory copy. Each field takes its own exit.
Why it lasts

An importer’s usual shape is an allowlist plus three exits. Fields that do not map are dropped — do not rewrite them into a look-alike. A prompt hook is not stuffed into command. MCP with placeholders is not blindly expanded. Rewrite it in another language and this table still works.

Idea 2 · Plugins install from the user level; slow work goes to the background
What problem it solves

Claude Code lets repo settings carry an enabled-plugin list, so a teammate clone gets the same plugins automatically. For Codex, a repo enable list is not install authority. Installing would write user config. An untrusted repo that wants to put executables on your machine has to have that road cut first.

What the idea is

Detect only scans plugins in home. A comment is hardcoded: repo-controlled settings cannot be install authority. The moment Cursor sees a repo root, plugin detect returns empty. Import seeing a nonempty working directory reports repository-scoped plugin migration is not allowed.

Source:codex-rs/external-agent-migration/src/detect/mod.rs lines 330–332 · codex-rs/external-agent-migration/src/migration_source.rs lines 116–125 · codex-rs/external-agent-migration/src/plugins.rs lines 26–36

Local plugins install on the spot. Remote marketplaces enter a pending-install queue. Sessions are two cuts too: they can sit on the detect list, and sync import() returns success for Sessions without writing anything. What actually becomes a Codex thread is a background task in app-server. The caller gets an import_id first.

Source:codex-rs/external-agent-migration/src/service.rs line 437 · codex-rs/app-server/src/external_agent_migration/session_importer.rs lines 100–111

When Codex recognizes a marketplace list, it looks up the first existing file on a fixed set of relative paths. Two of the four paths are its own; two are the competitor’s. The same lookup serves both “user added a marketplace” and “migrate a marketplace from a competitor.”

Source:codex-rs/core-plugins/src/marketplace.rs lines 20–25

Detect home plus repo Sync import Config, hooks, local plugins import_id Hand back a receipt first Background writes the thread Sessions sync is a no-op Background installs remote plugins git clone may take tens of seconds Input is the detect list. What happens: finish what can land on disk now. Output is a receipt plus background work.
Teaching sequence:The sync phase admits “we saw it.” Slow work is left to the background.
Why it lasts

Install authority for executables must sit at the user level. For a product that faces arbitrary git clones, home-only is safer. Sync first returns a receipt and slow work goes to the background — that is the usual shape of a long-job API.

Idea 3 · A nonempty target is not overwritten
What problem it solves

The scariest product switch is overwriting config you already edited. If the target hooks.json already has content, a whole-file replace would wash away handwritten hooks.

What the idea is

If the target hook file is nonempty, the whole item is cancelled. An existing config.toml only fills missing keys. Same-named MCP servers keep the old one. The first migration feels like filling blanks. Click migrate a second time and most entries stay off the list because they already exist.

Source:codex-rs/external-agent-migration/src/hooks_common.rs lines 13–19

memory has another door. The feature flag is off by default; the stage is UnderDevelopment. Detect does not look at this flag; the pre-import processor does. Off returns external agent memory import is disabled. Import copies by bytes and does not redact.

Source:codex-rs/features/src/lib.rs lines 998–1003 · codex-rs/app-server/src/external_agent_migration/processor.rs lines 180–185

Fields that map copy or rewrite. Fields that do not map are dropped.
Why it lasts

Import fills blanks. Files the user already wrote, migration must not touch. memory is off by default because another agent’s project memory would land on disk in the clear.

Side-by-side · The same batch of extensions: should they be read in

DeepSeek Harness: no migration, install again

DSH’s plugin door is a thin pnpm forwarder. It initializes a profile, runs pnpm in the profile directory, then reconciles the list from the install result. Plugins are npm packages. There is no Claude Code detect and no Cursor hook import. Switching products means you reinstall. The cost is a user tax. The gain is not keeping permanent compatibility with a settings schema that keeps growing fields.

Checked apps/cli/src/plugin.ts lines 120–133 · 2026-08-22

Grok: its own marketplace, no competitor migration

Grok copies from the marketplace root plus a relative path into its own install registry, and writes provenance. Relative paths ban .., absolute paths, and drive letters. If there is no plugin.json but a SKILL.md at the root, it synthesizes a list. The marketplace object is its own directory plus a source record. It does not read plugins the user enabled in another home.

Checked crates/codegen/xai-grok-plugin-marketplace/src/installer.rs lines 36–45 · 2026-08-22 · Grok · Marketplace discovery and trust
Classroom Exercise
01

Which fields enter the list

Four Claude Code assets sit on the table: a PreToolUse type: command hook, a same-event type: prompt hook, an MCP whose command contains ${API_KEY}, and a session from 40 days ago. Using the allowlist above, list what should and should not appear on the detect list.

One more: when does the caller get an import_id, and when does this session become a thread. If memory with the flag off is sent to import, which door turns it back.

Takeaway:Migration is detect plus map plus rewrite. Fields that map copy or rewrite; the rest drop. Plugins install from the user level; sessions and remote marketplaces go to the background. A nonempty target is not overwritten.

The handoffs inside “Try it first · Field by field: keep or drop”

“Someone said it would migrate from Claude Code automatically.” shows that an Agent is not defined by the model alone. Each handoff between model, context, tools, state, permissions, and people affects both progress and recovery.

Write the state before adding capability

Starting from “Swap to another teammate.”, split the workflow into starting state, next action, tool result, state update, and stop condition. Debugging then means finding the first lost piece of information or authority instead of saying vaguely that the model “got worse”.

  • Pick Cla or Cur by string; anything else falls to Claude Code migration_source.rs L59
  • Scan user-level config first; repo scope skips plugin detect detect/mod.rs L330
  • Sessions filter at 30 days and 50 items; older ones drop sessions/common.rs L43

A happy path is not reliability

Use “One more: when does the caller get an import_id , and when does this session become a thread.” to replay one successful and one failed run. Record the context, tool result, and owner at each turn; the workflow is maintainable when a second person can follow it without the original builder.

From “Try it first · Field by field: keep or drop” to “Idea 1 · Three exits: as-is, rewrite, drop”

“Try it first · Field by field: keep or drop” grounds the problem in “One set of belongings through the allowlist: as-is, rewrite, cannot move Play Step Reset Source Claude Code Cursor memory Off On Session 10 days 40 days Click a card to pull it out of the belongings. Changing s…”. “Idea 1 · Three exits: as-is, rewrite, drop” then moves it toward “Someone said it would migrate from Claude Code automatically. You thought that meant copying the whole config directory. Next day one hook is missing, one MCP server is missing, and memory is not on the list. T…”. Together, they show that the lesson is not just a conclusion to remember, but a claim with conditions.

Carry the judgment into the next situation

When analyzing an Agent, trace state, action, tool result, and next step in order. Each handoff should explain where information came from, who confirmed it, and where failure stops.

  • “Try it first · Field by field: keep or drop”: One set of belongings through the allowlist: as-is, rewrite, cannot move Play Step Reset Source Claude Code Cursor memory Off On Session 10 days 40 days Click a card to pull it out of the belongings. Changing s…
  • “Idea 1 · Three exits: as-is, rewrite, drop”: Someone said it would migrate from Claude Code automatically. You thought that meant copying the whole config directory. Next day one hook is missing, one MCP server is missing, and memory is not on the list. T…
  • “The closing point”: hooks keep only sync command; a prompt is skipped whole hooks_cla.rs L135

The final “The closing point” brings the discussion to “hooks keep only sync command; a prompt is skipped whole hooks_cla.rs L135”. The useful thing to carry forward is knowing which judgments must be revisited when input, scale, or risk changes.

Mark as learned Your reading progress updates automatically
← PreviousNext →

Keep reading

The next useful article in the thread.

ARTICLE DISCUSSION

Leave one useful thought here.

Keep the idea that clicked, the question that stayed open, or a small note for the next learner.

Discussing Migration only keeps fields that map Inside OpenAI Codex
3discussionsArticle discussion · synced with the Circle
View in the learning circle
AM
Asha MorganContent editor
INSIGHTField note

I turned one judgment from this article into a small experiment I could run today. Knowing what to observe next is more useful than simply remembering the conclusion.

ARTICLE DISCUSSION7 helpful
LH
Lin HarperIndie developer
INSIGHTInsight

After reading this, I first looked for the conditions behind the idea instead of copying the method into a project. That order made the later trade-offs much clearer.

ARTICLE DISCUSSION5 helpful
KM
Kiki MooreProduct operations
QUESTIONQuestion

When this judgment reaches real work, which constraint should be added first? I am curious which step matters most between reading and the first practical attempt.

ARTICLE DISCUSSION4 helpful