Skip to content

fix(claude-plugin): install both marketplaces from one committed host runtime - #1037

Open
Lykhoyda wants to merge 4 commits into
mainfrom
fm/rn-dev-agent-issue-892-one-committed-hos-28
Open

Lykhoyda wants to merge 4 commits into
mainfrom
fm/rn-dev-agent-issue-892-one-committed-hos-28

Conversation

@Lykhoyda

Copy link
Copy Markdown
Owner

Intent

Captain (2026-09-14): "Pickup the next issue: #892" - "cleanup: one committed host dist + thin plugin manifests instead of two 10 MB copies". In the issue's words: both plugin trees each commit a full ~10 MB rn-dev-agent-core/dist copy (claude + codex), regenerated on every core PR; the committed bundle is load-bearing (marketplace installs run it, verified in PR 871 QA) so deletion is not an option, but two identical copies double every diff, review surface and drift risk. Proposed: commit one canonical host dist and make each plugin dir a thin manifest that references it (or copy it at pack/publish time); keep the dist-freshness CI check pointed at the single copy. Acceptance: one committed dist; both plugins install and run from it; check-dist-fresh covers it; the repo sheds ~10 MB of duplicated artifact per revision. The architect's accepted design: GO for one physically contained common root - packages/claude-plugin stays the distribution directory for BOTH marketplaces (Claude and Codex installers copy a real directory and skip symlinks, so links are out), carrying .claude-plugin/plugin.json and a generated .codex-plugin/plugin.json plus codex.mcp.json, bin/, codex-skills/, codex-commands/, codex-agents/, codex-templates/, one committed rn-dev-agent-core/dist with neutral rn-dev-agent-core package metadata; packages/codex-plugin becomes authoring material, not an installation directory; no npm publication, no distribution branch, no runtime downloads.

What Changed

  • packages/claude-plugin becomes the single directory both marketplaces install: it now also carries the generated .codex-plugin/plugin.json, codex.mcp.json, bin/cdp-supervisor.js + bin/plugin-health.js launchers and the codex-skills/, codex-commands/, codex-agents/, codex-templates/ adapters, and .agents/plugins/marketplace.json points the Codex entry at it instead of packages/codex-plugin.
  • Drops the duplicated packages/codex-plugin runtime — its rn-dev-agent-core/dist copy, generated scripts/ (including the imported rn-fast-runner/rn-android-runner trees kept under packages/claude-plugin/scripts), runner-manifest.json and CLAUDE-MD-TEMPLATE.md — leaving that package as authoring material only; the committed core package metadata is host-neutral rn-dev-agent-core, and product-version.ts drops the per-host *-claude-runtime/*-codex-runtime package names and their manifest-ordering branches.
  • Retargets the generator and gates at the one copy: build-host-runtimes.ts emits the single package, check-dist-fresh.sh scopes porcelain to every generated path and fails if any second packages/*/rn-dev-agent-core/dist is tracked or the dist is a symlink, release.yml generates/validates one runner-manifest.json copy via --plugin-manifest, and .gitattributes/.gitignore/sync + check scripts, tests, READMEs and docs-site pages follow the new layout.

Risk Assessment

⚠️ Medium: The source-verifiable acceptance criteria are all met and densely guarded (one committed dist, both marketplaces resolving packages/claude-plugin, check-dist-fresh/check-core-dist-contract/check-agent-package-sync all rescoped to the single copy, release trust-root staging and version sync consistent), and the fix round's only behavioral edit is provably behavior-preserving — but the change reshapes the installed layout for both hosts, and the "both plugins install and run from it" criterion plus the two host-discovery boundaries (Codex seeing hooks/, Claude seeing codex-skills/) are only closable by a live install on each host.

Testing

I validated GH-892 by simulating both marketplace installs from the exact committed bytes of packages/claude-plugin and driving a real MCP session against each: the Claude manifest's supervisor.js path and the Codex codex.mcp.json launcher (discovering its root via CODEX_HOME) both start rn-dev-agent-cdp-bridge 1.0.8, expose the same 81 tools, and answer a live cdp_status call with the neutral package's product version, with the Codex health CLI and the bundled learned-actions CLI also running from those roots. Alongside that I confirmed only one host dist is tracked and codex-plugin holds no runtime artifacts, measured the 10.9 MB tracked-size drop, ran the full real check-dist-fresh gate on the repo (clean rebuild matched the committed bundle byte-for-byte, worktree stayed clean), ran the four packaging guard regression suites (60 cases, including the new "second tracked host dist fails" case), and ran the 163 core unit tests touched by the change — everything passed with no failures, flakes, or setup problems. There is no UI surface in this change, so no screenshots or rendered artifacts apply; the end-user surfaces are MCP tool responses and CLI output, which I captured as transcripts in the evidence directory. Temporary install roots were removed; the only rebuilt output is the gitignored packages/rn-dev-agent-core/dist that the gate regenerates by design.

Evidence: Both hosts install and run from the single committed dist (MCP initialize, tools/list, live cdp_status, health CLI)
GH-892 — one committed host dist + thin plugin manifests
base   d76a9e2a  ->  target e587136a

##############################################################
# 1. ONE committed dist (acceptance: 'one committed dist')
##############################################################
$ git ls-tree -r --name-only <commit> | grep 'rn-dev-agent-core/dist/' | (dir rollup)
-- base d76a9e2a --
  20 packages/claude-plugin/rn-dev-agent-core/dist
  20 packages/codex-plugin/rn-dev-agent-core/dist
-- target e587136a --
  20 packages/claude-plugin/rn-dev-agent-core/dist

packages/codex-plugin is now authoring material only:
  rn-dev-agent-core/dist       absent
  codex-skills                 absent
  codex.mcp.json               absent
  bin/plugin-health.js         absent
  scripts                      absent

##############################################################
# 2. Repo sheds the duplicated artifact (acceptance: ~10 MB)
##############################################################
d76a9e2a   total tracked bytes: 49996322 (47.7 MB)
e587136a   total tracked bytes: 38517938 (36.7 MB)
                          delta: -11478384 bytes (-10.9 MB)

##############################################################
# 3. BOTH hosts install and run from that ONE dist
##############################################################
Install staging (exact committed bytes, no working-tree state):
  $ git archive HEAD packages/claude-plugin | tar -x -C $WORK/committed
  $ cp -R $SRC $WORK/claude-home/plugins/marketplaces/rn-dev-agent/rn-dev-agent   # Claude installer
  $ cp -R $SRC $WORK/codex-home/plugins/cache/rn-dev-agent-marketplace/rn-dev-agent/1.0.8  # Codex installer
  (real directory copies; `find $WORK -type l` returned nothing => no symlinks)

--- Claude Code host ---
manifest mcpServers.cdp.args  : ${CLAUDE_PLUGIN_ROOT}/rn-dev-agent-core/dist/supervisor.js
resolved command              : node /tmp/gh892-install.4bGIpw/claude-home/plugins/marketplaces/rn-dev-agent/rn-dev-agent/rn-dev-agent-core/dist/supervisor.js
{
  "host": "claude-code",
  "launchedFrom": "/tmp/gh892-install.4bGIpw/claude-home/plugins/marketplaces/rn-dev-agent/rn-dev-agent/rn-dev-agent-core/dist/supervisor.js",
  "serverInfo": {
    "name": "rn-dev-agent-cdp-bridge",
    "version": "1.0.8"
  },
  "protocolVersion": "2024-11-05",
  "toolCount": 81,
  "sampleTools": [
    "cdp_auto_login",
    "cdp_component_state",
    "cdp_component_tree",
    "cdp_connect",
    "cdp_console_log",
    "cdp_cpu_profile",
    "cdp_dev_settings",
    "cdp_diagnostic_renderers"
  ],
  "hasCdpStatus": true,
  "hasDeviceScreenshot": true
}

--- Codex host ---
manifest mcpServers pointer   : ./codex.mcp.json
manifest skills pointer       : ./codex-skills/
launcher discovery env        : CODEX_HOME=/tmp/gh892-install.4bGIpw/codex-home (RN_DEV_AGENT_CODEX_PLUGIN_ROOT unset)
{
  "host": "codex",
  "launchedFrom": "const f=require('fs'),p=require('path'),o=require('os'),c=require('child_process');const V='1.0.8';const D=x=>{try{return f.readdirSync(x).map(n=>p.join(x,n)).filter(z=>{try{return f.statSync(z).isDirectory()}catch{return false}})}catch{return[]}};const C=process.env.CODEX_HOME||p.join(o.homedir(),'.codex');const R=[process.env.RN_DEV_AGENT_CODEX_PLUGIN_ROOT,...D(p.join(C,'plugins','cache')).map(m=>p.join(m,'rn-dev-agent',V))].filter(Boolean);let L=null;for(const r of R){const x=p.join(r,'bin','cdp-supervisor.js');if(f.existsSync(x)){L=x;break}}if(!L){process.stderr.write('rn-dev-agent: Codex launcher not found for rn-dev-agent '+V+'\\n');process.exit(1)}const ch=c.spawn(process.execPath,[L,...process.argv.slice(1)],{stdio:'inherit',cwd:process.cwd(),env:process.env});ch.on('error',e=>{process.stderr.write('rn-dev-agent: failed to spawn Codex launcher '+L+': '+e.message+'\\n');process.exit(1)});for(const s of ['SIGTERM','SIGINT','SIGHUP'])process.on(s,()=>{try{ch.kill(s)}catch{}});ch.on('exit',(code,sig)=>{if(sig){process.removeAllListeners(sig);try{process.kill(process.pid,sig)}catch{process.exit(1)}}else process.exit(code??0)})",
  "serverInfo": {
    "name": "rn-dev-agent-cdp-bridge",
    "version": "1.0.8"
  },
  "protocolVersion": "2024-11-05",
  "toolCount": 81,
  "sampleTools": [
    "cdp_auto_login",
    "cdp_component_state",
    "cdp_component_tree",
    "cdp_connect",
    "cdp_console_log",
    "cdp_cpu_profile",
    "cdp_dev_settings",
    "cdp_diagnostic_renderers"
  ],
  "hasCdpStatus": true,
  "hasDeviceScreenshot": true
}


##############################################################
# 4. Live MCP tool call through each installed host
##############################################################

===== claude-code (installed root): cdp_status tool result =====
{"ok":true,"data":{"product":{"coreVersion":"1.0.8"},"authoritative":false,"authority":{"available":false,"code":"NON_GIT_MANIFEST_REQUIRED","nextAction":"Declare the non-Git source explicitly: set RN_DEV_AGENT_DECLARED_ROOT to the exact existing application root, and set RN_DEV_AGENT_DECLARED_MANIFESTS to a comma-separated list of required existing manifest files inside that root, then restart the supervisor. Neither value is inferred from the working directory or generated."},"metro":{"port":8081,"requestedPort":null,"connected":false},"cdp":{"connected":false,"target":null,"requestedPlatform":null},"nextAction":"Use rn_session bind_metro and cdp_connect with the claimed exact port."},"meta":{"authoritative":false}}

===== codex (installed root): cdp_status tool result =====
{"ok":true,"data":{"product":{"coreVersion":"1.0.8"},"authoritative":false,"authority":{"available":false,"code":"NON_GIT_MANIFEST_REQUIRED","nextAction":"Declare the non-Git source explicitly: set RN_DEV_AGENT_DECLARED_ROOT to the exact existing application root, and set RN_DEV_AGENT_DECLARED_MANIFESTS to a comma-separated list of required existing manifest files inside that root, then restart the supervisor. Neither value is inferred from the working directory or generated."},"metro":{"port":8081,"requestedPort":null,"connected":false},"cdp":{"connected":false,"target":null,"requestedPlatform":null},"nextAction":"Use rn_session bind_metro and cdp_connect with the claimed exact port."},"meta":{"authoritative":false}}

===== Codex skill surface inside the SAME installed directory =====
codex-skills/ : build-and-test, capturing-proof, check-env, check-vercel-rules, creating-actions, debug-screen, doctor, list-learned-actions, lock-e2e, nav-graph, observe, proof-capture, qa-pr, rn-best-practices, rn-debugging, rn-device-control, rn-feature-dev, rn-feature-development, rn-setup, rn-testing, rn-workflow, run-action, run-workflow, send-feedback, sending-feedback, setup, test-feature, using-rn-dev-agent

===== Claude skill surface inside the SAME installed directory =====
skills/       : capturing-proof, creating-actions, rn-best-practices, rn-debugging, rn-device-control, rn-feature-development, rn-setup, rn-testing, rn-workflow, sending-feedback, using-rn-dev-agent

##############################################################
# 5. Codex health CLI + bundled runtime CLI, from the install roots
##############################################################
$ cd <codex install root> && node bin/plugin-health.js
rn-dev-agent Codex health: INDETERMINATE_TASK_STATE
- INDETERMINATE_TASK_STATE: Disk, registration, and contract are healthy; one or more active-task observations are unknown.
Next actions:
- Run `/mcp verbose`, inspect the skill inventory, and rerun with complete observation flags.

Paths named by the corrected 'using-rn-dev-agent' Codex bullet, resolved in the install root:
  .codex-plugin/plugin.json  OK (1 entries)
  codex-skills               OK (28 entries)
  codex.mcp.json             OK (1 entries)
  codex-commands             OK (17 entries)
  codex-agents               OK (6 entries)

