Skip to content

fix: Claude Code agent frontmatter and the failing upstream sync - #59

Merged
SmailG merged 8 commits into
mainfrom
fix/claude-agent-frontmatter
Oct 19, 2026
Merged

SmailG merged 8 commits into
mainfrom
fix/claude-agent-frontmatter

Conversation

@SmailG

@SmailG SmailG commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Problem

Upstream agents use Cursor-only frontmatter that Claude Code ignores. Claude Code 2.1.296's debug log flags them: has a frontmatter key Claude Code does not recognize or apply: "readonly" / "is_background".

The practical effect: the four agent-compatibility reviewers, create-plugin:plugin-architect and advisor:advisor-subagent are meant to be read-only, but in Claude Code they run with every tool. ci-watcher, poteto-agent and dyl-agent are meant to run in the background and don't.

Separately, the daily upstream sync has failed since Oct 6. This PR folds in #58 (by @back1ply), which fixes that, because the agent change needs a working sync to stay current. It will be merged with a merge commit so #58's commit keeps its author.

Fix

Editing the upstream agent files would be undone: sync-upstream.yml resolves every merge conflict in upstream's favour. Instead, scripts/sync-manifests.mjs now:

  • writes Claude Code copies of the affected agents under <plugin>/.claude-plugin/agents/, a fork-owned folder. The copies sit outside agents/, so upstream's pstack harness generator, which walks pstack/agents recursively, never sees them. Each copy carries a "generated, do not edit" comment. The folder's marker file lists what the script wrote, and the script refuses to delete a folder without the marker or with any file the marker does not list.
    • readonly: true → disallowedTools: Edit, Write, NotebookEdit, Agent, merged into any existing scalar disallowedTools. Bash is not restricted, so a read-only agent can still write files through it.
    • is_background: true → background: true, unless the agent already sets background.
    • model: fast → haiku; an [effort=…] suffix → effort:; a non-Claude model (grok-4.6[...]) is dropped, so the default model order applies.
  • lists every agent file in that plugin's .claude-plugin/plugin.json agents field. A manifest agents field replaces the default agents/ scan, so the originals don't load twice (manifest reference).

It fails closed. Only an unquoted YAML 1.2 false (any case, optionally commented) turns readonly off. Every other value denies writes, including a quoted "false", which a YAML parser reads as a truthy string. Each of these throws and fails the sync rather than shipping a writable agent:

  • a Cursor key that is not a plain top-level key: quoted, indented, tab-indented, in a flow map, as a ? key complex key, or outside a recognisable frontmatter block. This includes a plain key repeated in another shape (readonly: false plus "readonly": true).
  • a quoted or complex key with a backslash, which could spell a Cursor key in escapes;
  • a duplicate readonly, is_background, disallowedTools, model, effort or background;
  • an is_background value other than true or false;
  • a YAML-list or block-scalar disallowedTools;
  • a nested agent in an agents/ subfolder that needs adapting, or any subfolder when a top-level agent needs adapting;
  • a manifest agents field the script did not write.

The cleanup of .claude-plugin/ for plugins deleted upstream now only runs when that folder is all that is left, so a Claude-native plugin upstream ships is not deleted.

CRLF, a byte-order mark, trailing space on the closing fence and frontmatter at end of file are handled, and line endings are kept.

Upstream files are untouched. The README template and the sync-fork skill describe the new location.

CI

  • validate-plugins.yml is restored to upstream byte-for-byte. Any fork edit there is lost on the next upstream-wins conflict.
  • New fork-only fork-checks.yml runs the converter tests and fails if regenerated files differ from the committed ones. It skips drafts and triggers on scripts/**, */agents/**, */.claude-plugin/** and itself.
  • Sync PRs auto-merge, and those opened with GITHUB_TOKEN trigger no workflows. Before it pushes, sync-upstream.yml therefore runs these checks on the merged tree:
    • the converter tests;
    • upstream's validate-plugins.mjs;
    • generate-pstack-harness-trees.mjs --check, when that script exists.
  • The token. GITHUB_TOKEN cannot push commits that change .github/workflows, and workflows is not a permission it can be granted. Upstream's 182f2ce (Oct 16) changes validate-plugins.yml, so the sync now:
    • checks out with persist-credentials: false, so the steps that run upstream's scripts hold no token;
    • pushes, and opens the PR, with an optional SYNC_TOKEN secret: a fine-grained token for this repo with Contents, Pull requests and Workflows set to read and write. Without that secret it falls back to GITHUB_TOKEN.
    • Without SYNC_TOKEN, if any commit in origin/main..HEAD touches .github/workflows, the run fails with an error naming the secret to add.
    • The check runs per commit, because a push is refused when any commit in it touches workflows, even if the net diff is clean.
    • It uses git log --full-history, because default history simplification hides an upstream touch-then-revert.
    • Git hooks are disabled for the push.
  • Accepted risk. Earlier steps in the job run upstream code, which could reach the push step through the runner (GITHUB_ENV, PATH). That grants nothing upstream lacks, since its content reaches main through this unattended sync anyway. A separate push job was judged not worth the complexity. Give the token an expiry.
  • Failure report. A failed run opens a sync-failed issue, or comments on the open one; the next successful sync closes it. The sync was red for 14 days without anyone seeing it.

Verification

  • Tests and mutants. node --test scripts/claude-agents.test.mjs: 112/112 pass. All 87 mutants of claude-agents.mjs are killed:
    • every guard, early return, fallback, regex flag and set member the review named;
    • three equivalent mutants dropped, two of them by deleting the redundant code.
  • Regeneration. node scripts/sync-manifests.mjs is idempotent. Drift-check control: it exits 0 on a clean tree and 1 with a hand-edited generated file committed.
  • Sync dry run against today's upstream (d457567).
    • The merge conflicts on marketplace.json, README.md, validate-plugins.yml and validate-plugins.mjs.
    • Replaying the workflow's resolve, sync, rebuild and gate steps succeeds: 17 plugins, tests pass, All plugins validated successfully. and harness/pstack is up to date.
    • The stricter key checks raise nothing on any upstream agent.
    • The push guard fires without a token on this range and passes with one.
    • claude plugin validate passes on the merged tree. Its only warnings are the existing README install-line and version advisories.
  • Deletion guard. A test runs sync-manifests.mjs on a temporary root: a plugin dir holding only .claude-plugin/ is cleaned, and one with other content is kept. Removing the guard turns the test red.
  • Push guard on a scratch repo with a real merge. With an upstream range that touches a workflow and reverts it, plain git log reports 0 files and --full-history reports 2. With a file-only range it reports 0.
  • Lint. actionlint passes on both workflows, apart from one SC2129 style note that already exists on main.
  • Live check with Claude Code 2.1.303 (claude -p --plugin-dir …):
    • all four agent-compatibility reviewers list (Tools: All tools except Edit, Write, NotebookEdit, Agent);
    • pstack shows poteto-agent and Comment Sicko once each;
    • the debug log has no frontmatter warnings.

Not covered

  • advisor-subagent asks for a Grok model; in Claude Code it now runs on the default model.
  • background: true is checked only for ordinary subagents. Agent-team teammates were not exercised.
  • No SYNC_TOKEN is set yet. Until it is, a sync whose range touches workflows fails loudly and opens an issue.
  • Not tested:
    • the authenticated push itself, which runs only in Actions;
    • whether auto-merge lands on a PR that changes workflows.
  • The workflow shell (conflict resolution, gate order) has no automated test; it is covered by the dry run only.
  • Plugins whose manifest lists agents may show "default agents/ folder ignored" in /plugin. This is cosmetic and is checked after install.
  • A frontmatter line whose prose ends in readonly: or is_background: fails the sync. No current upstream agent does this; it is the cost of failing closed.
  • A hand-written manifest agents list in the generated shape is replaced by the full list. That is a superset, so no agent is lost.
  • README staleness is not checked in CI. The author field formatting churn is left as is.

back1ply and others added 2 commits October 16, 2026 18:25
Upstream cursor/plugins now ships its own .claude-plugin/marketplace.json
(origin-apps), which add/add-conflicts with this fork's generated one.
The conflict handler rebuilt README.md before that conflict was resolved,
so rebuild-readme.mjs crashed parsing conflict markers and every daily
sync has failed since 2026-10-06.

Resolve marketplace.json and README.md with --ours; the existing
"Sync manifests" and "Rebuild README" steps regenerate both from the
merged tree. Taking upstream's marketplace.json would also have renamed
the marketplace from cursor-plugins-claude to cursor-plugins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Upstream agents use readonly, is_background and model: fast, which Claude
Code ignores, so the agent-compatibility reviewers and plugin-architect ran
with Edit and Write. sync-manifests now writes adapted copies under
<plugin>/agents/claude-code/ and lists every agent in the plugin manifest;
upstream files stay untouched, so the sync workflow's upstream-wins conflict
rule cannot undo it. CI runs the converter tests and fails when the
generated files are stale, and skips draft PRs.
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one): 1
  • sync

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: SmailG/claude-cursor-plugins/.coderabbit.yml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5c65898f-82e0-4f65-83f6-da314fbda616

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Move generated copies out of agents/ so upstream's pstack harness walk
never sees them, mark the folder as generated, deny Agent too, parse
quoted, cased, commented, CRLF and BOM frontmatter, merge an existing
disallowedTools, and throw on anything the converter cannot read.
Restore validate-plugins.yml to upstream and run the fork's checks from
fork-checks.yml and a gate step in sync-upstream.yml.
@SmailG SmailG changed the title fix: generate Claude Code agent copies for Cursor-only frontmatter fix: Claude Code agent frontmatter and the failing upstream sync Oct 19, 2026
Throw on Cursor keys hidden in other YAML shapes, in nested agents and on
duplicate keys; read only YAML 1.2 booleans; refuse to delete or replace
files and agents fields the generator did not write. The sync workflow
keeps tokens away from upstream scripts, pushes with an optional
SYNC_TOKEN (GITHUB_TOKEN cannot push workflow changes), validates the
merged tree, and opens an issue when a run fails.
@SmailG
SmailG force-pushed the fix/claude-agent-frontmatter branch from ff73c2f to 9b02a5a Compare October 19, 2026 14:29
Guard pushes with git log --full-history so a touch-then-revert range
still needs SYNC_TOKEN, disable hooks for the push, and close the
failure issue after a successful sync. Deny writes for a quoted false,
throw on escaped or mixed-shape Cursor keys and block-scalar
disallowedTools, and test the cleanup loop in sync-manifests.
@SmailG
SmailG marked this pull request as ready for review October 19, 2026 14:43
@SmailG
SmailG merged commit 2157661 into main Oct 19, 2026
3 checks passed
@SmailG
SmailG deleted the fix/claude-agent-frontmatter branch October 19, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants