8.2 KiB
title, date, category, module, problem_type, component, symptoms, root_cause, resolution_type, severity, tags, related_components
| title | date | category | module | problem_type | component | symptoms | root_cause | resolution_type | severity | tags | related_components | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Agent Plugins $schema on root plugin.json is a host routing switch that truncates or drops skills on Codex and oh-my-pi | 2026-08-17 | integrations | plugin.json | integration_issue | tooling |
|
config_error | config_change | high |
|
|
Agent Plugins $schema on root plugin.json routes Codex and oh-my-pi to strict paths that truncate or drop skills
Problem
Declaring the Agent Plugins v1 $schema on root plugin.json (PR #1345, 3.22.0) was treated as inert metadata. On two shipping hosts it is a routing switch: it selects a stricter discovery path that truncates every SKILL.md at 8000 bytes (Codex) or rejects any SKILL.md with Claude Code frontmatter keys (oh-my-pi). Most of the plugin's skills silently stopped working on those hosts.
Symptoms
- Codex >= 0.147: skills such as
ce-setupran without their gates/handoffs; the transcript carried the warning "Skillce-setupexceeded the main prompt context limit and was truncated." The reported figure (11,392 bytes) matchedskills/ce-setup/SKILL.mdmeasured CRLF-adjusted, confirming a Windows checkout (#1412). - oh-my-pi 17.3.5:
omp plugin listreported the plugin enabled, yet/skill:<name>was absent for 30/33 skills; only the three skills with pure Agent Skills frontmatter loaded (#1411). - Neither host errored at install time; failure was visible only at invocation.
What Didn't Work
- Trusting the spec's "clients MUST skip non-conformant skills" and the 2026-08-07 note in
docs/specs/agent-plugins.mdthat "no shipping conformant Agent Plugins client is known" and runtime impact was unverified. Two clients shipped strict routing within ten days. - Relying on the model to notice Codex's truncation warning and re-read the SKILL.md from disk. That is exactly the failure users reported: the model proceeded on the truncated prompt.
- Looking for a per-host override on omp. There is none:
legacyProviderAllowed(agent-plugin-format.ts, called from omp-plugins.ts) locks the lenient provider out on the$schemaprefix alone, and any other agent-plugins$schemavalue is fatally invalid rather than a fallback. - Keeping
$schemaat the root while emitting frontmatter both hosts accept. Claude Code needsargument-hint/disable-model-invocationat the top level, so no single root manifest + skill set satisfies Claude, Codex, and omp. - A conditional test guard ("
$schemaforbidden while any skill >8KB or has non-conformant frontmatter"). It accreted several review rounds of predicate edge cases (flow-listallowed-tools, per-field types, emptycompatibility, YAML timestampmetadata) before being replaced by an unconditional assertion (PR #1427).
Solution
PR #1426 removed $schema from root plugin.json (the Agent Plugins / agy manifest at the repo root; .claude-plugin/plugin.json and .codex-plugin/plugin.json are separate, host-native manifests and were untouched); every other field stayed. Before:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "compound-engineering",
"version": "3.22.1",
...
}
After (root plugin.json):
{
"name": "compound-engineering",
"version": "3.22.2",
"description": "Brainstorm, plan, debug, review, and compound learnings with AI agents",
...
}
PR #1427 pinned the posture in tests/codex-skill-prompt-budget.test.ts:
const CODEX_MAX_SKILL_PROMPT_BYTES = 8_000
const AGENT_PLUGINS_SCHEMA_PREFIX = "https://agent-plugins.org/schemas/"
/** Byte size as a Windows checkout with CRLF line endings would inject it. */
function crlfByteSize(contents: string): number {
const lf = contents.replace(/\r\n/g, "\n")
return Buffer.byteLength(lf, "utf8") + (lf.match(/\n/g)?.length ?? 0)
}
test("root plugin.json never carries an Agent Plugins $schema", () => {
const schema = typeof manifest.$schema === "string" ? manifest.$schema : ""
expect(schema.startsWith(AGENT_PLUGINS_SCHEMA_PREFIX)).toBe(false)
})
plus a set-based OVER_BUDGET allowlist (26 names) that is shrink-only: no new skill may exceed 8000 CRLF-adjusted bytes, and a name must be removed once its SKILL.md fits. Membership is a set rather than pinned sizes so ordinary edits to already-truncated skills do not churn the list. tests/release-metadata.test.ts (agentPluginsManifestErrors) accepts an absent $schema and still rejects any value other than the exact Agent Plugins URL when present. docs/specs/agent-plugins.md records the decision: root stays schema-less indefinitely; a strict client gets a separately emitted package (Claude keys under metadata:, allowed-tools as a string) from its own marketplace source.
Why This Works
Both hosts branch on the $schema prefix, not on the plugin's actual conformance:
- Codex
find_plugin_manifest_path(codex-rs/utils/plugins/src/plugin_namespace.rs, openai/codex#37027) prefers rootplugin.jsonover.codex-plugin/plugin.jsonwhen$schemastarts withhttps://agent-plugins.org/schemas/, classifying the package as an Agent Plugin;build_skill_injections(codex-rs/core-skills/src/injection.rs) then appliestake_bytes_at_char_boundary(contents, MAX_SKILL_PROMPT_BYTES), with the constant (8_000) defined in openai/codexcodex-rs/core-skills/src/lib.rs. Legacy manifests are exempt. - omp
agent-plugin-format.ts(~line 179) routes the same prefix to a provider whosevalidateAgentSkillFrontmatter(~124-159) allows only{name, description, license, compatibility, metadata, allowed-tools}with stringallowed-tools. 28 skills carryargument-hint, 13disable-model-invocation, andce-proofandce-product-pulseuse a listallowed-tools.
Removing the $schema puts both hosts on their lenient legacy paths (.codex-plugin/plugin.json for Codex; omp's legacy provider loads all 33), while agy, Grok, and Claude Code never depended on the field. The unconditional assertion encodes the real condition (the routing switch itself is harmful given this plugin's shape) instead of a predicate over skill contents that reviewers could keep refining.
Prevention
tests/codex-skill-prompt-budget.test.tsfails CI if the root manifest regains an Agent Plugins$schema, if a new skill crosses 8000 CRLF-adjusted bytes, or ifOVER_BUDGETlists a skill that now fits.- The schema-less posture is a workaround, not a resolution: the
OVER_BUDGETsweep is the standing goal that eventually makes a conformant package possible. Procedure for taking a skill under the cap:docs/solutions/skill-design/size-driven-skill-restructure.md. - Before adopting a manifest field that a spec calls "metadata", read the shipping hosts' discovery source (curl the raw Codex tree;
npm pack @oh-my-pi/pi-coding-agentfor omp) rather than trusting "clients MUST skip". A field can be a routing switch. - Reproduce Windows-reported byte figures with
Buffer.byteLength(lf) + count('\n'); a Codex truncation number that matches CRLF size confirms the mechanism. - Write guards as unconditional statements of the decided posture; a guard qualified by content predicates invites an accretion loop of edge cases.
- Re-verify (see
docs/specs/agent-plugins.md) if omp adds a per-host override (can1357/oh-my-pi#8853) or Codex bounds all skill sources at 8KB (openai/codex#37463), which would force restructuring every skill to <=8KB regardless of manifest.
Related Issues
- #1411 (oh-my-pi), #1412 (Codex), PR #1345 (introduced
$schema), PR #1426 (removed it), PR #1427 (tests + spec posture) - openai/codex#37027, openai/codex#37463, can1357/oh-my-pi#8853
docs/specs/agent-plugins.md