$ node <root>/rn-dev-agent-core/dist/learned-actions.js --json   (same bundled runtime, both roots)
-- Claude root --
{
  "cwd": "~/.no-mistakes/worktrees/3df3fd656078/01M2FS1T43RD2D1K0J5HW98BP0",
  "memoryCwd": "~/.no-mistakes/worktrees/3df3fd656078/01M2FS1T43RD2D1K0J5HW98BP0",
  "filter": null,
  "sections": {
    "memories": {
-- Codex root  --
{
  "cwd": "~/.no-mistakes/worktrees/3df3fd656078/01M2FS1T43RD2D1K0J5HW98BP0",
  "memoryCwd": "~/.no-mistakes/worktrees/3df3fd656078/01M2FS1T43RD2D1K0J5HW98BP0",
  "filter": null,
  "sections": {
    "memories": {
Evidence: CI guard coverage for the single dist (check-dist-fresh regression suite + real gate + package sync)
GH-892 — CI guard coverage for the single committed dist

$ bash scripts/test/check-dist-fresh.test.sh
ok: fresh distributed outputs pass without committed core dist
ok: large package manifest containing supervisor passes
ok: large package manifest missing supervisor fails
ok: tracked core dist fails
ok: second tracked host dist fails
ok: stale host runtime fails
ok: committed host orphan fails
ok: emitted-but-uncommitted host file fails
ok: gitignored extra core dist file does not fail
ok: web build failure fails the gate
ok: missing supervisor.js fails
ok: stale packaged runtime fails
ok: stale packaged runner manifest fails
ok: stale packaged native runner copy fails
ok: stale generated Codex adapter fails
ok: missing generated Codex adapter fails
ok: symlinked packaged runtime dist fails

$ bash scripts/check-agent-package-sync.sh   (real repo, real consumer)
check-agent-package-sync: package split, app deliverables, and host artifacts are in sync.

$ bash scripts/test/check-agent-package-sync.test.sh
ok: valid package split passes
ok: native runner local build outputs are ignored
ok: committed Xcode shared scheme drift fails
ok: missing apps workspace fails
ok: legacy core workspace fails
ok: old core package name fails
ok: legacy Codex MCP path fails
ok: Codex plugin version drift fails
ok: Codex manifest without inline empty hooks fails (would discover Claude hooks/)
ok: Codex manifest selecting Claude skills/ fails
ok: distributed Codex manifest drifting from its authoring source fails
ok: Claude manifest without cleared Codex root hints fails
ok: host-specific packaged runtime name fails
ok: second host runtime under packages/codex-plugin fails
ok: root .mcp.json in the distributed package fails (Claude would auto-discover it)
ok: missing generated codex-commands fails
ok: generated workflow skill checked into the authoring tree fails
ok: root npm lock fails
ok: docs app npm lock fails
ok: missing shared ignore fails
ok: ignored Claude release package fails
ok: missing Codex skills adapter fails
ok: legacy root skills shim fails
ok: legacy root iOS runner project fails
ok: legacy root Android runner project fails
ok: symlinked Codex skills adapter fails
ok: Codex launcher uses packaged runtime and preserves app cwd/args
ok: Codex launcher ignores unrelated global core cache
ok: Codex MCP bootstrap chooses only the pinned plugin version and preserves app cwd/args
ALL PASS

$ bash scripts/test/check-core-dist-contract.test.sh
ok: gitignored core dist plus one tracked host passes
ok: second tracked host dist fails
ok: tracked core dist fails
ok: missing gitignore and host tracking fails

$ bash scripts/test/check-typescript-only.test.sh
ok: baseline + exclusions pass
ok: new unlisted js rejected and named
ok: baseline shrink (migration) passes without editing baseline
ok: missing baseline errors

$ bash scripts/test/check-web-bundle.test.sh
ok: matching host SPA copies pass
ok: default host bundle list is the single distributed package
ok: stale host SPA copy fails
ok: missing host SPA copy fails
ok: missing generated SPA fails
ok: web build failure fails the gate


$ bash scripts/check-dist-fresh.sh   (REAL repo: wipes core dist, rebuilds tsc+SPA+native, regenerates all host outputs, diffs porcelain)
dist fresh
Evidence: Live MCP handshake through both installed hosts
--- Claude Code host ---
manifest mcpServers.cdp.args : ${CLAUDE_PLUGIN_ROOT}/rn-dev-agent-core/dist/supervisor.js
{
"host": "claude-code",
"serverInfo": { "name": "rn-dev-agent-cdp-bridge", "version": "1.0.8" },
"toolCount": 81, "hasCdpStatus": true, "hasDeviceScreenshot": true
}

--- Codex host ---
manifest mcpServers pointer : ./codex.mcp.json
manifest skills pointer : ./codex-skills/
launcher discovery env : CODEX_HOME=<fake> (RN_DEV_AGENT_CODEX_PLUGIN_ROOT unset)
{
"host": "codex",
"serverInfo": { "name": "rn-dev-agent-cdp-bridge", "version": "1.0.8" },
"toolCount": 81, "hasCdpStatus": true, "hasDeviceScreenshot": true
}

===== cdp_status tool result (identical from both installed roots) =====
{"ok":true,"data":{"product":{"coreVersion":"1.0.8"}, ...}}
Evidence: Single committed dist + size shed + real dist-fresh gate
$ git ls-tree -r --name-only <commit> | grep rn-dev-agent-core/dist/
-- base d76a9e2a --
20 packages/claude-plugin/rn-dev-agent-core/dist
20 packages/codex-plugin/rn-dev-agent-core/dist
-- target e587136a --
20 packages/claude-plugin/rn-dev-agent-core/dist

tracked bytes d76a9e2a: 49996322 (47.7 MB)
tracked bytes e587136a: 38517938 (36.7 MB) delta -10.9 MB

$ bash scripts/test/check-dist-fresh.test.sh
ok: second tracked host dist fails <-- new single-dist rule
ok: symlinked packaged runtime dist fails
... 17/17 ok

$ bash scripts/check-dist-fresh.sh # real repo, clean rebuild
dist fresh
$ git status --porcelain # empty

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 infos
  • ⚠️ packages/shared-agent-knowledge/skills/using-rn-dev-agent/SKILL.md:28 - The shipped using-rn-dev-agent skill still documents the pre-change Codex install surface: "Codex local development points at /path/to/rn-dev-agent/packages/codex-plugin. Codex loads .codex-plugin/plugin.json, package-local shared skills in skills/, and the same cdp MCP server from .mcp.json." All three facts are now false. Concrete failure: a Claude session loads this skill (it is listed in packages/claude-plugin/plugin.json and is the designated "front door" routing skill), and instructs the user to register packages/codex-plugin with Codex. That directory no longer contains rn-dev-agent-core/dist/, codex-skills/, codex.mcp.json, or a bin/plugin-health.js (all removed in this branch; scripts/check-agent-package-sync.sh:326-331 now asserts they must NOT exist), so the registration yields a plugin with no MCP runtime and no discoverable skills. The identical sentence was correctly rewritten in the Codex adaptation (packages/codex-plugin/skills/using-rn-dev-agent/SKILL.md:20-21), README.md:269, apps/docs-site/.../getting-started.mdx:78, and troubleshooting.mdx:92 — only the canonical shared copy and its Claude byte-mirror (packages/claude-plugin/skills/using-rn-dev-agent/SKILL.md:28, byte-identical, enforced by check-agent-package-sync.sh) were missed. Fix both files together so the sync guard stays green: point Codex local development at /path/to/rn-dev-agent/packages/claude-plugin, and name codex-skills/ and codex.mcp.json.
  • ℹ️ packages/codex-plugin/.codex-plugin/plugin.json:25 - New in this change: &#34;hooks&#34;: {&#34;hooks&#34;: {}} is the sole mechanism keeping Codex from discovering packages/claude-plugin/hooks/hooks.json, which now ships inside the very directory Codex installs (it declares SessionStart/PostToolUse/CwdChanged command hooks). Before this change the Codex install root had no hooks/ directory at all, so this exposure is newly created. The assertion that the inline empty object suppresses discovery is only checked by re-reading the manifest (gh-575-codex-workflows.test.ts:52-56, check-agent-package-sync.sh:453-456), never by a real Codex load. Blast radius is bounded — every hook command is ${CLAUDE_PLUGIN_ROOT}/hooks/..., which Codex does not set, so a discovered hook would resolve to an absolute /hooks/... and fail rather than execute plugin code — but it would surface as per-session hook errors in Codex. Worth one live codex plugin add + detail-screen check ("No plugin hooks") before release; no source change implied.
  • ℹ️ packages/rn-dev-agent-core/src/session/product-version.ts:12 - Renaming the packaged runtime to the neutral rn-dev-agent-core (build-host-runtimes.ts:244) leaves EXECUTING_CORE_PACKAGE_NAMES entries rn-dev-agent-core-claude-runtime / rn-dev-agent-core-codex-runtime and their four dedicated branches in launchingHostManifestCandidates() (lines 75-81) and pluginManifestCandidates() (lines 94-99) unreachable in any shipped artifact: git ls-files now tracks exactly one rn-dev-agent-core/dist, and its sibling package.json carries the neutral name. These names cannot reappear from an older install either, since such an install runs its own bundled copy of this file. The branches survive only because gh-1025-status-product-version.test.ts still builds fixtures with the legacy names. Smallest honest remedy is removing the two legacy names and collapsing the three-way branch to the neutral ordering, dropping the corresponding fixtures. Kept as info because the dead code is inert and removal is optional cleanup, not a correctness fix. (The analogous codex-launcher branch in proof-capture.ts:452-465 is NOT dead by the same argument — it resolves against an arbitrary candidate checkout, which may legitimately be an older SHA with the old layout, exactly as the reworked fixture in gh-588-proof-dual-authority.test.ts:79-91 now documents.)
  • ℹ️ packages/rn-dev-agent-core/test/unit/gh-590-codex-feedback.test.ts:109 - After the path rewrite, codexCollector and claudeCollector both read packages/claude-plugin/scripts/collect-feedback.sh, so assert.equal(codexCollector, canonicalCollector) and assert.equal(claudeCollector, canonicalCollector) (lines 115-116) are the same assertion made twice against the same bytes. Harmless, but it now reads as if two independent packaged copies were being verified when only one exists. Collapse to a single read/assertion.

🔧 Fix: drop dead host-runtime branches, fix stale Codex install docs
2 infos still open:

  • ℹ️ packages/claude-plugin/.claude-plugin/plugin.json:22 - New in this change: 28 Codex skill directories (packages/claude-plugin/codex-skills/*/SKILL.md) now ship inside the very directory Claude Code installs. Before this change the Claude install root contained no codex-skills/ at all, so this exposure is newly created. The only thing keeping them out of a Claude session is the explicit &#34;skills&#34;: [...] array at line 22, which enumerates exactly the 11 ./skills/&lt;name&gt; paths. That is a real, source-proven control (Claude's default discovery root is ./skills/, and codex-skills is a different directory name), and the same control correctly scopes commands/agents away from codex-commands//codex-agents/. I am NOT claiming it fails — I have no source in this repo for Claude Code's plugin loader, and the instructions forbid inferring a defect from absence. Flagging only because the blast radius would be user-visible if the loader also globbed **/SKILL.md: 28 extra entries per session whose bodies instruct $rn-dev-agent:&lt;workflow&gt; invocation syntax that does not exist in Claude. This is the exact mirror of the hooks-suppression tradeoff you already accepted (Codex discovering hooks/), and the author neutralized that side explicitly via hooks: {hooks: {}}. One live claude --plugin-dir /path/to/rn-dev-agent + /help skill-list check before release closes it. No source change implied.
  • ℹ️ packages/rn-dev-agent-core/src/session/product-version.ts:74 - After the single-distribution-root move, two of the four entries the rewritten pluginManifestCandidates() returns can no longer be reached by any shipped or repo layout. Candidate 2 (&lt;hostRoot&gt;/.codex-plugin/plugin.json) is only consulted when candidate 1 (&lt;hostRoot&gt;/.claude-plugin/plugin.json) is absent — but check-agent-package-sync.sh:250-252 now requires BOTH manifests to exist side by side in packages/claude-plugin, and sync-versions.sh:31-32 keeps their versions equal, so candidate 1 always hits first. Candidate 4 (&lt;hostRoot&gt;/codex-plugin/.codex-plugin/plugin.json) is only consulted when candidate 3 (&lt;hostRoot&gt;/claude-plugin/.claude-plugin/plugin.json) is absent — in the repo-source case hostRoot is packages/, where claude-plugin/.claude-plugin/plugin.json is a required tracked file. An older marketplace install cannot revive either branch, since such an install executes its own bundled copy of this file. Purely inert dead code, not a correctness defect: both hosts already resolve correctly through the env candidates in launchingHostManifestCandidates() (Claude via CLAUDE_PLUGIN_ROOT with the Codex hints blanked by the new mcpServers.cdp.env block, Codex via RN_DEV_AGENT_CODEX_PLUGIN_ROOT set by bin/cdp-supervisor.js:52). Marked no-op rather than a cleanup request because you already scoped the dead-branch removal in this exact function last round and deliberately drew the line where you drew it; noting it only so the remaining inertness is on record.
✅ **Test** - passed

✅ No issues found.

  • git ls-tree -r --name-only d76a9e2a|e587136a | grep rn-dev-agent-core/dist/ — confirmed two tracked host dist copies collapse to one
  • git ls-tree -r -l byte totals on both commits — tracked repo size 47.7 MB -> 36.7 MB (-10.9 MB)
  • Staged both installs from the committed bytes: git archive HEAD packages/claude-plugin | tar -x then cp -R into a fake Claude marketplace root and a fake $CODEX_HOME/plugins/cache/&lt;marketplace&gt;/rn-dev-agent/1.0.8; find -type l confirmed no symlinks
  • Live MCP stdio session against the Claude install root using the command read from the installed .claude-plugin/plugin.json (initialize + tools/list) — rn-dev-agent-cdp-bridge 1.0.8, 81 tools
  • Live MCP stdio session against the Codex install root using .codex-plugin/plugin.json -> codex.mcp.json launcher with only CODEX_HOME set and RN_DEV_AGENT_CODEX_PLUGIN_ROOT empty (forces real cache discovery) — rn-dev-agent-cdp-bridge 1.0.8, 81 tools
  • tools/call cdp_status through each installed host — both return {&#34;product&#34;:{&#34;coreVersion&#34;:&#34;1.0.8&#34;}}, exercising the reworked neutral-package product-version resolution
  • node bin/plugin-health.js from the Codex install root — "Disk, registration, and contract are healthy"
  • node &lt;root&gt;/rn-dev-agent-core/dist/learned-actions.js --json from both install roots
  • Resolved every path named by the corrected Codex bullet inside the install root: .codex-plugin/plugin.json, codex-skills/ (28), codex.mcp.json, codex-commands/ (17), codex-agents/ (6)
  • shasum -a256 byte-mirror check of packages/shared-agent-knowledge/skills/using-rn-dev-agent/SKILL.md vs packages/claude-plugin/skills/using-rn-dev-agent/SKILL.md
  • bash scripts/test/check-dist-fresh.test.sh — 17/17 including the new second tracked host dist fails case
  • bash scripts/check-dist-fresh.sh (real repo gate: wipe core dist, corepack yarn build, build:web, node scripts/build-host-runtimes.ts, porcelain diff, npm pack check) — "dist fresh", git status --porcelain empty afterwards
  • bash scripts/check-agent-package-sync.sh on the real repo
  • bash scripts/test/check-agent-package-sync.test.sh, check-core-dist-contract.test.sh, check-typescript-only.test.sh, check-web-bundle.test.sh — 43 further guard cases
  • node --test on the core units touched by the change: gh-575-codex-workflows, gh-590-codex-feedback, session/gh-1025-status-product-version, runtime-paths, runner-manifest-publication, gh-588-proof-dual-authority, gh-525-feedback-packaging, gh-575-build-helper, gh-575-snapshot-helper, session/process-birth — 163 pass / 0 fail
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…oth marketplaces (#892)

Both the Claude and Codex marketplaces now install packages/claude-plugin.
It carries the single bundled runtime (rn-dev-agent-core/dist with host-neutral
rn-dev-agent-core metadata) plus the Codex adapters generated from the
packages/codex-plugin authoring tree: .codex-plugin/plugin.json (explicit
./codex-skills/, ./codex.mcp.json, commands: [], inline empty hooks),
codex.mcp.json, bin/ launchers with an ESM package boundary, codex-skills/,
codex-commands/, codex-agents/, codex-templates/, AGENTS-MD-TEMPLATE.md.

packages/codex-plugin keeps only authoring sources; its duplicated runtime,
native helpers, helper scripts, runner manifest and generated skills are
removed. The freshness, package-sync, core-dist and web-bundle guards rebuild
and compare the one committed tree and every generated Codex output, and
refuse a second tracked host dist. Claude's manifests clear inherited Codex
root hints so the neutral runtime reports Claude's launching root.

Tracked bytes: 49,965,515 -> 38,493,416.
…claude-plugin copy (#892)

The finalize step, the offline validator and the public-asset check now
generate, compare and commit one distributed runner-manifest.json copy under
packages/claude-plugin instead of the removed packages/codex-plugin copy, and
the version-bump allowlist admits the regenerated codex.mcp.json. The callable
producer and sweep workflows are untouched.
@greptile-apps

greptile-apps Bot commented Sep 14, 2026

Copy link
Copy Markdown

Too many files changed for review (291 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T11:54:52.128247Z 7ed1e80 PR opened
🔒 Security Review Completed 2026-09-14T11:53:49.457423Z 7ed1e80 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Lykhoyda

Copy link
Copy Markdown
Owner Author

Local install proof for this head's layout (isolated HOME/CLAUDE_CONFIG_DIR/CODEX_HOME, Claude Code 2.1.270, Codex CLI 0.154.0):

  • Both claude plugin install rn-dev-agent@rn-dev-agent (root marketplace file) and codex plugin add rn-dev-agent@rn-dev-agent (.agents/plugins/marketplace.json) materialized packages/claude-plugin byte-identically: 294 files, 13,232,134 bytes, no symlinks, packaged core metadata rn-dev-agent-core 1.0.8.
  • MCP through each host's own registration (Claude: rn-dev-agent-core/dist/supervisor.js; Codex: the codex.mcp.json bootstrap -> bin/cdp-supervisor.js): initialize -> rn-dev-agent-cdp-bridge 1.0.8, tools/list = 81 tools, worker executing the installed rn-dev-agent-core/dist/index.js from the app cwd.
  • bin/plugin-health.js --json on the Codex root: installation ENABLED, MCP REGISTERED, materialization EXACT_HEALTHY, contract probe HEALTHY (81 tools); 28 Codex skills, manifest skills ./codex-skills/, commands [], mcpServers ./codex.mcp.json, hooks {"hooks":{}}.
  • On the review note about Claude possibly globbing the shipped codex-skills/: claude plugin details rn-dev-agent@rn-dev-agent on the isolated Claude install lists Skills (11), exactly the ./skills/* entries from .claude-plugin/plugin.json, and no codex-skills/codex-commands/codex-agents entry.

Device runner health, app flow and the 1.0.8 upgrade path remain for independent QA.

@Lykhoyda Lykhoyda added the needs-qa Independent dogfood QA on test-app simulator; Orchestrator posts a PR comment report label Sep 14, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HANDOFF: needs-qa

QA requested (needs-qa).
Linux automation cannot run device QA. GetSafe Project will spawn a new local Mac session.

QA_PR: #1037
PLATFORM: all
IMAGE_WIDTH: 800
POST_COMMENT: true

Claim: GH-892 makes packages/claude-plugin the single marketplace install root for both Claude and Codex, carrying one committed rn-dev-agent-core/dist plus generated Codex manifests and adapters (.codex-plugin/plugin.json, codex.mcp.json, bin/, codex-skills/, codex-commands/, codex-agents/, codex-templates/); packages/codex-plugin is authoring-only, and both hosts must install and run from that one copy with CI gates (check-dist-fresh, package-sync, release trust-root) pointed at it.

Changed paths:

  • .agents/plugins/marketplace.json
  • packages/claude-plugin/.claude-plugin/plugin.json
  • packages/claude-plugin/.codex-plugin/plugin.json
  • packages/claude-plugin/bin/cdp-supervisor.js
  • packages/claude-plugin/codex-skills/, codex-commands/, codex-agents/
  • packages/rn-dev-agent-core/src/session/product-version.ts
  • scripts/build-host-runtimes.ts / scripts/check-dist-fresh.sh
  • .github/workflows/release.yml
  • docs/README/AGENTS updates; packages/codex-plugin runtime artifacts removed

Local runner must:

  • New session (do not reuse an old chat; plugin ≥ 1.0.8 with /qa-pr)
  • Version preflight vs GitHub main + latest release before any device work
  • Own disposable git worktree (never the primary checkout)
  • Own dedicated simulator/emulator named with the PR identity, e.g. QA PR 1017 short-title
  • Plugin-repo PRs bind workspace test-app/
  • Report back on this PR with gh --attach and
  • Overall PASS or FAIL (PR/fix). Never PARTIAL.

Follow: https://github.com/Lykhoyda/rn-dev-agent-workspace/blob/main/.cursor/skills/cursor-project-qa/SKILL.md

Open in Web View Automation 

Sent by Cursor Automation: New bug for QA

@Lykhoyda

Lykhoyda commented Sep 14, 2026

Copy link
Copy Markdown
Owner Author

Verdict: FAILED at exact head 7ed1e80de5d65c11ca5a7e58d4477cf2a8e3043c.

Phases: 1 GO · 2 GO (loud same-version upgrade finding) · 3 iOS GO · 3 Android NOT RUN.

Acceptance QA of one committed host runtime under packages/claude-plugin for both marketplaces (issue 892). Isolated Claude CLI 2.1.270 + Codex CLI 0.154.0 marketplace installs of this Git head only. Desktop Codex NOT RUN (GUI would mutate the real Codex home). Merge stays with the captain.

Phase 1 — GO

Fresh isolated installs, no sibling source tree. Both caches: 294 files, 13,230,174 bytes, 0 symlinks, core package rn-dev-agent-core 1.0.8 ESM, plugin 1.0.8. One tracked dist (20 paths). 28 Codex skills. Claude commands + hooks present. Codex hooks: {"hooks": {}} (empty object). MCP via each host’s normal registration: protocol 2024-11-05, 81 tools, product.coreVersion 1.0.8 (pluginVersion omitted when equal). Packaging refusal battery all pass (second dist, stale/missing adapters, symlink materialization, wrong native pin, Claude-hook fallback shapes).

Phase 2 — GO, with a loud finding

Upgrade from existing 1.0.8: Codex marketplace retarget + plugin add recopied onto the single dist. Claude plugin update is a same-version no-op (up_to_date at 1.0.8); uninstall+install refreshed the cache. Architect asked for a version transaction; this head did not bump off 1.0.8. Claude users will not get the packaging change from a normal update.

Phase 3

iOS GO on an owned simulator through the isolated PR-head install (workspace main 8c0187e4622694708c397835fc3125dea8ec43cf). Managed pnpm run ios after observe stop + apply_integration. Snapshot source rn-fast-runner, quiescence bypass active, runnerBound. First auto-start timed out at 30s then recovered — preserved, not smoothed. runner-manifest.json 1.0.8 iOS zip sha256 773ea34531ba3dbf183895ceec9eaf0ff7f24dbe5b4f96a2cfae66210e92ea48 matches the 1.0.8 cache artifact.

  • toggle-theme correctly refused: ENGINE_PIN_MISMATCH (YAML has no enginePin; pin is maestro-runner 1.1.24).
  • qa-observe-screen-match (already pinned) passed: transport=cdp-js, proofDomain=react-tree, result screen below.

Android NOT RUN — required by the commission, so overall FAILED. Owned emulator died before adb registration (host memory). Did not bind another lane’s emulator or the physical phone.

Owned sim deleted; owned AVD directory removed; reverse session cleanup (close → stop Metro → restore integration → release) proved on iOS.

iOS QA acceptance result after qa-observe-screen-match Managed iOS launch showing RnFastRunner chrome

@Lykhoyda

Copy link
Copy Markdown
Owner Author

QA dogfood (needs-qa)

Verdict: blocked

Original problem

Issue #892: both Claude and Codex plugin trees each committed a full ~10 MB rn-dev-agent-core/dist copy. This PR consolidates to one committed host dist under packages/claude-plugin (both marketplaces install that directory) and leaves packages/codex-plugin as authoring material only.

What I tested

Nothing live. Environment gate failed before worktree / test-app / bind.

Environment

  • head SHA: 7ed1e80de5d65c11ca5a7e58d4477cf2a8e3043c
  • branch: fm/rn-dev-agent-issue-892-one-committed-hos-28
  • Mac local-exec: unavailableListMachines returned an empty machine list (no registered Mac connected)
  • worktree: not opened (blocked by Mac disconnect policy)
  • test-app / simulator / bind: not attempted

Expected vs actual

  • Expected: Mac reachable so dogfood can open a PR worktree, launch workspace test-app on simulator, bind the PR supervisor, and verify both marketplace install roots still expose MCP tools / live session behavior from the single committed dist.
  • Actual: No Mac in ListMachines; live device QA cannot run from the Linux sidecar.

Alternate paths

  • Re-checked ListMachines at routine wake — still empty.
  • Per policy: do not clone product repos onto the sidecar for live device work; do not open a worktree while Mac is disconnected.

Evidence

  • Candidate discovery: open PR with needs-qa#1037
  • No prior QA dogfood (needs-qa) comment on this PR (first-time block)
  • Blocker: Mac local-exec / ListMachines empty

Open risks / next step

Reconnect Anton's Mac (Grok Bot desktop app → Agent → Execution on Local Computer not "Never allowed", full quit Cmd+Q and reopen). Re-run will dogfood this head once the Mac appears in ListMachines. Until then, subsequent Mac-disconnect wakes stay silent for 24h on this PR.

@Lykhoyda

Copy link
Copy Markdown
Owner Author

Verdict: FAILED at exact head 7ed1e80de5d65c11ca5a7e58d4477cf2a8e3043c.

Phases this completion run covered: Desktop NOT RUN (supported isolation blocker) · Android NOT RUN (owned emulator exited before adb registration). Preserved from the earlier exact-head run: Phase 1 GO · Phase 2 GO (same-version no-op control) · Phase 3 iOS GO. Upgrade proof remains OUT OF SCOPE (needs main’s future immutable U with unused V). Merge stays with the captain.

Acceptance remains NO-GO. Missing Android public-tool proof is not waived. CLI is not Desktop.

Desktop Codex — NOT RUN

Tried the public product surfaces only. Did not launch the GUI and did not mutate the real Desktop home.

  • Official env table lists CODEX_HOME for CLI, IDE extension, app-server, and installers — not Desktop.
  • codex app --help (CLI 0.154.0) opens the stock app with a workspace path; no --home / --user-data-dir; config help still names the real user ~/.codex/config.toml.
  • CLI binary has no --user-data-dir and does not set CODEX_ELECTRON_USER_DATA_PATH. An undocumented Electron env appears only on an internal demo-app path in the asar, not as a documented codex app flag.
  • Third-party profile wrappers would invent isolation. Forbidden here.

Missing: a first-party documented Desktop launch that isolates both Electron userData and plugin/config state. Real config.toml hash was unchanged after the investigation.

Android — NOT RUN

Ran in parallel with #1034 (that lane kept emulator-5554). This lane used its own AVD, console port 5574, workspace base 7a63707af91b8bec20cab76f902a1553985251e6 (newest workspace main), and the preserved Phase 1 candidate install (81 tools, core 1.0.8, Android runner zip digest matches the 1.0.8 cache). USB phone and two foreign iOS sims were not bound.

The owned qemu started, passed the emulator compatibility checks, created a window, printed SwiftShader/ANGLE GPU lines, then exited before emulator-5574 appeared in adb. The other lane’s emulator stayed registered. Retry died at the same log line. No FATAL/ERROR in the emulator log, no crashpad report, no DiagnosticReports, no jetsam line in a short unified-log window. Not reported as an OOM kill — termination cause is bounded uncertainty. A booted-then-dead qemu and a matching artifact hash are not Android acceptance. Pin/protocol checks were left intact (toggle-theme unpinned refusal stays correct).

Host at first boot: load ~41 with another qemu live; earlier in the session load spiked above 80 on a 32 GB machine. Owned AVD and worktree were removed; foreign inventories were unchanged.

Upgrade — outstanding

No version bump and no reinstall workaround in this run. The recorded 1.0.8→1.0.8 Claude up_to_date no-op remains a control. Normal upgrade still needs the centrally prepared unused version V on immutable candidate U.

@Lykhoyda

Lykhoyda commented Sep 14, 2026

Copy link
Copy Markdown
Owner Author

Verdict: FAILED at exact head 7ed1e80de5d65c11ca5a7e58d4477cf2a8e3043c.

This is a serial Android completion on an idle Mac (zero qemu at boot). Phases 1, 2, and 3 iOS stay PRESERVED from the earlier exact-head run. Desktop Codex stays NOT RUN (supported isolation blocker; not re-investigated). Upgrade transaction stays OUT OF SCOPE (needs main’s future U/V; 1.0.8→1.0.8 no-op remains a control). Merge stays with the captain.

Android — session GO, pinned action FAIL

Owned AVD QA PR 1037 android serial 7ed1 (console 5584, -memory 2048 -gpu guest -cores 2; predecessor used 5574 / 1536 / swiftshader_indirect and never registered). Isolated candidate MCP of this Git head: 81 tools, bridge 1.0.8, product.coreVersion 1.0.8. Android runner zip sha256 48e369f80f28e65ecfd1b9eb124cbca49cfbe245e9c6d6c5957d2417b5a1bbfa matches runner-manifest.json. Managed pnpm run android via the session adapter targeted this AVD only. pin_dev_client + device_snapshot open: originAuthority: proven, reactNativeUi: ready, cdp.connected: true, installIdentity: verified.

Before the action, the Test App was on onboarding and app-root was present in both the native snapshot and the fiber tree:

Android Test App onboarding before the pinned action

cdp_run_action qa-observe-screen-match (already enginePin: maestro-runner@1.1.24) ran maestro-runner 1.1.24, then failed assertVisible id=app-root after YAML launchApp: stopApp: true. Auto-repair skipped (NOT_REPAIRABLE_KIND); YAML not rewritten. Unpinned toggle-theme was left intact. After the action the screen was the Expo Dev Client picker — the JS session had been torn down:

Expo Dev Client picker after launchApp stopApp true

A booted emulator is not acceptance. Missing passing pinned-action proof keeps this NO-GO.

Owned qemu, AVD, Metro, and app copy were reverse-cleaned. Two foreign iOS sims and the USB phone were not touched.

@Lykhoyda

Copy link
Copy Markdown
Owner Author

FAILED — QA input-correction experiment for #1037 at 7ed1e80de5d65c11ca5a7e58d4477cf2a8e3043c against workspace 7a63707af91b8bec20cab76f902a1553985251e6.

Binding the existing Expo Dev Client URL before the owned Android build did not produce a visible app result from the original saved action. This is evidence for the captain, not a merge gate. No approval or merge decision. No product source was patched.

Twin comparison (the deliverable). The identical correction on #1034 already survived as FAILED. This independent second data point MATCHES that stage and symptom: pin_dev_client still returned BUNDLE_HANDSHAKE_UNAVAILABLE / CDP_TARGET_AUTHORITY_MISMATCH (expected one target, found 0, Metro 8095, 120s). Unchanged qa-observe-screen-match never executed (RUNNER_OWNERSHIP_MISMATCH). This is not the later 1037 android2 wall (YAML launchApp stopApp:true then assertVisible app-root timeout on the picker). Snapshot refusal here was NOT_CONNECTED (app not running) rather than the twin’s RN_ANDROID_RUNNER_DOWN; neither bound a runner or showed a native surface. installIdentity stayed verified. APP_INSTALL_IDENTITY_CHANGED did not appear.

What changed vs prior Android runs on this PR. Previous exact-head Android bound the device without a URL, then built, then pinned (pin succeeded; action then dropped to the picker). This run called rn_session bind_device with devClientUrl=exp+rn-dev-agent-test://expo-development-client/?url=http%3A%2F%2F10.0.2.2%3A8095 (the reserved session Metro port) before pnpm run android. The signed receipt carried that same devClientUrl. The adapter opened that URL on the owned emulator QA_PR1037_bindfirst.

Unchanged action YAML sha256 2f3521de6b5a0e6007d82d17fc1bf33d28109df563ae084ece4d8707b78132a1 (stopApp: true, assertVisible app-root). Repeat was not reachable. No native screenshot this run.

Picker-first and APP_INSTALL_IDENTITY_CHANGED are the known separate Android launch issue from prior runs. This experiment does not use them as verdict reasoning.

iOS, CLI/reporting, and Desktop were NOT RUN (preserved / out of scope). Foreign iOS simulators and the attached USB phone were not touched. Owned emulator and Metro were removed; package scripts restored to expo run:ios / expo run:android.

The bind-first input correction did not avoid the Android saved-action wall on this PR. No product patch is justified from this run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-qa Independent dogfood QA on test-app simulator; Orchestrator posts a PR comment report

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant