Skip to content

fix: correct Codex plugin output for the target registry - #16

Merged
steve-calvert-glean merged 5 commits into
mainfrom
refactor/codex-target-registry
Jul 26, 2026
Merged

fix: correct Codex plugin output for the target registry#16
steve-calvert-glean merged 5 commits into
mainfrom
refactor/codex-target-registry

Conversation

@steve-calvert-glean

Copy link
Copy Markdown
Contributor

Summary

Ports emitCodex/validateCodex into src/targets/codex.ts as a PluginTargetDefinition, and along the way corrects three real spec discrepancies — re-verified directly against https://developers.openai.com/codex/plugins/build (fetched twice, independently, for consistency), since Codex has neither a CLI validator nor a vendored schema to check against, unlike claude/cursor.

  • plugin.json was over-validated. Only name is actually required; the old validator also required version/description. Fixed — pluginpack still emits both by default (an authoring convenience), it just no longer rejects a manifest that omits them.
  • policy.installation/policy.authentication/category were unvalidated. The docs are explicit these are required on every marketplace entry. pluginpack can't infer them, so the base entry stays guess-free (as the existing code comment already intended) and validateOutput now errors clearly when they're missing — instead of silently shipping an incomplete entry. The existing conformance fixture already supplies them via the per-plugin entry passthrough, so this needed no fixture changes.
  • source is only a bare string for local plugins. url/git-subdir/npm sources are structured objects with an inner source discriminator (e.g. { source: "git-subdir", url, path, ref }). validateMarketplaceEntry now accepts either shape — this is the concrete case that motivated making Codex's validator genuinely custom rather than sharing the string-only validator every other target used.
  • hooks was never declared in the manifest. plugin.json now points hooks at the bundled hooks/hooks.json when present, matching how skills/mcpServers are already declared.

Updates CONFORMANCE.md's Codex section, which had pinned a stale, bare-string-only shape from an earlier (2026-06-17) doc retrieval.

Stacking

Based on refactor/claude-target-registry (#15). Merge in order: #14#15 → this.

Test plan

  • npm run typecheck
  • npm run lint
  • npm test (63/63 passing — 4 new Codex regression tests: over-strict manifest validation dropped, missing policy/category caught, structured non-local source accepted, hooks pointer declared)
  • npm run build
  • node dist/cli.js docs --check

steve-calvert-glean and others added 4 commits July 26, 2026 13:42
Re-checking the originally planned Cursor "fixes" against the vendored
plugin.schema.json / marketplace.schema.json (this repo's own
conformance oracle, already passing against the current shape) showed
they were wrong:

- displayName/category/tags ARE valid plugin.json fields per the
  schema (additionalProperties: false, and they're explicitly listed)
  — not marketplace-entry-only fields as previously assumed.
- Marketplace `owner` is genuinely optional (required: ["name",
  "plugins"] does not include it) — not required as previously
  assumed. Moving category/tags to the marketplace entry would have
  been actively wrong: entries only allow name/source/description
  (additionalProperties: false).

None of that is changed here. What this commit actually does:

- Migrates cursor onto PluginTargetDefinition, preserving every
  existing field and behavior (verified by the vendored-schema
  conformance test staying green).
- Fixes one genuine, low-risk issue: the manifest builder's own
  hardcoded default-components list could diverge from this target's
  actual defaultComponents (components.ts). Replaced both with one
  list of schema-valid pointer fields, checked directly against the
  plugin's real resolved componentDirs — eliminates the divergence
  risk with no observable behavior change (confirmed via a new test
  exercising the one case that could have differed: an explicit
  `components: [...]` override).
- Ports update-check's hook-injection into the new shared engine
  (src/targets/engine.ts), which previously only existed in the
  legacy emitCursor/emitClaude path — migrating cursor without this
  would have silently dropped update-check support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
buildPluginManifest used the target-level `version` param directly
instead of `pluginConfig.version ?? version`, silently dropping a
per-plugin version override — a real regression from the pre-migration
behavior, caught by porting the equivalent test from the claude
migration (no test previously covered this for cursor specifically).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports emitClaude/validateClaude into src/targets/claude.ts as a
PluginTargetDefinition, verified directly against `claude plugin
validate --strict`: a minimal manifest with only `name` fails that
check, confirming the existing version/description/author.name
requirements already match the real CLI rather than over-constraining
it.

Applies the per-plugin version override in buildPluginManifest
(pluginConfig.version ?? version) up front, matching the identical fix
just made to cursor's copy of this pattern.
Ports emitCodex/validateCodex into src/targets/codex.ts as a
PluginTargetDefinition, correcting the plugin format's real shape —
re-verified directly against developers.openai.com/codex/plugins/build
(fetched twice, independently, for consistency) since Codex has no
CLI validator or vendored schema to check against:

- plugin.json requires only "name"; version/description/author etc.
  are optional. The previous validator wrongly required version and
  description.
- Every marketplace entry needs policy.installation,
  policy.authentication, and category — previously unvalidated, so an
  incomplete entry shipped silently. pluginpack can't infer these, so
  the base entry stays guess-free and validateOutput now errors
  clearly when an author never supplies them via the per-plugin
  `entry` passthrough (already how the existing conformance fixture
  supplies them).
- A marketplace entry's source is a bare string only for local
  plugins (the only shape pluginpack itself ever emits); url/git-subdir/npm
  sources are structured objects with an inner "source" discriminator.
  validateMarketplaceEntry now accepts either shape instead of the
  previously shared, string-only validator.
- plugin.json now declares a `hooks` pointer when hooks/ is present,
  matching skills/mcpServers (previously only skills/mcpServers were
  declared, so hooks were emitted but never referenced).

Updates CONFORMANCE.md's Codex section, which had pinned a stale,
bare-string-only shape from an earlier doc retrieval.
Comment thread src/targets/codex.ts
return null;
}
const { name } = entry;
if (typeof entry.source === "string") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Issue: validateCodexEntry does not return null when entry.source is invalid (unsafe path, missing local directory, or malformed structured source), so validateOutput still attempts to resolve and read the plugin manifest using the invalid source, unlike the legacy validatePluginEntry and shared validateBareStringSourceEntry helpers which bail out on source errors.

Suggested fix: Update validateCodexEntry to mirror validateBareStringSourceEntry and the legacy validatePluginEntry contract by returning null immediately after any source-related error (unsafe relative path, missing local directory, invalid source.source discriminator, missing required fields for local/url/git-subdir/npm), so that callers like codex.validateOutput skip further filesystem validation for that entry.

🔧 Tag @ glean-for-engineering to fix or click here to fix in Glean

💬 Help us improve! Was this comment helpful? React with 👍 or 👎

@steve-calvert-glean steve-calvert-glean added the bug Something isn't working label Jul 26, 2026
Base automatically changed from refactor/claude-target-registry to main July 26, 2026 23:11
…-registry

# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
#
# Conflicts:
#	src/targets/registry.ts
#	tests/core.test.ts
@steve-calvert-glean
steve-calvert-glean merged commit 8eccdb1 into main Jul 26, 2026
1 check passed
@steve-calvert-glean
steve-calvert-glean deleted the refactor/codex-target-registry branch July 26, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant