diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json
new file mode 100644
index 00000000..4e28729c
--- /dev/null
+++ b/.agents/plugins/marketplace.json
@@ -0,0 +1,20 @@
+{
+  "name": "cortex-codex-plugins",
+  "interface": {
+    "displayName": "Cortex for Codex"
+  },
+  "plugins": [
+    {
+      "name": "hypermnesia-mcp-codex",
+      "source": {
+        "source": "local",
+        "path": "./plugins/hypermnesia-mcp-codex"
+      },
+      "policy": {
+        "installation": "AVAILABLE",
+        "authentication": "ON_INSTALL"
+      },
+      "category": "Developer Tools"
+    }
+  ]
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e0e50afb..aee76b67 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -396,6 +396,11 @@ jobs:
           # the repository's existing release-toolchain pin.
           node-version: "24"
 
+      - name: Install uv (pinned)
+        uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
+        with:
+          version: "0.11.3"
+
       # Vendor parsers catch configuration drift that a generic MCP client
       # cannot: Claude's custom plugin component path, Gemini's extension
       # schema, and Codex's config.toml transport/timeouts. Versions and bytes
@@ -418,6 +423,30 @@ jobs:
           "$cli_bin/codex" mcp list --json \
             -c 'mcp_servers={cortex={command="hypermnesia-mcp",startup_timeout_sec=30,tool_timeout_sec=600,env={CORTEX_MEMORY_STORE_BACKEND="sqlite"}}}' \
             | python -c 'import json,sys; servers={s["name"]:s for s in json.load(sys.stdin)}; c=servers["cortex"]; assert c["transport"]["type"]=="stdio"; assert c["transport"]["command"]=="hypermnesia-mcp"; assert c["transport"]["env"]["CORTEX_MEMORY_STORE_BACKEND"]=="sqlite"; assert c["startup_timeout_sec"]==30.0; assert c["tool_timeout_sec"]==600.0'
+          marketplace_ref="${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}"
+          "$cli_bin/codex" plugin marketplace add cdeust/Cortex \
+            --ref "$marketplace_ref" --json
+          "$cli_bin/codex" plugin list --available --json \
+            | python -c 'import json,sys; d=json.load(sys.stdin); expected=json.load(open("package.json"))["version"]; ps=d.get("available",[])+d.get("installed",[]); p=[x for x in ps if x.get("pluginId")=="hypermnesia-mcp-codex@cortex-codex-plugins"]; assert len(p)==1, p; assert p[0]["version"]==expected'
+          "$cli_bin/codex" plugin add hypermnesia-mcp-codex@cortex-codex-plugins --json
+          "$cli_bin/codex" plugin list --json \
+            | python -c 'import json,sys; d=json.load(sys.stdin); p=[x for x in d.get("installed",[]) if x.get("pluginId")=="hypermnesia-mcp-codex@cortex-codex-plugins"]; assert len(p)==1, p; assert p[0]["installed"] and p[0]["enabled"]'
+          "$cli_bin/codex" mcp list --json \
+            | python -c 'import json,sys; ss=[s for s in json.load(sys.stdin) if s.get("name")=="cortex"]; assert len(ss)==1, ss; s=ss[0]; assert s["startup_timeout_sec"]==180.0, s; assert s["transport"]["command"]=="uvx", s'
+          mapfile -t plugin_command < <(
+            python -c 'import json; s=json.load(open("plugins/hypermnesia-mcp-codex/.mcp.json"))["mcpServers"]["cortex"]; print(s["command"]); print(*s["args"], sep="\n")'
+          )
+          plugin_timeout="$(
+            python -c 'import json; print(json.load(open("plugins/hypermnesia-mcp-codex/.mcp.json"))["mcpServers"]["cortex"]["startup_timeout_sec"])'
+          )"
+          UV_CACHE_DIR="$RUNNER_TEMP/cortex-codex-cold-uv-cache" \
+          UV_TOOL_DIR="$RUNNER_TEMP/cortex-codex-cold-uv-tools" \
+          PYTHONPATH="$GITHUB_WORKSPACE" \
+            python scripts/verify_mcp_hosts.py \
+              --timeout "$plugin_timeout" --clients codex-cli --profiles lean \
+              --command-includes-profile \
+              --allow-bootstrap-network \
+              -- "${plugin_command[@]}"
 
   test-windows:
     name: Test (Windows, SQLite backend)
diff --git a/.mcpbignore b/.mcpbignore
index 70961690..09a59304 100644
--- a/.mcpbignore
+++ b/.mcpbignore
@@ -47,11 +47,14 @@ deps/
 # these files (scripts/, docker/) is already excluded above.
 requirements/
 
-# Claude Code plugin assets (not used by the MCPB / uv server runtime)
+# Host plugin assets (not used by the MCPB / uv server runtime)
 .claude-plugin/
+.agents/
 agents/
 commands/
 skills/
+plugins/hypermnesia-mcp-codex/
+plugins/cortex-deprecated/
 scripts/
 docker/
 Dockerfile
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e84d1ea0..d6b0b02d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,14 @@ adheres to [Semantic Versioning](https://semver.org/).
 
 ### Added
 
+- **Native Codex local plugin packaging.** A dedicated
+  `cortex-codex-plugins` repository marketplace now exposes an isolated,
+  MCP-only Codex package backed by the published PyPI stdio server on the
+  exact 10-tool `lean` profile. Its 180-second startup ceiling is backed by a
+  clean-cache `uvx` lifecycle measured at 110.46 seconds locally (macOS 26.5.1
+  arm64, uv 0.8.19) and 23.87 seconds on `ubuntu-latest` CI. This is additive:
+  Claude Code remains the primary integration and keeps its complete profile,
+  lifecycle hooks, custom agent, marketplace, and installation path unchanged.
 - **Hook-free MCP protocol and host-configuration gates.** CI now starts the
   installed production stdio entry point under representative Claude, Gemini,
   and Codex client identities, completes the MCP lifecycle for both the full
@@ -19,6 +27,8 @@ adheres to [Semantic Versioning](https://semver.org/).
 
 ### Changed
 
+- Benchmark provenance now requires the date, environment, exact command, code
+  revision, and experimental conditions alongside before/after measurements.
 - Claude's wiki-groomer agent now lives in a Claude-specific manifest path,
   preventing Gemini CLI from auto-loading Claude-only agent frontmatter while
   preserving the Claude plugin behavior and tool list.
diff --git a/CLAUDE.md b/CLAUDE.md
index ff0dd661..34d3526d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -85,8 +85,8 @@ separate **cortex-viz** MCP (reads this same store read-only).
   are tracked as a follow-up (found 2026-07-14 during #114), not a
   standard to add to.
 - No invented constants: every hardcoded number carries a `# source:`
-  comment (paper, committed benchmark, or dated measurement). A number
-  without one blocks the diff in review.
+  comment (paper, committed benchmark, or dated measurement naming the
+  environment and conditions). A number without one blocks the diff in review.
 
 ## What NOT to do
 
@@ -103,13 +103,16 @@ separate **cortex-viz** MCP (reads this same store read-only).
 Every change to the retrieval or memory system:
 
 1. **No source, no implementation.** Every algorithm/constant/threshold
-   traces to a published paper, a committed benchmark, or a dated
-   measurement. No source → say "I don't know" and stop.
+   traces to a published paper, a committed benchmark, or a dated measurement
+   that names the environment and experimental conditions. No source → say
+   "I don't know" and stop.
 2. **Verify sources, don't guess.** Read the actual paper; confirm its
    experimental conditions match ours (small corpus, conversational
    content, 384-dim embeddings) before reusing an equation or constant.
-3. **Benchmark before commit.** Re-run the affected benchmarks; no
-   regression accepted. Results must be reproducible on a clean DB.
+3. **Benchmark before commit.** Re-run the affected benchmarks; no regression
+   accepted. Record the before/after values, exact command, code revision,
+   environment, and experimental conditions. Results must be reproducible on
+   a clean DB.
 4. **Audit trail.** Every module docstring cites its paper and equations;
    `docs/provenance/paper-implementation-audit.md` stays current.
 
diff --git a/README.md b/README.md
index 4609cd93..137b0698 100644
--- a/README.md
+++ b/README.md
@@ -159,7 +159,29 @@ Or add it to `~/.gemini/settings.json` directly:
 }
 ```
 
-**OpenAI Codex CLI** — install the executable first so the first MCP handshake is not spent downloading the Python environment, then register it:
+**OpenAI Codex and ChatGPT desktop — native local plugin (recommended).** The
+repository now carries an isolated Codex marketplace and an exact 10-tool
+lean MCP surface. Claude Code remains the primary integration and retains its
+automatic hooks, custom agent, full tool profile, and unchanged marketplace.
+Pre-install the same published package once so the plugin's first `uvx`
+handshake can reuse the local uv cache instead of spending its startup budget
+downloading the Python environment. The bundled server also declares a
+180-second startup ceiling, backed by a 110.46-second clean-cache launch
+measured on 2026-08-02 on local macOS 26.5.1 arm64 with uv 0.8.19 (the clean
+`ubuntu-latest` CI run completed in 23.87 seconds):
+
+```bash
+uv tool install "hypermnesia-mcp[sqlite]"
+codex plugin marketplace add cdeust/Cortex
+codex plugin add hypermnesia-mcp-codex@cortex-codex-plugins
+```
+
+Restart the ChatGPT desktop app and start a new task after installation. See
+[the Codex plugin guide](docs/codex-plugin.md) for the host boundary and the
+public-directory requirements Cortex deliberately does not claim.
+
+**Direct Codex MCP configuration (fallback).** After the same pre-installation,
+register the executable directly:
 
 ```bash
 uv tool install "hypermnesia-mcp[sqlite]"
diff --git a/docs/codex-plugin.md b/docs/codex-plugin.md
new file mode 100644
index 00000000..2f465866
--- /dev/null
+++ b/docs/codex-plugin.md
@@ -0,0 +1,69 @@
+# Cortex plugin for Codex
+
+Cortex ships a native Codex package in an isolated repository subdirectory.
+It points at the same Cortex product as the existing Claude Code plugin, but
+the two packages deliberately offer different host integrations:
+
+- **Claude Code is primary.** Its marketplace package keeps automatic hooks,
+  custom agents, the complete MCP tool profile, and the existing installation
+  flow.
+- **Codex is additive.** Its MCP-only package starts the published PyPI server
+  over local stdio with the exact 10-tool `lean` profile. It installs no
+  lifecycle hooks, skills, apps, or agents and does not alter the Claude
+  package.
+
+The Codex `.mcp.json` lives under `plugins/hypermnesia-mcp-codex/`, never at
+the repository root. This preserves Cortex's Claude contract: Claude Code
+must not discover a second project-scoped MCP server when this repository is
+the active working directory.
+
+## Install from the repository marketplace
+
+Add the Cortex repository marketplace and install the plugin:
+
+```bash
+uv tool install "hypermnesia-mcp[sqlite]"
+codex plugin marketplace add cdeust/Cortex
+codex plugin add hypermnesia-mcp-codex@cortex-codex-plugins
+```
+
+Restart the ChatGPT desktop app and start a new task so Codex loads the new
+plugin components. The plugin uses `uvx`, so `uv` must be available on `PATH`.
+The preliminary `uv tool install` is deliberate: it downloads the published
+package before Codex's startup window, allowing the first plugin handshake to
+reuse uv's local artifact cache.
+
+The bundled server declares `startup_timeout_sec: 180`. This is a bounded
+startup ceiling, not a delay. On 2026-08-02, a local macOS 26.5.1 arm64 run
+with uv 0.8.19 and clean `UV_CACHE_DIR` and `UV_TOOL_DIR` completed
+`initialize`, `tools/list`, and `memory_stats` in 110.46 seconds with exactly
+ten lean tools. A follow-up on the same machine after the tool installation
+completed the same contract in 28.19 seconds. The clean `ubuntu-latest` CI
+runner completed it in 23.87 seconds; CI reads the command and timeout from the
+manifest itself.
+
+The bundled MCP command is equivalent to:
+
+```bash
+uvx --from "hypermnesia-mcp[sqlite]" \
+  hypermnesia-mcp --profile lean
+```
+
+This is a local plugin. It does not make Cortex available to ChatGPT web and
+does not expose the local memory database over the internet.
+
+The repository-marketplace schema requires both `policy.installation` and
+`policy.authentication`. Cortex uses the documented `ON_INSTALL` value. This
+is marketplace timing metadata, not an added authentication mechanism: the
+local stdio server declares no credentials or remote endpoint, and the
+non-interactive install is exercised in CI. See OpenAI's
+[marketplace metadata contract](https://developers.openai.com/plugins/build/plugins#marketplace-metadata).
+
+## Public directory boundary
+
+A future hosted Cortex integration is a separate security and product scope.
+Public submission requires a stable HTTPS MCP Streamable HTTP endpoint,
+authentication and per-user or per-organization isolation, reviewable tool
+metadata, domain verification, operational monitoring, and the applicable
+privacy and legal material. None of those remote-deployment claims are made by
+this local package.
diff --git a/plugins/hypermnesia-mcp-codex/.codex-plugin/plugin.json b/plugins/hypermnesia-mcp-codex/.codex-plugin/plugin.json
new file mode 100644
index 00000000..5cb63ab9
--- /dev/null
+++ b/plugins/hypermnesia-mcp-codex/.codex-plugin/plugin.json
@@ -0,0 +1,40 @@
+{
+  "name": "hypermnesia-mcp-codex",
+  "version": "4.17.2",
+  "description": "Cortex persistent local-first memory for Codex, with Claude Code remaining the primary automatic integration.",
+  "author": {
+    "name": "Clement Deust",
+    "email": "admin@ai-architect.tools",
+    "url": "https://github.com/cdeust"
+  },
+  "homepage": "https://github.com/cdeust/Cortex/blob/main/docs/codex-plugin.md",
+  "repository": "https://github.com/cdeust/Cortex",
+  "license": "MIT",
+  "keywords": [
+    "memory",
+    "mcp",
+    "codex",
+    "claude-code",
+    "local-first"
+  ],
+  "mcpServers": "./.mcp.json",
+  "interface": {
+    "displayName": "Cortex Memory",
+    "shortDescription": "Persistent local memory for Codex",
+    "longDescription": "Use Cortex's focused local-memory tools from Codex. Claude Code remains the primary integration, with automatic lifecycle hooks, custom agents, and the complete tool profile.",
+    "developerName": "Clement Deust",
+    "category": "Developer Tools",
+    "capabilities": [
+      "Persistent project memory",
+      "Decision and correction recall",
+      "Local-first storage",
+      "Memory inspection"
+    ],
+    "websiteURL": "https://github.com/cdeust/Cortex",
+    "defaultPrompt": [
+      "Recall the decisions relevant to this repository.",
+      "Show what Cortex remembers about this project.",
+      "Store the durable lesson from this task."
+    ]
+  }
+}
diff --git a/plugins/hypermnesia-mcp-codex/.mcp.json b/plugins/hypermnesia-mcp-codex/.mcp.json
new file mode 100644
index 00000000..dd9f8bd0
--- /dev/null
+++ b/plugins/hypermnesia-mcp-codex/.mcp.json
@@ -0,0 +1,15 @@
+{
+  "mcpServers": {
+    "cortex": {
+      "command": "uvx",
+      "args": [
+        "--from",
+        "hypermnesia-mcp[sqlite]",
+        "hypermnesia-mcp",
+        "--profile",
+        "lean"
+      ],
+      "startup_timeout_sec": 180
+    }
+  }
+}
diff --git a/scripts/verify_mcp_hosts.py b/scripts/verify_mcp_hosts.py
index b1788d44..4c5d5d5f 100644
--- a/scripts/verify_mcp_hosts.py
+++ b/scripts/verify_mcp_hosts.py
@@ -18,6 +18,7 @@
 import subprocess
 import sys
 import tempfile
+import time
 from typing import Literal
 
 from mcp_server.tool_profiles import LEAN_TOOL_NAMES
@@ -46,6 +47,7 @@ class ContractCase:
     command: tuple[str, ...]
     data_root: Path
     timeout: int
+    socks_proxy_regression: bool
 
     @property
     def label(self) -> str:
@@ -80,20 +82,22 @@ def _frames(client_name: str) -> str:
     )
 
 
-def _environment(data_root: Path) -> dict[str, str]:
+def _environment(data_root: Path, *, socks_proxy_regression: bool) -> dict[str, str]:
     env = os.environ.copy()
     env.update(
         {
             "CORTEX_CLAUDE_DIR": str(data_root),
             "CORTEX_MEMORY_STORE_BACKEND": "sqlite",
             "CORTEX_MEMORY_AP_ENABLED": "0",
-            # Regression environment: FastMCP's banner-time update check used
-            # to import SOCKS support and abort before initialize. The MCP
-            # runtime must not make that non-essential network request.
-            "ALL_PROXY": "socks5://127.0.0.1:9",
-            "all_proxy": "socks5://127.0.0.1:9",
         }
     )
+    if socks_proxy_regression:
+        # Regression environment: FastMCP's banner-time update check used to
+        # import SOCKS support and abort before initialize. The MCP runtime
+        # must not make that non-essential network request. Cold uvx bootstrap
+        # explicitly disables this fixture because it must reach PyPI.
+        env["ALL_PROXY"] = "socks5://127.0.0.1:9"
+        env["all_proxy"] = "socks5://127.0.0.1:9"
     env.pop("FASTMCP_SHOW_SERVER_BANNER", None)
     env.pop("FASTMCP_CHECK_FOR_UPDATES", None)
     env.pop("CORTEX_MCP_PROFILE", None)
@@ -137,7 +141,10 @@ def _run_client(case: ContractCase) -> dict[int, dict[str, object]]:
         input=_frames(case.client_name),
         text=True,
         capture_output=True,
-        env=_environment(case.data_root / case.client_name / case.profile),
+        env=_environment(
+            case.data_root / case.client_name / case.profile,
+            socks_proxy_regression=case.socks_proxy_regression,
+        ),
         timeout=case.timeout,
         check=False,
     )
@@ -223,18 +230,45 @@ def _verify_memory_call(
         raise ContractError(f"{case.label}: memory_stats failed: {call_result!r}")
 
 
-def _verify(case: ContractCase) -> int:
+def _verify(case: ContractCase) -> tuple[int, float]:
+    started_at = time.monotonic()
     responses = _run_client(case)
     _verify_initialize(case, responses)
     count = _verify_tool_surface(case, responses)
     _verify_auxiliary_discovery(case, responses)
     _verify_memory_call(case, responses)
-    return count
+    return count, time.monotonic() - started_at
 
 
 def main() -> int:
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument("--timeout", type=int, default=2 * 60)
+    parser.add_argument(
+        "--clients",
+        nargs="+",
+        choices=CLIENTS,
+        default=CLIENTS,
+        help="client identities to exercise (default: all)",
+    )
+    parser.add_argument(
+        "--profiles",
+        nargs="+",
+        choices=PROFILES,
+        default=PROFILES,
+        help="tool profiles to exercise (default: full lean)",
+    )
+    parser.add_argument(
+        "--command-includes-profile",
+        action="store_true",
+        help=(
+            "run the supplied command unchanged; requires exactly one --profiles value"
+        ),
+    )
+    parser.add_argument(
+        "--allow-bootstrap-network",
+        action="store_true",
+        help="do not inject the SOCKS regression fixture; intended for cold uvx",
+    )
     parser.add_argument(
         "command",
         nargs=argparse.REMAINDER,
@@ -246,21 +280,29 @@ def main() -> int:
         command = command[1:]
     if not command:
         parser.error("server command after -- cannot be empty")
+    if args.command_includes_profile and len(args.profiles) != 1:
+        parser.error("--command-includes-profile requires exactly one --profiles value")
 
     with tempfile.TemporaryDirectory(prefix="cortex_mcp_hosts_") as temp_dir:
-        for client_name in CLIENTS:
-            for profile in PROFILES:
+        for client_name in args.clients:
+            for profile in args.profiles:
+                server_command = (
+                    tuple(command)
+                    if args.command_includes_profile
+                    else (*command, "--profile", profile)
+                )
                 case = ContractCase(
                     client_name=client_name,
                     profile=profile,
-                    command=(*command, "--profile", profile),
+                    command=server_command,
                     data_root=Path(temp_dir),
                     timeout=args.timeout,
+                    socks_proxy_regression=not args.allow_bootstrap_network,
                 )
-                count = _verify(case)
+                count, elapsed_seconds = _verify(case)
                 print(
                     f"PASS {case.label}: initialize + discovery + "
-                    f"memory_stats ({count} tools)"
+                    f"memory_stats ({count} tools, {elapsed_seconds:.2f}s)"
                 )
     return 0
 
diff --git a/tests_py/scripts/test_codex_plugin_contract.py b/tests_py/scripts/test_codex_plugin_contract.py
new file mode 100644
index 00000000..950925e2
--- /dev/null
+++ b/tests_py/scripts/test_codex_plugin_contract.py
@@ -0,0 +1,93 @@
+"""Contract tests for the additive, isolated Codex plugin package."""
+
+from __future__ import annotations
+
+import json
+from pathlib import Path
+
+
+REPO_ROOT = Path(__file__).resolve().parents[2]
+MARKETPLACE_PATH = REPO_ROOT / ".agents/plugins/marketplace.json"
+PLUGIN_ROOT = REPO_ROOT / "plugins/hypermnesia-mcp-codex"
+PLUGIN_PATH = PLUGIN_ROOT / ".codex-plugin/plugin.json"
+MCP_PATH = PLUGIN_ROOT / ".mcp.json"
+
+
+def _json(path: Path) -> dict:
+    return json.loads(path.read_text())
+
+
+def test_codex_plugin_is_confined_to_a_dedicated_subdirectory() -> None:
+    """Never reintroduce the project-scoped MCP collision in Claude Code."""
+    assert not (REPO_ROOT / ".mcp.json").exists()
+    assert not (REPO_ROOT / ".codex-plugin").exists()
+    assert PLUGIN_PATH.is_file()
+    assert MCP_PATH.is_file()
+
+    ignored = (REPO_ROOT / ".mcpbignore").read_text().splitlines()
+    assert ".agents/" in ignored
+    assert "plugins/hypermnesia-mcp-codex/" in ignored
+    assert "plugins/cortex-deprecated/" in ignored
+
+
+def test_codex_marketplace_resolves_only_the_dedicated_plugin() -> None:
+    marketplace = _json(MARKETPLACE_PATH)
+    assert marketplace["name"] == "cortex-codex-plugins"
+    assert len(marketplace["plugins"]) == 1
+
+    entry = marketplace["plugins"][0]
+    assert entry == {
+        "name": "hypermnesia-mcp-codex",
+        "source": {
+            "source": "local",
+            "path": "./plugins/hypermnesia-mcp-codex",
+        },
+        "policy": {
+            "installation": "AVAILABLE",
+            "authentication": "ON_INSTALL",
+        },
+        "category": "Developer Tools",
+    }
+    source = (REPO_ROOT / entry["source"]["path"]).resolve()
+    assert source == PLUGIN_ROOT.resolve()
+    assert source.is_relative_to(REPO_ROOT.resolve())
+
+
+def test_codex_plugin_is_mcp_only_and_uses_the_exact_lean_profile() -> None:
+    plugin = _json(PLUGIN_PATH)
+    server = _json(MCP_PATH)["mcpServers"]["cortex"]
+
+    assert plugin["name"] == "hypermnesia-mcp-codex"
+    assert plugin["mcpServers"] == "./.mcp.json"
+    for unsupported in ("hooks", "skills", "apps", "agents", "postInstall"):
+        assert unsupported not in plugin
+
+    assert server == {
+        "command": "uvx",
+        "args": [
+            "--from",
+            "hypermnesia-mcp[sqlite]",
+            "hypermnesia-mcp",
+            "--profile",
+            "lean",
+        ],
+        # Measured clean-cache startup on 2026-08-02: 110.46s on macOS 26.5.1
+        # arm64 with uv 0.8.19. This bounded ceiling leaves startup headroom
+        # without inventing a sleep or retry.
+        "startup_timeout_sec": 180,
+    }
+
+
+def test_codex_package_does_not_weaken_the_primary_claude_plugin() -> None:
+    claude = _json(REPO_ROOT / ".claude-plugin/plugin.json")
+    claude_server = claude["mcpServers"]["cortex"]
+
+    assert claude["name"] == "hypermnesia-mcp"
+    assert claude["hooks"]
+    assert claude["agents"] == ["./claude-agents/cortex-wiki-groomer.md"]
+    assert claude_server["command"] == "python3"
+    assert claude_server["args"] == [
+        "${CLAUDE_PLUGIN_ROOT}/scripts/launcher.py",
+        "mcp_server",
+    ]
+    assert "--profile" not in claude_server["args"]
diff --git a/tests_py/scripts/test_cross_host_manifests.py b/tests_py/scripts/test_cross_host_manifests.py
index f3c43767..fab94bd8 100644
--- a/tests_py/scripts/test_cross_host_manifests.py
+++ b/tests_py/scripts/test_cross_host_manifests.py
@@ -27,3 +27,7 @@ def test_cross_host_manifest_versions_match_the_release() -> None:
     assert _json("server.json")["version"] == expected
     assert _json("manifest.json")["version"] == expected
     assert _json(".claude-plugin/plugin.json")["version"] == expected
+    assert (
+        _json("plugins/hypermnesia-mcp-codex/.codex-plugin/plugin.json")["version"]
+        == expected
+    )
