{"owner":"EveryInc","repo":"compound-engineering-plugin","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","GEMINI.md"],"skills":{"AGENTS.md":"# Agent Instructions\n\nThis repository is the root of the `compound-engineering` coding-agent plugin and the marketplace/catalog metadata used to distribute it.\n\nIt also contains:\n- the Bun/TypeScript CLI that converts Claude Code plugins into other agent platform formats\n- shared release and metadata infrastructure for the CLI, marketplace, and plugin\n\n`AGENTS.md` is the canonical repo instruction file. Root `CLAUDE.md` is a symlink to `AGENTS.md` so Claude Code and other tools that look for `CLAUDE.md` still find it at the expected path. Keep that symlink (do not replace it with a regular file): a real root `CLAUDE.md` makes `claude plugin validate --strict` fail because this checkout is also the plugin root.\n\n## Quick Start\n\n```bash\nbun install\nbun run test              # full test suite (also runs in CI; `--parallel` across worker processes)\nbun run release:validate  # plugin/marketplace consistency (also runs in CI)\nbun run plugin:validate   # Claude marketplace + plugin schema (also runs in CI; needs `claude` on PATH)\n```\n\n### Codex Local Plugin Development\n\nWhen testing current skill files in Codex, run the repository workflow from the checkout or worktree you intend to test:\n\n```bash\nbun run codex:dev -- local    # link this worktree's skills and remove CE plugin installs\nbun run codex:dev -- status   # show local/remote state and checkout provenance\nbun run codex:dev -- remote   # restore the official marketplace-backed plugin\nbun run codex:dev -- remove   # remove both supported CE installation surfaces\n```\n\n`refresh` is an idempotent alias for `local`. Local mode manages only the exact `$CODEX_HOME/skills/compound-engineering-local` symlink and Compound Engineering plugin IDs; it must not alter unrelated user skills. The symlink includes modified and untracked files from the selected worktree. Start a new Codex session after switching installation modes. Current Codex versions detect direct skill edits automatically; restart only if an edit does not appear. For live local testing, use this workflow instead of adding the repository as a marketplace: a marketplace install caches a snapshot, while local mode links the current skill files.\n\n## Working Agreement\n\n- **Branching:** Create a feature branch for any non-trivial change. If already on the correct branch for the task, keep using it; do not create additional branches or worktrees unless explicitly requested.\n- **Merge policy:** All changes to `main` go through pull requests. Direct pushes and direct merges are not allowed; branch protection on `main` enforces this by requiring the `test` status check to pass. The direct path bypasses `release:validate`, the test suite, and PR title validation — past direct merges have caused version drift requiring multi-PR recovery (see `docs/solutions/workflow/release-please-version-drift-recovery.md`).\n- **Contribution gate (non-maintainers):** If you are not a repository maintainer or admin, do not open a PR without a linked issue — file the issue first and reference it from the PR. Adding a **new skill** has a stricter gate: non-maintainers and non-admins must raise a discussion in an issue and get explicit maintainer approval **before** starting the work; do not open a new-skill PR that has not been approved this way. Maintainers and admins are exempt from both gates but still follow the merge policy above.\n- **PR disclosure:** `.github/pull_request_template.md` ends with `## Security Disclosure` and `## Agent Disclosure` sections. Fill both when opening a PR — including PRs authored via `gh pr create --body`/`--body-file`, which bypass the template so nothing pre-fills them. State any security-relevant changes (or \"No security-relevant changes\"), and the model that did the bulk of the work — your harness plus the most specific model identity your own context gives you, e.g. `Claude Code · claude-opus-4-8` or `Codex CLI · GPT-5`. Copy an exact model ID verbatim when your harness states one; when it exposes only a generic family, report the family and stop. Measured 2026-07-24: Codex and Cursor agents cannot see their running model at all (Codex's \"based on GPT-5\" is fixed boilerplate), so do not upgrade a family to a version, and do not read config files for one — the configured default is often not the model actually running. Never invent a version or variant. The body above those sections stays freeform — add whatever sections best explain the change.\n- **Safety:** Do not delete or overwrite user data. Avoid destructive commands.\n- **Testing:** Run `bun run test` after changes that affect parsing, conversion, output, skill conventions, or other mechanical guards. Local `bun run test` is the same suite CI runs — there is no separate local-only unit-test lane. Prefer it over bare `bun test`: the package script carries `--parallel`, which is where the suite's speed comes from. Bare `bun test <file>` is still the right tool for iterating on one file.\n- **Release versioning:** Releases are prepared by release automation, not normal feature PRs. The repo has one root plugin/package release component (`compound-engineering`) plus marketplace components (`marketplace`, `cursor-marketplace`). GitHub release PRs and GitHub Releases are the canonical release-notes surface for new releases; root `CHANGELOG.md` is only a pointer to that history. Use conventional titles such as `feat:` and `fix:` so release automation can classify change intent, but do not hand-bump release-owned versions or hand-author release notes in routine PRs.\n- **Output Paths:** Keep OpenCode output at `opencode.json` and `.opencode/{agents,skills,plugins}`. For OpenCode, commands go to `~/.config/opencode/commands/<name>.md`; `opencode.json` is deep-merged (never overwritten wholesale).\n- **Scratch Space:** Default to OS temp. Use `.context/` only when explicitly justified by the rules below.\n  - **Default: OS temp** — covers most scratch, including per-run throwaway AND cross-invocation reusable, regardless of whether a repo is present or whether other skills may read the files. A stable OS-temp prefix handles cross-skill and cross-invocation coordination equally well as an in-repo path; repo-adjacency is rarely the relevant property.\n    - **Per-run throwaway**: `mktemp -d \"${TMPDIR:-/tmp}/<prefix>-XXXXXX\"` (OS handles cleanup). Use for files consumed once and discarded — captured screenshots, stitched GIFs, intermediate build outputs, recordings, delegation prompts/results, single-run checkpoints. Always pass an explicit template under `${TMPDIR:-/tmp}`. Do not use bare `mktemp`, bare `mktemp -d`, `mktemp -t`, or `mktemp -d -t`: those forms ignore `$TMPDIR` on macOS and can resolve outside a sandbox's writable temp directory.\n    - **Cross-invocation reusable**: use a stable, effective-user-owned prefix under `/tmp/compound-engineering-<effective-uid>/<skill-name>/` — **not** `mktemp -d` — so later invocations by the same OS user can find prior outputs without sharing a writable root with other users. Derive the effective UID with `id -u`, reject a symlink or path not owned by the current user, and create or repair the top-level root to mode `0700` before use. **Probe before committing to `/tmp`:** when that root cannot be created, is not yours, or is not writable, use `${TMPDIR:-/tmp}/compound-engineering-<effective-uid>` instead — the same rule, in the same order, in every shell preamble and Python default, so a later invocation resolves the same root. Claude Code's macOS sandbox allowlists writes under `$TMPDIR` (`/tmp/claude-<uid>`) but not `/tmp` itself, so without the fallback every skill's scratch setup aborts there with `Operation not permitted`; and an existing root from an unsandboxed session passes `mkdir -p` as a no-op yet refuses the first write, which is why the probe is a writability check (`[ -w ]`), not creation alone. Copy the block from any shipped skill (for example `skills/ce-compound/SKILL.md`) rather than re-deriving it; `tests/scratch-root-preamble-executes.test.ts` runs every copy, including the fallback. The default layout is one `<scratch-root>/<skill-name>/<run-id>/` directory per run; use it for caches keyed by session, checkpoints meant to survive context compaction, intermediate state, and outputs whose lifecycle or mutation belongs to one run.\n      - **Discoverable collection exception**: omit the per-run directory only when later invocations intentionally enumerate multiple sibling **final artifacts** as core product behavior and run isolation would materially worsen discovery or the user-facing path. Use a stable collection namespace (for example, repository identity plus a `general` fallback), descriptive immutable filenames, metadata that supports ranking, and no-overwrite collision handling that atomically reserves the final filename and retries with the next suffix on collision; never check availability and then write. Do not use this exception for caches, checkpoints, intermediate files, or merely to shorten a path.\n      - Prefer `/tmp` over `$TMPDIR` so paths stay accessible: `$TMPDIR` on unsandboxed macOS resolves to `/var/folders/64/.../T/`, which is hostile for users who want to inspect checkpoints, grep them, or copy them out — which is why `$TMPDIR` is the fallback, taken only when `/tmp` cannot host the root, and never the first choice. The explicit effective-UID segment supplies the required cross-user boundary while preserving a readable path. Agents running as the same OS user intentionally remain in one discretionary-access-control principal.\n  - **Exception: `.context/`** — use only when the artifact is genuinely bound to the CWD repo AND meets at least one of:\n    - (a) **User-curated**: the user is expected to inspect, manipulate, or manually curate the artifact outside the skill (e.g., a per-repo TODO database, a per-spec optimization log that survives across sessions on the same checkout).\n    - (b) **Repo+branch-inseparable**: the artifact's meaning is inseparable from this specific repo or branch (e.g., branch-specific resume state that a user expects to pick up again in the same checkout).\n    - (c) **Path is core UX**: surfacing the artifact path back to the user is a core part of the skill's output and that path is easier to communicate as a repo-relative location than an OS-temp one.\n    Namespace under `.context/compound-engineering/<workflow-or-skill-name>/`, add a per-run subdirectory when concurrent runs are plausible, and decide cleanup behavior per the artifact's lifecycle (per-run scratch clears on success; user-curated state persists). \"Shared between skills\" is not by itself sufficient — OS temp handles that equally well.\n  - **Durable outputs** (plans, specs, learnings, docs, final deliverables) belong in `docs/` or another repo-tracked location, not in either scratch tier.\n  - **Cross-platform note:** `/tmp` is writable on macOS (symlink to `/private/tmp`), Linux, and WSL. For per-run throwaway files, use an explicit `${TMPDIR:-/tmp}` template so macOS and sandboxed hosts honor the selected temp parent. Skills authored here assume Unix-like shells (bash on macOS/Linux, or Git Bash on Windows). Native Windows is a supported target for Python interpreter resolution and peer-job detach — never hardcode `python3`; probe execution per `docs/solutions/conventions/resolve-python-interpreter-not-python3.md`.\n- **Character encoding:**\n  - **Identifiers** (file names, agent names, command names): ASCII only -- converters and regex patterns depend on it.\n  - **Markdown tables:** Use pipe-delimited (`| col | col |`), never box-drawing characters.\n  - **Prose and skill content:** Unicode is fine (emoji, punctuation, etc.). Prefer ASCII arrows (`->`, `<-`) over Unicode arrows in code blocks and terminal examples.\n\n## Directory Layout\n\n```\nsrc/              CLI entry point, parsers, converters, target writers\nskills/           Compound Engineering plugin skills\n.claude-plugin/   Claude plugin manifest and marketplace catalog metadata\n.codex-plugin/    Codex plugin manifest\n.cursor-plugin/   Cursor plugin manifest and marketplace catalog metadata\n.opencode/        OpenCode package entrypoint and install docs\n.pi/              Pi extension entrypoint\ntests/            Converter, writer, and CLI tests + fixtures\ndocs/             Requirements, plans, solutions, and target specs\nCONCEPTS.md       Shared domain vocabulary (glossary of project-specific terms)\n```\n\n## Repo Surfaces\n\nChanges in this repo may affect one or more of these surfaces:\n\n- root plugin content under `skills/`, `AGENTS.md`, `README.md`, and platform manifests\n- marketplace catalogs under `.claude-plugin/`, `.cursor-plugin/`, and `.agents/plugins/`\n- the converter/install CLI in `src/` and `package.json`\n\nDo not assume a repo change is \"just CLI\" or \"just plugin\" without checking which surface owns the affected files.\n\n## Plugin Maintenance\n\nWhen changing plugin content:\n\n- Update substantive docs like `README.md` when the plugin behavior, inventory, or usage changes.\n- When adding a user-facing skill, document it: create a `docs/skills/<skill-name>.md` page (purpose, novel mechanics, when to use, chain position — follow the shape of the existing pages) and add a catalog row under the right category in `docs/skills/README.md`, alongside the root `README.md` inventory row and the skill-count bump in `tests/release-metadata.test.ts`. Keep these in sync when a skill's purpose or inventory changes. This is convention, not yet validated by a test, so it is easy to miss — most skills have a page; the few that don't (e.g. `lfg`, `ce-dogfood-beta`) are the exception, not the rule.\n- When adding, removing, renaming, or changing the meaning/default/consumer of a `.compound-engineering/config.yaml` option, update `skills/ce-setup/references/config-template.yaml`, its byte-identical `.compound-engineering/config.example.yaml` copy, the centralized `docs/skills/configuration.md` reference, and the affected consumer skill docs in the same change. Ordinary keys may also live in optional checkout-local `config.local.yaml` (overrides the repo file). `docs_root` belongs only in `config.yaml`. Durable team instructions still belong in the project's normal agent-instructions mechanism.\n- Do not hand-bump release-owned versions in plugin or marketplace manifests.\n- Do not hand-add release entries to `CHANGELOG.md` or treat it as the canonical source for new releases.\n- Run `bun run release:validate` if agents, commands, skills, MCP servers, or release-owned descriptions/counts may have changed.\n- When removing a skill, agent, or command, add its name to both cleanup registries so stale flat-install artifacts are swept on upgrade:\n  - `STALE_SKILL_DIRS` / `STALE_AGENT_NAMES` / `STALE_PROMPT_FILES` in `src/utils/legacy-cleanup.ts`\n  - `EXTRA_LEGACY_ARTIFACTS_BY_PLUGIN[\"compound-engineering\"]` in `src/data/plugin-legacy-artifacts.ts`\n\nUseful validation commands:\n\n```bash\nbun run release:validate\ncat .claude-plugin/marketplace.json | jq .\ncat .claude-plugin/plugin.json | jq .\n```\n\n## Runtime vs Authoring Context\n\n`AGENTS.md`, `CLAUDE.md` (symlink to `AGENTS.md`), and `GEMINI.md` are authoring context for this source repository. Skills are installed into end-user environments, where they run against the user's local instruction files, not this repo's. Behavioral rules that must affect a skill at runtime belong in that skill's `SKILL.md` or files under its own `references/` directory.\n\n## Working on Skills\n\nThis repository authors each skill once and distributes it across multiple agent models and harnesses. A skill is a set of goals, not a state machine: it hands the agent the goal, the done condition, the safe failure direction, and the facts it cannot derive from the repo in front of it, then gets out of the way. The reading agent is expected to reason to the mechanics from there. Before creating, materially revising, or reviewing a skill or skill-local persona, read `docs/solutions/skill-design/portable-agent-skill-authoring.md`, applying only the sections relevant to the skill; the rules in this file supplement it and take precedence where they are more specific. The four activities below — authoring, editing, reviewing, and acting on review — are one lifecycle and share one standard, so a rule stated for one applies to the others unless it says otherwise.\n\n### Authoring a new skill\n\n- **State conditions, not procedures.** Write what must be true (goal, done, safe direction) and the non-derivable facts. When you find yourself adding cases to a block to make it hold, name the condition those cases are proxies for and state that instead. Read `docs/solutions/skill-design/skill-gates-state-conditions-not-prescribed-git-commands.md` for the canonical failure.\n- **Prescribe a mechanism only where it is owned.** A skill spells out commands, exit semantics, or state transitions for the mechanics it owns (`ce-commit-push-pr` owns PR detection) and for deterministic work that is cheap to compute but costly to reason to (a data-processing script, a path anchor). A skill that delegates that work states the condition that must hold, the safe failure direction, and the non-derivable facts about the callee -- never a re-derivation of the callee's commands. Prescribed commands in a delegating skill encode one configuration and are wrong for the next one.\n- **Prose admission.** Keep a line only when it states a falsifiable constraint, counters a known default tendency or observed shortcut, or supplies domain knowledge that materially changes a decision. Do not keep vague effort or quality language (\"be thorough\") as a standalone instruction; replace it with an observable rule, or retain a targeted effort cue only when it counters a documented runtime tendency and has been evaluated there. Do not append motivational rationale to a directive that already stands on its own. Repeat an instruction only at a demonstrated drift point where placement changes whether it fires, and protect genuinely required always-loaded duplicates with a parity test.\n- **User-facing invocations.** Keep agent-to-agent or skill-to-skill routing semantic: format formal skill names as inline code (for example, `ce-plan`) and invoke the named skill through the active harness's callable skill mechanism. When a skill prints or copies a user-runnable invocation, default to `/skill-name`; use `$skill-name` only when the active harness is Codex or explicitly documents dollar-prefixed skill invocation. On oh-my-pi (`omp`), keep the default form for model-visible targets; use native `/skill:<name>` only when the target is not model-visible because it declares `disable-model-invocation` or `hide` (for example, `/skill:ce-polish`). In prose, render only the invocation as inline code; use a fenced block only when the command stands alone. Output exactly one form. Do not apply this rendering rule to built-in commands such as `/goal`. At runtime, put the smallest self-contained rendering rule immediately before the smallest section that contains all affected user-copy seams; do not repeat it in every step, only in a separately loaded reference that independently owns output.\n- **Loading and placement.** Keep every load-bearing action, route, and reference-load instruction inline at the point where it must fire (`docs/solutions/skill-design/post-menu-routing-belongs-inline.md`). Do not inline a summary complete enough to suppress loading the authoritative reference. Extract a block to `references/` when it is conditional or late-sequence and a meaningful share of the skill (~20%+), replacing it with a 1-3 line condition and backtick path. Never use `@` for an extracted block; it inlines at load time and defeats extraction.\n\n### Editing an existing skill\n\nSkills predate the current authoring standard and evolve toward it; the standard is the guide above, not the text around your edit.\n\n- **Bring the block you touch up to the standard.** When a change lands in a block written as a procedure, a menu, or an enumeration of cases, restate that block as its conditions as part of the change rather than matching the old shape. Matching a neighboring procedure because it is there is how procedures propagate.\n- **Scope the rewrite to your change plus what it makes wrong.** Reconcile the blocks your change contradicts or duplicates; leave untouched blocks alone even when they fall short of the standard, and name them in the PR as follow-up. A repo-wide modernization is its own change, requested explicitly.\n- **Repeated case-specific repair is the defect signal.** When a block keeps absorbing \"add the case we just found\" -- in authoring, in a review round, or in your own fix to a finding -- the representation is wrong, usually a procedure that should be a condition. Delete the additions and restate the goal, then re-verify the shorter rule against every path the additions served: a condensed rule that no longer names a path is a new defect, not a simplification.\n\n### Reviewing a skill change (bots and humans)\n\nReview bots read this file when reviewing a PR here. On `skills/**`:\n\n- **A finding is a gap in the goal, the done condition, or the safe failure direction, or a mechanism at the wrong owning layer** — commands prescribed in a skill that delegates that work, a rule placed where it will not fire, a Claude-only construct in a cross-host skill, a rendering that breaks on another harness.\n- **A case a stated condition already covers is not a finding.** Before filing \"what if X\" against a rule, check whether the rule's condition decides X. \"Rename only on positive proof the branch was never published; any other result keeps the name\" already decides an unreachable remote. If it does, do not file; if the condition is wrong or missing, file *that*.\n- **State the requested fix as a condition or an owning-layer move, never as a case to add.** \"This probe fails open on network error\" is a correct observation; the fix to request is \"state the condition\" or \"delete the probe\", not \"also check the exit code\". \"Command X fails in state Y\" against a delegating skill is a finding about the representation; the fix is to drop the command and state the condition, not to correct the command.\n- **A block restated to the standard is the expected shape of an edit**, not scope creep, when the restatement covers every path the old text served.\n- Ordinary code under `src/`, `tests/`, and `scripts/` gets ordinary code review; these rules are about instruction prose.\n\n### Acting on review feedback\n\nApplying review, peer, or eval feedback to a skill is a material revision under the same standard. An item is not addressed because a sentence landed; it is addressed when a demonstrated gap is closed at its owning layer by the smallest mechanism. Skill prose is not code: a natural-language instruction can always be made more specific, so a reviewer can produce a valid-looking edge case against any condition indefinitely, and patching each one dilutes the instruction. Measured 2026-08-15 (#1397): a two-condition setup step absorbed 24 bot findings over nine rounds, most of them cases against text the previous round had added, before being restated as the two conditions it began as. Before editing:\n\n1. **Evidence** — classify each item as Change, Verify, or Consider using the guide's evidence rules. Do not edit the skill for Verify or Consider items. A case the stated condition already decides is Verify at most: answer it with the condition (`not-addressing` quoting it, or `replied` for a question), do not patch. \"Default to fixing\" is the right rule for code; on skill prose the default for a case-level finding is to point at the condition.\n2. **Owning layer** — for each Change, identify its owning layer: activation contract, outcome spine or skill boundary, runtime protocol, loading or placement, deterministic enforcement, or shared authoring rule. Several fixes in #1397 belonged in the callee skills (`ce-commit`, `ce-commit-push-pr`), not in the calling skill's prose.\n3. **Mechanism** — fix the gap at its owning layer. Add prose only when it is the smallest mechanism that closes the gap, and then only the smallest falsifiable unit per prose admission.\n4. **Reconcile** — reread the affected block; remove or rewrite text the change makes conflicting, duplicated, or obsolete. Resolve conflicting feedback items rather than stacking both.\n5. **Stop the accretion loop** — when a finding targets text an earlier round added, delete or restate that addition rather than qualifying it. On the second round against the same block, stop patching: restate the block as its goal, done condition, and safe direction and re-verify against every path the additions served. This holds whether the rounds arrive in one review or across a babysit loop's re-invocations.\n\nWhen evidence shows the same cause across skills, fix the shared guide, rule, or mechanism unless the skills' contracts materially differ. For a multi-item round, record one line per item in the existing PR body or work note: `item -> Change|Verify|Consider | owning layer | mechanism - why`. A single-item fix still follows the steps above; the written line is optional. Reviewer wording is a hypothesis about mechanism, not authority over it — the reviewer's one-line prose fix is sometimes exactly right.\n\n## Referencing Project Conventions in Skills\n\nWhen a skill needs to discover a project convention at runtime — the issue tracker, coding standards, commit format, lint command, scope constraints, etc. — describe **what to look for in the agent's existing context**, not **which file to open**.\n\n**On the read path, do not name instruction files (`AGENTS.md` / `CLAUDE.md` / `GEMINI.md` / `.cursor/rules`).** Phrase it as \"the project's active instructions and conventions already in your context.\" Three reasons:\n\n- **Redundant.** Every major harness auto-injects the project's root instruction file into context at session start (Claude Code loads `CLAUDE.md`, Codex `AGENTS.md`, Gemini `GEMINI.md`). Telling the agent to \"read `AGENTS.md`\" asks it to re-open content it already has.\n- **Brittle / not portable.** The filename differs per harness, and this plugin is authored once and converted to all of them. A hardcoded \"read `AGENTS.md` (or `CLAUDE.md`)\" silently finds nothing on a harness that uses a different name.\n- **Security smell.** Instructing an agent to go *read named instruction dotfiles* is the exact shape that prompt-injection defenses in some agent frameworks (e.g., Hermes) flag. Referencing context rather than filenames avoids tripping those guards.\n\n**Name a concrete file only where the skill must do something a context reference can't express:**\n\n- **Writing a convention back** (e.g., persisting `project_tracker: linear`) needs a target — name it minimally and as an example (\"the project's root agent-instructions file, e.g., `AGENTS.md`; if it `@`-includes another, write to the substantive one\").\n- **Reading content that is genuinely not auto-loaded** — a subdirectory-scoped instruction file governing the area being changed, an optional project doc like `STRATEGY.md` / `CONCEPTS.md` / `README.md`, or any file a *fresh subagent* (which does not inherit the parent's loaded instructions) must open to do its job. Auditing tools that must enumerate every standards file (e.g., `ce-code-review`'s project-standards reviewer globbing all `CLAUDE.md`/`AGENTS.md`) are a legitimate exception — they review the files, they don't re-read them for context.\n\n**Describe the capability, not the tool.** Pair this with naming the *category* of thing rather than a closed set: \"the project's issue tracker (e.g., GitHub Issues, Linear, Jira)\" and \"whatever interface that tracker exposes (connector/MCP, documented API, or a documented CLI)\" — never assume a specific CLI exists, and never treat a missing binary / env var / MCP server as proof the capability is unavailable.\n\n## Validating Agent and Skill Changes\n\nBehavioral changes to a plugin skill or skill-local persona (anything under `skills/`) need a different validation path than mechanical code changes, because of how Claude Code loads plugins.\n\n- **Use the `skill-creator` skill to test changes.** Skill-creator is purpose-built for this: it spawns a generic subagent and injects the agent or skill content into the subagent's prompt at dispatch time, so each run reads the current source from disk. Invoke `/skill-creator` and use its eval workflow rather than reaching for ad-hoc workarounds.\n\n- **Plugin agent and skill definitions both cache at session start.** Once a Claude Code session is open, dispatching a typed plugin agent runs the in-memory copy that was loaded when the session began. The same applies to skills: invoking a skill goes through the cached skill loader, so edits to skill scripts are also not tested via that path. File edits to either layer after session start do not propagate within the same session. Any iteration loop built around typed-agent dispatch or Skill-tool invocation in the same session is testing pre-edit content, not your changes.\n\n- **Do NOT edit `~/.claude/plugins/cache/` or `~/.claude/plugins/marketplaces/` to try to force a reload.** Those paths are user machine state, not repo-managed. Modifying them does not reliably bypass the in-session cache (it didn't, in observed behavior), risks being silently overwritten by plugin updates, and is the wrong layer to test from. The skill-creator pattern is the proper approach; if you genuinely need fresh-loaded behavior of the typed-agent dispatch path, restart the Claude Code session — but skill-creator is preferred for fast iteration.\n\n- **A version-matched cache is not automatically stale — confirm by content, not by version.** When this working tree is the local marketplace source, a session (re)start re-copies it into `~/.claude/plugins/cache/.../compound-engineering/<version>/` (a plain copy, no `.git`; `<version>` is the working tree's `.claude-plugin/plugin.json` version), so the loaded plugin can be identical to — and as current as — your edits. Do not assume the running copy is stale just because it lives under the cache path; equally, do not assume a matching `<version>` means it includes your latest change. Version match is necessary but not sufficient: edits within a release do not bump the version, so a matching segment proves only that the cache was built from this release, not that it captured your most recent edit. To know which copy is actually loaded, diff the specific cache file against the working-tree file — identical means the running plugin is your current edit and you can trust it; differing means the session predates the edit, so restart (or use skill-creator). Never infer \"stale\" or \"current\" from the version segment alone.\n\n- **Mechanical changes do not have this restriction.** Skill scripts (e.g., `extract-metadata.py`), parser logic, conversion code, and anything `bun test` exercises always run the current source. The caching issue only affects LLM-driven skill prose behavior dispatched through the plugin loader.\n\n## CI and Quality Gates\n\nPR CI (`.github/workflows/ci.yml`) is the merge gate. It runs, in order: PR-title lint (PRs only), `bun run release:validate`, `bun run plugin:validate`, and `bun run test`. Do not invent a parallel local-only mechanical suite — if a check is deterministic and should block merges, put it in one of those steps (usually `bun run test`).\n\nThe `test` script runs `bun test --parallel`, which distributes test *files* across worker processes (one file still runs its own tests serially, and `--parallel` implies `--isolate`). This is the single biggest lever on CI wall time, because most of the suite is spent blocked on subprocesses — `python3`, `bash`, `git`, and `bun run src/index.ts` — not on CPU. Keeping it in the package script rather than the workflow means CI and a contributor's local run cannot drift apart.\n\nThat makes cross-file isolation load-bearing rather than incidental: a test file may not depend on another file's leftovers, and any test that writes outside its own `mktemp` directory is a latent flake. There are no exceptions — a test that needs a dirty tree builds a throwaway git repo for it.\n\n**A test that runs a bundled script which inspects the repository must point that script at a throwaway repo, never this checkout.** Otherwise the developer's uncommitted work becomes test input. `tests/skills/ce-code-review-cross-model-routes.test.ts` ran the real review script against `git diff HEAD` in the checkout, so any uncommitted change over roughly 160KB crossed the script's large-diff threshold and failed 31 of its tests for reasons unrelated to the change under test. CI never saw it, because CI runs on a committed tree. The fixture pattern is `dirtyFixtureRepo()` in that file: `git init` a temp dir, two commits so `HEAD~1` resolves, then one staged edit.\n\n**Do not pin a worker count.** `--parallel` with no value tracks the runner's core count, which is what you want. Raising it looks free — the suite is idle-bound, so more workers should pack better — but it was measured on CI and it is not: at `--parallel=8` on a 4-core runner, wall time improved ~9% (102s -> 93s) while total test-CPU inflated from 223s to 343s, and five tests crossed the 5000ms default per-test timeout. That converts runner busyness into red builds. A file that legitimately runs for seconds should call `setDefaultTimeout` instead, as the subprocess-heavy suites do.\n\nA file never splits across workers, so an oversized file sets a floor. `tests/skills/ce-work-unit-workspace.test.ts` was 4,564 lines and 86 tests under one `describe`; it is now five `ce-work-unit-workspace-*.test.ts` files sharing `tests/skills/helpers/ce-work-workspace-harness.ts`. Measured with three `workflow_dispatch` runs per ref in the same window, the `Run tests` step went from a median of 88s (87/112/88) to 81s (83/80/81).\n\n**Splitting bought ~8% of CI wall time and most of the run-to-run variance** — baseline spread 25s, split spread 3s. That second effect is the durable one: a 60s serial file makes wall time depend on which worker takes it, so a busy runner produced the 112s outlier. Locally the same split is much larger (160s -> 75s), because a developer machine has enough cores for the long file to be the whole critical path.\n\n**Do not use `bun test --parallel=4` on a many-core laptop as a CI proxy.** It predicted a 26% CI win where the real number was 8%: capping bun to four workers still leaves the other cores absorbing the `git` / `python3` / `bash` subprocess load, so it does not behave like a 4-core runner. Dispatch the real workflow on both refs instead.\n\nAt 81s the suite is nearer CPU-bound on a 4-core runner than bounded by its slowest file, so further file splitting has small returns; `tests/ce-babysit-pr-snapshot.test.ts` (~36s) is the largest remaining file and was deliberately left intact.\n\n**Size a test file by its measured time, not its line count.** A file is a wall-time problem when it approaches the suite's slowest-file ceiling. Roughly a thousand lines under one `describe` is a smell worth measuring, never a threshold to split at on sight — splitting a file that already runs well under the ceiling buys nothing. Get per-file times before deciding:\n\n```bash\nbun test --parallel --reporter=junit --reporter-outfile=/tmp/t.xml\n```\n\nThe `ce-work-unit-workspace-*` shards run 10-23s each against that ~36s ceiling, so two of them sitting just over a thousand lines is fine and they are deliberately left whole. Put shared fixtures in `tests/skills/helpers/`.\n\n### What belongs where\n\n| Kind of check | Where it lives | Notes |\n|---|---|---|\n| Deterministic invariants (frontmatter, parity, path safety, script behavior, converter/writer output, greppable skill contracts) | `bun test` / `release:validate` / `plugin:validate` | Must pass in CI |\n| Skill *prose behavior* (routing judgment, restraint, cross-model peer outcomes) | `skill-creator` eval, local / PR evidence | Not a CI job; non-deterministic and needs a model |\n\nThat split is intentional. See `docs/solutions/skill-design/portable-agent-skill-authoring.md` (\"Evaluate proportionally\"). Mechanical checks belong in CI; behavioral agent evals are best-effort evidence, not an exhaustive CI matrix.\n\n### Right-size new mechanical guards\n\nWhen a review bot or human finds a greppable invariant that `bun test` missed:\n\n1. Prefer **tightening an existing guard** over adding a new suite (e.g. widen a regex that already documents the rule).\n2. Pin the **smallest falsifiable unit** — a token, enum, path, heading, or one fixture that would have failed on the regressing diff. Do not snapshot whole skill bodies or pin incidental wording.\n3. If the failure needs an LLM to judge, keep it in skill-creator; do not fake it as a brittle string test.\n\n### Maintaining `plugin:validate`\n\n- `package.json` `plugin:validate` must validate **both** the marketplace catalog and the plugin manifest, with `--strict` on each. Paths: `.claude-plugin/marketplace.json` and `.claude-plugin/plugin.json`. Do **not** use `claude plugin validate .` — that resolves this repo as a marketplace only (because `.claude-plugin/marketplace.json` exists with `source: \"./\"`) and skips plugin-root checks.\n- CI pins `@anthropic-ai/claude-code` for reproducible schema rules. Bump the pin deliberately when adopting new upstream rules; do not float `@latest`.\n- Root `CLAUDE.md` must remain a **symlink** to `AGENTS.md` (path stays at the repo root where contributors expect it). Upstream warns on a regular-file plugin-root `CLAUDE.md` because it is not loaded as end-user project context; the symlink avoids that warning so `--strict` can stay on. Do not replace the symlink with a regular `@AGENTS.md` shim or relocate the file just for validators.\n- If `--strict` starts failing again on `CLAUDE.md` after an upstream bump, check whether the symlink was materialized into a regular file (Windows/`core.symlinks=false` checkouts) or whether the validator started following symlinks — fix the layout or pin, do not silently drop `--strict`.\n\n### When CI comments look \"stale\"\n\nIf CI claims a deferred warning or a missing gate, reproduce with the **pinned** `claude` version against `.claude-plugin/plugin.json` before treating the comment as current. Marketplace-only validation can hide plugin warnings.\n\n## Coding Conventions\n\n- Prefer explicit mappings over implicit magic when converting between platforms.\n- Keep target-specific behavior in dedicated converters/writers instead of scattering conditionals across unrelated files.\n- Preserve stable output paths and merge semantics for installed targets; do not casually change generated file locations.\n- When adding or changing a target, update fixtures/tests alongside implementation rather than treating docs or examples as sufficient proof.\n\n## Commit Conventions\n\n- **Prefix is based on intent, not file type.** Use conventional prefixes (`feat:`, `fix:`, `docs:`, `refactor:`, etc.) but classify by what the change does, not the file extension. Files under `skills/` and plugin manifests are product code even though they are Markdown or JSON. Reserve `docs:` for files whose sole purpose is documentation (`README.md`, `docs/`, `CHANGELOG.md`).\n- **Type selection — classify by intent, not diff shape.** Where `fix:` and `feat:` could both seem to fit, default to `fix:`: a change that remedies broken or missing behavior is `fix:` even when implemented by adding code, and net additions do not turn a fix into a `feat:`. Reserve `feat:` for capabilities the user could not previously accomplish where nothing was broken. Other conventional types (`chore:`, `refactor:`, `docs:`, `perf:`, `test:`, `ci:`, `build:`, `style:`) remain primary when they describe the change more precisely than either. Heuristic: if a regression test you could write today would have failed *before* the change, it's `fix:`. The user may override this default for a specific change.\n- **Include a component scope.** The scope appears verbatim in the changelog. Pick the narrowest useful label: skill/agent name (`document-review`, `learnings-researcher`), CLI or marketplace area (`cli`, `marketplace`), or shared area when cross-cutting (`review`, `research`, `converters`). Never use `compound-engineering` — it's the entire plugin and tells the reader nothing. Omit scope only when no single label adds clarity.\n- **Never use `!` or a `BREAKING CHANGE:` footer without explicit user confirmation.** These markers trigger release-please's automatic major version bump — a decision the user may not want even when a change is technically breaking. If a change appears breaking, surface that to the user and let them decide whether to apply the marker.\n\n## Adding a New Target Provider\n\nOnly add a provider when the target format is stable, documented, and has a clear mapping for tools/permissions/hooks. Use this checklist:\n\n1. **Define the target entry**\n   - Add a new handler in `src/targets/index.ts` with `implemented: false` until complete.\n   - Use a dedicated writer module (e.g., `src/targets/codex.ts`).\n\n2. **Define types and mapping**\n   - Add provider-specific types under `src/types/`.\n   - Implement conversion logic in `src/converters/` (from Claude → provider).\n   - Keep mappings explicit: tools, permissions, hooks/events, model naming.\n\n3. **Wire the CLI**\n   - Ensure `convert` and `install` support `--to <provider>` and `--also`.\n   - Keep behavior consistent with OpenCode (write to a clean provider root).\n\n4. **Tests (required)**\n   - Extend fixtures in `tests/fixtures/sample-plugin`.\n   - Add spec coverage for mappings in `tests/converter.test.ts`.\n   - Add a writer test for the new provider output tree.\n   - Add a CLI test for the provider (similar to `tests/cli.test.ts`).\n\n5. **Docs**\n   - Update README with the new `--to` option and output locations.\n\n## Specialist Prompt Assets in Skills\n\nThe compound-engineering plugin no longer ships standalone agent definitions under `agents/`. When a skill needs a specialist persona, store it inside that skill directory, usually under `references/agents/` or `references/personas/`, and have the calling skill dispatch a generic subagent with that file's contents in the prompt.\n\nInternal prompt asset file names should be descriptive and unprefixed because they are not externally exposed agent names.\n\nExample:\n- `references/agents/learnings-researcher.md` (correct)\n- `references/agents/ce-learnings-researcher.md` (wrong for an internal prompt asset)\n\nThese prompt assets must not include YAML frontmatter. Model selection, tool constraints, and dispatch policy belong in the calling skill's `SKILL.md`, not in the prompt asset.\n\n## File References in Skills\n\nEach skill directory is a self-contained unit. A SKILL.md file must only reference files within its own directory tree (e.g., `references/`, `assets/`, `scripts/`) using relative paths from the skill root. Never reference files outside the skill directory — whether by relative traversal or absolute path.\n\nBroken patterns:\n\n- `../other-skill/references/schema.yaml` — relative traversal into a sibling skill\n- `/home/user/compound-engineering-plugin/skills/other-skill/file.md` — absolute path to another skill\n- `~/.claude/plugins/cache/marketplace/compound-engineering/1.0.0/skills/other-skill/file.md` — absolute path to an installed plugin location\n\nWhy this matters:\n\n- **Runtime resolution:** Skills execute from the user's working directory, not the skill directory. Cross-directory paths and absolute paths will not resolve as expected.\n- **Unpredictable install paths:** Plugins installed from the marketplace are cached at versioned paths. Absolute paths that worked in the source repo will not match the installed layout, and the version segment changes on every release.\n- **Converter portability:** The CLI copies each skill directory as an isolated unit when converting to other agent platforms. Cross-directory references break because sibling directories are not included in the copy.\n\nIf two skills need the same supporting file, duplicate it into each skill's directory. Prefer small, self-contained reference files over shared dependencies.\n\n> **Note (March 2026):** This constraint reflects current Claude Code skill resolution behavior and known path-resolution bugs ([#11011](https://github.com/anthropics/claude-code/issues/11011), [#17741](https://github.com/anthropics/claude-code/issues/17741), [#12541](https://github.com/anthropics/claude-code/issues/12541)). If Anthropic introduces a shared-files mechanism or cross-skill imports in the future, this guidance should be revisited with supporting documentation.\n\n## Lean Repo Grounding\n\nUse the project's active instructions already in the main agent's context, then go directly to task-specific current evidence. Pass fresh subagents the relevant project and task context, or have them read the applicable current instruction source when operational rules affect their work. If a task cannot be scoped from that context, use one targeted probe. Do not create a reusable generic repo profile or run a default root, stack, or layout scan.\n\n## Platform-Specific Variables in Skills\n\nThis plugin is authored once and converted for multiple agent platforms (Claude Code, Codex, Gemini CLI, etc.). Do not use platform-specific environment variables or string substitutions (e.g., `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_SKILL_DIR}`, `${CLAUDE_SESSION_ID}`, `CODEX_SANDBOX`, `CODEX_SESSION_ID`) in skill content without a graceful fallback that works when the variable is unavailable or unresolved.\n\nHow a bundled-file reference resolves depends on *who* resolves it and whether a shell is involved, so references fall into three tiers. Do not assume a bare `scripts/…` path behaves the same in all three.\n\n**Tier 1 — Read-time file references (relative, no anchor):** When skill *content* points the agent at a co-located file to read (e.g., \"read `references/schema.yaml`\"), use a relative path from the skill root. The skill loader resolves these against the skill's own directory on all major platforms — no variable prefix needed. This is the rule in *File References in Skills* above.\n\n**Tier 2 — Prose pointers to a bundled file the agent acts on (relative + a \"from this skill's directory\" cue):** When skill prose names a bundled file the agent will use but does *not* put it in an executed shell command (e.g., \"drive the loop with `scripts/hitl-loop.template.sh`\" or \"generate the package with `scripts/review-package BASE HEAD`\"), use a relative path plus an explicit \"from this skill's directory\" phrase. The cue tells the agent what to resolve against without the verbosity of an anchor.\n\n**Tier 3 — Executed shell commands (the `SKILL_DIR` anchor):** When skill content puts a bundled script in a command the agent runs through the Bash tool — a fenced ` ```bash ` block **or** an inline `bash …` / `python …` — anchor it to the skill dir. The Bash tool's working directory is the user's **project**, not the skill directory, on Claude Code, Codex, and Cursor alike, so a bare `bash scripts/my-script.sh` resolves to `<project>/scripts/…`. Relative paths here *often still work* — a capable agent resolves them against the skill dir it loaded (which is how the agentskills.io spec and other ecosystems ship them) — but that relies on the agent translating the path, and the failure mode is a fenced block copied **verbatim** into a Bash call, which runs literally and misses (`exit 127`; recovery is a wasted round-trip that weaker models / mid-tier subagents botch). Anchoring bakes the resolution into the command, so it is **deterministic**. Use the anchor for executed shell as the house default — a conservative choice, not a claim that bare relative *cannot* work (recurring bug class: #764 `ce-worktree`, #811 `ce-code-review`, #898 `ce-compound`):\n\n```\n# set inline in the SAME command (shell state does not persist between Bash calls):\nSKILL_DIR=\"<absolute path of the directory containing the SKILL.md you just read>\";\nbash \"$SKILL_DIR/scripts/my-script.sh\" ARG\n```\n\n**Keep the trailing `;` on the assignment line.** Some hosts (observed on Codex) flatten a fenced multi-line block into a single line by replacing the newline with a space before executing it. Without the `;`, `SKILL_DIR=\"…\"` + newline + `bash \"$SKILL_DIR/…\"` collapses to the env-var-prefix form `SKILL_DIR=\"…\" bash \"$SKILL_DIR/…\"`, where the shell expands `$SKILL_DIR` *before* the prefix assignment takes effect — so it expands to empty and the script path becomes `/scripts/my-script.sh` (`No such file or directory`). The trailing `;` makes the assignment a complete statement that survives flattening; it is load-bearing, not a style choice, so do not remove it.\n\nAn existence guard (`if [ -f \"$SKILL_DIR/scripts/my-script.sh\" ]; then … else echo \"not found — re-check the SKILL.md path\"; fi`) is optional — useful when there's a real fallback, but see the permission caveat below before guarding a pinned call.\n\n`SKILL_DIR` is a **model-filled** value, not a harness variable: every harness loads SKILL.md from a real absolute path the agent knows, so the skill instructs the agent to set `SKILL_DIR` to that directory. This works identically on Claude Code, Codex, and Cursor precisely because it depends on no host-specific variable — `SKILL_DIR`, `CLAUDE_SKILL_DIR`, `CODEX_SKILL_DIR`, `AGENT_SKILL_DIR` are **not** env vars on any of them, yet the script runs because the agent supplies the path. This is the production pattern used by widely-installed cross-host skills (e.g. `last30days`). Two constraints: (1) shell state does **not** persist between separate Bash-tool calls, so `SKILL_DIR` cannot be set once and reused — each invocation must carry the absolute path (set it inline in the same command). (2) A script that needs its *own* directory (to read a sibling file) derives it from `BASH_SOURCE`, not `SKILL_DIR`, since `SKILL_DIR` is the orchestrator's shell var and is not exported to the child process — see `skills/ce-code-review/scripts/cross-model-adversarial-review.sh` for the reference implementation. `last30days` adopted this anchor for its critical multi-host engine after a path-resolution regression; it is the right tool when a script must run *reliably*, which is why it is the tier-3 default — but tiers 1 and 2 deliberately stay lighter.\n\n**Avoid `${CLAUDE_SKILL_DIR}` here — in this cross-agent plugin it is a footgun, not a neutral alternative.** Every skill in this repo is authored once and installed across Claude Code, Codex, Cursor, and Gemini, and `${CLAUDE_SKILL_DIR}` is a Claude-Code-only SKILL.md *content* substitution (not an env var) that is **empty on every other host**. So a `${CLAUDE_SKILL_DIR}`-guarded call's `then` branch quietly never fires off-Claude — the **genuine silent skip** — and a Claude-only mechanism breaks on Codex/Cursor because the converter doesn't rewrite these paths and the native Codex install loads raw `SKILL.md` (no `ce_platforms` filtering). The model-filled `SKILL_DIR` anchor works on every host, so it is the right replacement wherever a `${CLAUDE_SKILL_DIR}`-guarded executed-shell call exists today (tier 3). Do not reach for `${CLAUDE_SKILL_DIR}` as a \"portable\" option — it isn't. Reach for it only for behavior that is genuinely Claude-Code-only and will *never* run on another harness — which, given this plugin's cross-host install model, is essentially never; treat any new use as a smell to justify or remove.\n\nSo: a skill's *core* behavior **can** live in a bundled script across hosts — invoke it via the `SKILL_DIR`-from-read-path anchor. You no longer need to avoid bundled scripts for portability; anchor them instead. Read-time references (`references/*.md`) still resolve against the skill dir on all targets and need no anchor.\n\n**Permission caveat (Claude Code).** Claude Code's permission checker evaluates every subcommand of a compound command, and a bare `[ -f … ]` test is not pre-approved — so wrapping a pinned `bash \"…sh\"` call in an `if … then … fi` guard defeats a narrow `Bash(bash *…sh)` allow-rule and prompts on every run. If a bundled-script call must stay auto-approved via such a pin, keep it a single pinned command rather than guarding it inline. Note the model-filled `SKILL_DIR` anchor produces a *dynamic* absolute path that won't match a static `Bash(bash /…/scripts/x.sh)` pin regardless of guarding — so for the anchor, expect a one-time approval prompt per distinct command (or use a broader allow-rule); the static-pin trick mainly applies to the fixed `${CLAUDE_SKILL_DIR}` form.\n\n**Do not use `!` load-time pre-resolution in skills.** The `!`cmd`` SKILL.md syntax runs `cmd` at skill load and inlines its stdout, but it is banned here (enforced by `tests/skill-shell-safety.test.ts`) for two unfixable reasons: it runs **only on Claude Code** — on Codex, Cursor, Gemini, and Grok the line is inert literal text — and on Claude Code a command that exits **non-zero aborts skill load** with a user-facing error. Every real use was git context (`git rev-parse …`, `gh pr view …`) whose non-zero exit is a *normal* state (no PR yet, no `origin/HEAD`, detached HEAD, not a repo), so the ordinary case broke the skill. The POSIX guards that force exit 0 (`2>/dev/null || echo SENTINEL`) then fail to parse under Windows PowerShell 5.1, which broke skill load there instead (issue #1066). No single command string both exits 0 on the expected-failure states and parses under both POSIX sh and PowerShell, so the construct cannot be made safe.\n\n**Gather context at runtime instead.** Have the agent run one argv-style command per shell tool call (`git …`, `gh …`) — no `;`, `&&`, `||`, pipes, `$(…)`, or redirects — and interpret each exit status as control flow. This parses identically under POSIX sh and PowerShell because it is a single external-program invocation, and a non-zero exit becomes data the agent reads rather than a load-time abort. See `ce-commit` / `ce-commit-push-pr` for the pattern.\n\n**When a platform variable is unavoidable:** resolve it at runtime with a single shell tool call and include explicit fallback instructions, so the agent knows what to do if the value is empty, a literal command string, or an error — e.g. run `jq -r .version \"${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json\"`; if it resolved to a semantic version use it, otherwise fall back to the versionless behavior. This applies equally to any platform's variables — a skill converted from Codex, Gemini, or any other platform will have the same problem if it assumes platform-only variables exist without a fallback.\n\n## Repository Docs Convention\n\n- **Plans** live in `docs/plans/` — unified plan artifacts. New `ce-brainstorm` outputs are requirements-only unified plans (`artifact_readiness: requirements-only`); `ce-plan` enriches them to implementation-ready plans (`artifact_readiness: implementation-ready`). Historical `docs/brainstorms/*-requirements.*` files remain readable legacy inputs and should not be migrated just because a new plan is created.\n- **Brainstorm evidence / legacy requirements** may live in `docs/brainstorms/` — historical requirements docs and specialized analysis artifacts such as `docs/brainstorms/riffrec-feedback/`. Do not treat this as the canonical output path for new `ce-brainstorm` artifacts.\n- **Solutions** live in `docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas.\n- **Specs** live in `docs/specs/` — target platform format specifications.\n\n### Solution categories (`docs/solutions/`)\n\nThis repo builds a plugin *for* developers. Categorize solutions from the perspective of the end user (a developer using the plugin), not a contributor to this repo.\n\n- **`developer-experience/`** — Issues with contributing to *this repo*: local dev setup, shell aliases, test ergonomics, CI friction. If the fix only matters to someone with a checkout of this repo, it belongs here.\n- **`integrations/`** — Issues where plugin output doesn't work correctly on a target platform or OS. Cross-platform bugs, target writer output problems, and converter compatibility issues go here.\n- **`workflow/`**, **`skill-design/`** — Plugin skill and agent design patterns, workflow improvements.\n\nWhen in doubt: if the bug affects someone running `bun install compound-engineering` or `bun convert`, it's an integration or product issue, not developer-experience.\n","GEMINI.md":"# Compound Engineering\n\nThis Gemini extension provides the Compound Engineering skill set for planning,\nreview, implementation, debugging, and release workflows.\n\nUse the installed skills when they match the user's request. Treat this file as\nruntime context for extension users, not contributor guidance for this source\nrepository. Do not apply this repository's `AGENTS.md` maintainer workflow to\nthe user's project.\n"},"files":{"AGENTS.md":"# Agent Instructions\n\nThis repository is the root of the `compound-engineering` coding-agent plugin and the marketplace/catalog metadata used to distribute it.\n\nIt also contains:\n- the Bun/TypeScript CLI that converts Claude Code plugins into other agent platform formats\n- shared release and metadata infrastructure for the CLI, marketplace, and plugin\n\n`AGENTS.md` is the canonical repo instruction file. Root `CLAUDE.md` is a symlink to `AGENTS.md` so Claude Code and other tools that look for `CLAUDE.md` still find it at the expected path. Keep that symlink (do not replace it with a regular file): a real root `CLAUDE.md` makes `claude plugin validate --strict` fail because this checkout is also the plugin root.\n\n## Quick Start\n\n```bash\nbun install\nbun run test              # full test suite (also runs in CI; `--parallel` across worker processes)\nbun run release:validate  # plugin/marketplace consistency (also runs in CI)\nbun run plugin:validate   # Claude marketplace + plugin schema (also runs in CI; needs `claude` on PATH)\n```\n\n### Codex Local Plugin Development\n\nWhen testing current skill files in Codex, run the repository workflow from the checkout or worktree you intend to test:\n\n```bash\nbun run codex:dev -- local    # link this worktree's skills and remove CE plugin installs\nbun run codex:dev -- status   # show local/remote state and checkout provenance\nbun run codex:dev -- remote   # restore the official marketplace-backed plugin\nbun run codex:dev -- remove   # remove both supported CE installation surfaces\n```\n\n`refresh` is an idempotent alias for `local`. Local mode manages only the exact `$CODEX_HOME/skills/compound-engineering-local` symlink and Compound Engineering plugin IDs; it must not alter unrelated user skills. The symlink includes modified and untracked files from the selected worktree. Start a new Codex session after switching installation modes. Current Codex versions detect direct skill edits automatically; restart only if an edit does not appear. For live local testing, use this workflow instead of adding the repository as a marketplace: a marketplace install caches a snapshot, while local mode links the current skill files.\n\n## Working Agreement\n\n- **Branching:** Create a feature branch for any non-trivial change. If already on the correct branch for the task, keep using it; do not create additional branches or worktrees unless explicitly requested.\n- **Merge policy:** All changes to `main` go through pull requests. Direct pushes and direct merges are not allowed; branch protection on `main` enforces this by requiring the `test` status check to pass. The direct path bypasses `release:validate`, the test suite, and PR title validation — past direct merges have caused version drift requiring multi-PR recovery (see `docs/solutions/workflow/release-please-version-drift-recovery.md`).\n- **Contribution gate (non-maintainers):** If you are not a repository maintainer or admin, do not open a PR without a linked issue — file the issue first and reference it from the PR. Adding a **new skill** has a stricter gate: non-maintainers and non-admins must raise a discussion in an issue and get explicit maintainer approval **before** starting the work; do not open a new-skill PR that has not been approved this way. Maintainers and admins are exempt from both gates but still follow the merge policy above.\n- **PR disclosure:** `.github/pull_request_template.md` ends with `## Security Disclosure` and `## Agent Disclosure` sections. Fill both when opening a PR — including PRs authored via `gh pr create --body`/`--body-file`, which bypass the template so nothing pre-fills them. State any security-relevant changes (or \"No security-relevant changes\"), and the model that did the bulk of the work — your harness plus the most specific model identity your own context gives you, e.g. `Claude Code · claude-opus-4-8` or `Codex CLI · GPT-5`. Copy an exact model ID verbatim when your harness states one; when it exposes only a generic family, report the family and stop. Measured 2026-07-24: Codex and Cursor agents cannot see their running model at all (Codex's \"based on GPT-5\" is fixed boilerplate), so do not upgrade a family to a version, and do not read config files for one — the configured default is often not the model actually running. Never invent a version or variant. The body above those sections stays freeform — add whatever sections best explain the change.\n- **Safety:** Do not delete or overwrite user data. Avoid destructive commands.\n- **Testing:** Run `bun run test` after changes that affect parsing, conversion, output, skill conventions, or other mechanical guards. Local `bun run test` is the same suite CI runs — there is no separate local-only unit-test lane. Prefer it over bare `bun test`: the package script carries `--parallel`, which is where the suite's speed comes from. Bare `bun test <file>` is still the right tool for iterating on one file.\n- **Release versioning:** Releases are prepared by release automation, not normal feature PRs. The repo has one root plugin/package release component (`compound-engineering`) plus marketplace components (`marketplace`, `cursor-marketplace`). GitHub release PRs and GitHub Releases are the canonical release-notes surface for new releases; root `CHANGELOG.md` is only a pointer to that history. Use conventional titles such as `feat:` and `fix:` so release automation can classify change intent, but do not hand-bump release-owned versions or hand-author release notes in routine PRs.\n- **Output Paths:** Keep OpenCode output at `opencode.json` and `.opencode/{agents,skills,plugins}`. For OpenCode, commands go to `~/.config/opencode/commands/<name>.md`; `opencode.json` is deep-merged (never overwritten wholesale).\n- **Scratch Space:** Default to OS temp. Use `.context/` only when explicitly justified by the rules below.\n  - **Default: OS temp** — covers most scratch, including per-run throwaway AND cross-invocation reusable, regardless of whether a repo is present or whether other skills may read the files. A stable OS-temp prefix handles cross-skill and cross-invocation coordination equally well as an in-repo path; repo-adjacency is rarely the relevant property.\n    - **Per-run throwaway**: `mktemp -d \"${TMPDIR:-/tmp}/<prefix>-XXXXXX\"` (OS handles cleanup). Use for files consumed once and discarded — captured screenshots, stitched GIFs, intermediate build outputs, recordings, delegation prompts/results, single-run checkpoints. Always pass an explicit template under `${TMPDIR:-/tmp}`. Do not use bare `mktemp`, bare `mktemp -d`, `mktemp -t`, or `mktemp -d -t`: those forms ignore `$TMPDIR` on macOS and can resolve outside a sandbox's writable temp directory.\n    - **Cross-invocation reusable**: use a stable, effective-user-owned prefix under `/tmp/compound-engineering-<effective-uid>/<skill-name>/` — **not** `mktemp -d` — so later invocations by the same OS user can find prior outputs without sharing a writable root with other users. Derive the effective UID with `id -u`, reject a symlink or path not owned by the current user, and create or repair the top-level root to mode `0700` before use. **Probe before committing to `/tmp`:** when that root cannot be created, is not yours, or is not writable, use `${TMPDIR:-/tmp}/compound-engineering-<effective-uid>` instead — the same rule, in the same order, in every shell preamble and Python default, so a later invocation resolves the same root. Claude Code's macOS sandbox allowlists writes under `$TMPDIR` (`/tmp/claude-<uid>`) but not `/tmp` itself, so without the fallback every skill's scratch setup aborts there with `Operation not permitted`; and an existing root from an unsandboxed session passes `mkdir -p` as a no-op yet refuses the first write, which is why the probe is a writability check (`[ -w ]`), not creation alone. Copy the block from any shipped skill (for example `skills/ce-compound/SKILL.md`) rather than re-deriving it; `tests/scratch-root-preamble-executes.test.ts` runs every copy, including the fallback. The default layout is one `<scratch-root>/<skill-name>/<run-id>/` directory per run; use it for caches keyed by session, checkpoints meant to survive context compaction, intermediate state, and outputs whose lifecycle or mutation belongs to one run.\n      - **Discoverable collection exception**: omit the per-run directory only when later invocations intentionally enumerate multiple sibling **final artifacts** as core product behavior and run isolation would materially worsen discovery or the user-facing path. Use a stable collection namespace (for example, repository identity plus a `general` fallback), descriptive immutable filenames, metadata that supports ranking, and no-overwrite collision handling that atomically reserves the final filename and retries with the next suffix on collision; never check availability and then write. Do not use this exception for caches, checkpoints, intermediate files, or merely to shorten a path.\n      - Prefer `/tmp` over `$TMPDIR` so paths stay accessible: `$TMPDIR` on unsandboxed macOS resolves to `/var/folders/64/.../T/`, which is hostile for users who want to inspect checkpoints, grep them, or copy them out — which is why `$TMPDIR` is the fallback, taken only when `/tmp` cannot host the root, and never the first choice. The explicit effective-UID segment supplies the required cross-user boundary while preserving a readable path. Agents running as the same OS user intentionally remain in one discretionary-access-control principal.\n  - **Exception: `.context/`** — use only when the artifact is genuinely bound to the CWD repo AND meets at least one of:\n    - (a) **User-curated**: the user is expected to inspect, manipulate, or manually curate the artifact outside the skill (e.g., a per-repo TODO database, a per-spec optimization log that survives across sessions on the same checkout).\n    - (b) **Repo+branch-inseparable**: the artifact's meaning is inseparable from this specific repo or branch (e.g., branch-specific resume state that a user expects to pick up again in the same checkout).\n    - (c) **Path is core UX**: surfacing the artifact path back to the user is a core part of the skill's output and that path is easier to communicate as a repo-relative location than an OS-temp one.\n    Namespace under `.context/compound-engineering/<workflow-or-skill-name>/`, add a per-run subdirectory when concurrent runs are plausible, and decide cleanup behavior per the artifact's lifecycle (per-run scratch clears on success; user-curated state persists). \"Shared between skills\" is not by itself sufficient — OS temp handles that equally well.\n  - **Durable outputs** (plans, specs, learnings, docs, final deliverables) belong in `docs/` or another repo-tracked location, not in either scratch tier.\n  - **Cross-platform note:** `/tmp` is writable on macOS (symlink to `/private/tmp`), Linux, and WSL. For per-run throwaway files, use an explicit `${TMPDIR:-/tmp}` template so macOS and sandboxed hosts honor the selected temp parent. Skills authored here assume Unix-like shells (bash on macOS/Linux, or Git Bash on Windows). Native Windows is a supported target for Python interpreter resolution and peer-job detach — never hardcode `python3`; probe execution per `docs/solutions/conventions/resolve-python-interpreter-not-python3.md`.\n- **Character encoding:**\n  - **Identifiers** (file names, agent names, command names): ASCII only -- converters and regex patterns depend on it.\n  - **Markdown tables:** Use pipe-delimited (`| col | col |`), never box-drawing characters.\n  - **Prose and skill content:** Unicode is fine (emoji, punctuation, etc.). Prefer ASCII arrows (`->`, `<-`) over Unicode arrows in code blocks and terminal examples.\n\n## Directory Layout\n\n```\nsrc/              CLI entry point, parsers, converters, target writers\nskills/           Compound Engineering plugin skills\n.claude-plugin/   Claude plugin manifest and marketplace catalog metadata\n.codex-plugin/    Codex plugin manifest\n.cursor-plugin/   Cursor plugin manifest and marketplace catalog metadata\n.opencode/        OpenCode package entrypoint and install docs\n.pi/              Pi extension entrypoint\ntests/            Converter, writer, and CLI tests + fixtures\ndocs/             Requirements, plans, solutions, and target specs\nCONCEPTS.md       Shared domain vocabulary (glossary of project-specific terms)\n```\n\n## Repo Surfaces\n\nChanges in this repo may affect one or more of these surfaces:\n\n- root plugin content under `skills/`, `AGENTS.md`, `README.md`, and platform manifests\n- marketplace catalogs under `.claude-plugin/`, `.cursor-plugin/`, and `.agents/plugins/`\n- the converter/install CLI in `src/` and `package.json`\n\nDo not assume a repo change is \"just CLI\" or \"just plugin\" without checking which surface owns the affected files.\n\n## Plugin Maintenance\n\nWhen changing plugin content:\n\n- Update substantive docs like `README.md` when the plugin behavior, inventory, or usage changes.\n- When adding a user-facing skill, document it: create a `docs/skills/<skill-name>.md` page (purpose, novel mechanics, when to use, chain position — follow the shape of the existing pages) and add a catalog row under the right category in `docs/skills/README.md`, alongside the root `README.md` inventory row and the skill-count bump in `tests/release-metadata.test.ts`. Keep these in sync when a skill's purpose or inventory changes. This is convention, not yet validated by a test, so it is easy to miss — most skills have a page; the few that don't (e.g. `lfg`, `ce-dogfood-beta`) are the exception, not the rule.\n- When adding, removing, renaming, or changing the meaning/default/consumer of a `.compound-engineering/config.yaml` option, update `skills/ce-setup/references/config-template.yaml`, its byte-identical `.compound-engineering/config.example.yaml` copy, the centralized `docs/skills/configuration.md` reference, and the affected consumer skill docs in the same change. Ordinary keys may also live in optional checkout-local `config.local.yaml` (overrides the repo file). `docs_root` belongs only in `config.yaml`. Durable team instructions still belong in the project's normal agent-instructions mechanism.\n- Do not hand-bump release-owned versions in plugin or marketplace manifests.\n- Do not hand-add release entries to `CHANGELOG.md` or treat it as the canonical source for new releases.\n- Run `bun run release:validate` if agents, commands, skills, MCP servers, or release-owned descriptions/counts may have changed.\n- When removing a skill, agent, or command, add its name to both cleanup registries so stale flat-install artifacts are swept on upgrade:\n  - `STALE_SKILL_DIRS` / `STALE_AGENT_NAMES` / `STALE_PROMPT_FILES` in `src/utils/legacy-cleanup.ts`\n  - `EXTRA_LEGACY_ARTIFACTS_BY_PLUGIN[\"compound-engineering\"]` in `src/data/plugin-legacy-artifacts.ts`\n\nUseful validation commands:\n\n```bash\nbun run release:validate\ncat .claude-plugin/marketplace.json | jq .\ncat .claude-plugin/plugin.json | jq .\n```\n\n## Runtime vs Authoring Context\n\n`AGENTS.md`, `CLAUDE.md` (symlink to `AGENTS.md`), and `GEMINI.md` are authoring context for this source repository. Skills are installed into end-user environments, where they run against the user's local instruction files, not this repo's. Behavioral rules that must affect a skill at runtime belong in that skill's `SKILL.md` or files under its own `references/` directory.\n\n## Working on Skills\n\nThis repository authors each skill once and distributes it across multiple agent models and harnesses. A skill is a set of goals, not a state machine: it hands the agent the goal, the done condition, the safe failure direction, and the facts it cannot derive from the repo in front of it, then gets out of the way. The reading agent is expected to reason to the mechanics from there. Before creating, materially revising, or reviewing a skill or skill-local persona, read `docs/solutions/skill-design/portable-agent-skill-authoring.md`, applying only the sections relevant to the skill; the rules in this file supplement it and take precedence where they are more specific. The four activities below — authoring, editing, reviewing, and acting on review — are one lifecycle and share one standard, so a rule stated for one applies to the others unless it says otherwise.\n\n### Authoring a new skill\n\n- **State conditions, not procedures.** Write what must be true (goal, done, safe direction) and the non-derivable facts. When you find yourself adding cases to a block to make it hold, name the condition those cases are proxies for and state that instead. Read `docs/solutions/skill-design/skill-gates-state-conditions-not-prescribed-git-commands.md` for the canonical failure.\n- **Prescribe a mechanism only where it is owned.** A skill spells out commands, exit semantics, or state transitions for the mechanics it owns (`ce-commit-push-pr` owns PR detection) and for deterministic work that is cheap to compute but costly to reason to (a data-processing script, a path anchor). A skill that delegates that work states the condition that must hold, the safe failure direction, and the non-derivable facts about the callee -- never a re-derivation of the callee's commands. Prescribed commands in a delegating skill encode one configuration and are wrong for the next one.\n- **Prose admission.** Keep a line only when it states a falsifiable constraint, counters a known default tendency or observed shortcut, or supplies domain knowledge that materially changes a decision. Do not keep vague effort or quality language (\"be thorough\") as a standalone instruction; replace it with an observable rule, or retain a targeted effort cue only when it counters a documented runtime tendency and has been evaluated there. Do not append motivational rationale to a directive that already stands on its own. Repeat an instruction only at a demonstrated drift point where placement changes whether it fires, and protect genuinely required always-loaded duplicates with a parity test.\n- **User-facing invocations.** Keep agent-to-agent or skill-to-skill routing semantic: format formal skill names as inline code (for example, `ce-plan`) and invoke the named skill through the active harness's callable skill mechanism. When a skill prints or copies a user-runnable invocation, default to `/skill-name`; use `$skill-name` only when the active harness is Codex or explicitly documents dollar-prefixed skill invocation. On oh-my-pi (`omp`), keep the default form for model-visible targets; use native `/skill:<name>` only when the target is not model-visible because it declares `disable-model-invocation` or `hide` (for example, `/skill:ce-polish`). In prose, render only the invocation as inline code; use a fenced block only when the command stands alone. Output exactly one form. Do not apply this rendering rule to built-in commands such as `/goal`. At runtime, put the smallest self-contained rendering rule immediately before the smallest section that contains all affected user-copy seams; do not repeat it in every step, only in a separately loaded reference that independently owns output.\n- **Loading and placement.** Keep every load-bearing action, route, and reference-load instruction inline at the point where it must fire (`docs/solutions/skill-design/post-menu-routing-belongs-inline.md`). Do not inline a summary complete enough to suppress loading the authoritative reference. Extract a block to `references/` when it is conditional or late-sequence and a meaningful share of the skill (~20%+), replacing it with a 1-3 line condition and backtick path. Never use `@` for an extracted block; it inlines at load time and defeats extraction.\n\n### Editing an existing skill\n\nSkills predate the current authoring standard and evolve toward it; the standard is the guide above, not the text around your edit.\n\n- **Bring the block you touch up to the standard.** When a change lands in a block written as a procedure, a menu, or an enumeration of cases, restate that block as its conditions as part of the change rather than matching the old shape. Matching a neighboring procedure because it is there is how procedures propagate.\n- **Scope the rewrite to your change plus what it makes wrong.** Reconcile the blocks your change contradicts or duplicates; leave untouched blocks alone even when they fall short of the standard, and name them in the PR as follow-up. A repo-wide modernization is its own change, requested explicitly.\n- **Repeated case-specific repair is the defect signal.** When a block keeps absorbing \"add the case we just found\" -- in authoring, in a review round, or in your own fix to a finding -- the representation is wrong, usually a procedure that should be a condition. Delete the additions and restate the goal, then re-verify the shorter rule against every path the additions served: a condensed rule that no longer names a path is a new defect, not a simplification.\n\n### Reviewing a skill change (bots and humans)\n\nReview bots read this file when reviewing a PR here. On `skills/**`:\n\n- **A finding is a gap in the goal, the done condition, or the safe failure direction, or a mechanism at the wrong owning layer** — commands prescribed in a skill that delegates that work, a rule placed where it will not fire, a Claude-only construct in a cross-host skill, a rendering that breaks on another harness.\n- **A case a stated condition already covers is not a finding.** Before filing \"what if X\" against a rule, check whether the rule's condition decides X. \"Rename only on positive proof the branch was never published; any other result keeps the name\" already decides an unreachable remote. If it does, do not file; if the condition is wrong or missing, file *that*.\n- **State the requested fix as a condition or an owning-layer move, never as a case to add.** \"This probe fails open on network error\" is a correct observation; the fix to request is \"state the condition\" or \"delete the probe\", not \"also check the exit code\". \"Command X fails in state Y\" against a delegating skill is a finding about the representation; the fix is to drop the command and state the condition, not to correct the command.\n- **A block restated to the standard is the expected shape of an edit**, not scope creep, when the restatement covers every path the old text served.\n- Ordinary code under `src/`, `tests/`, and `scripts/` gets ordinary code review; these rules are about instruction prose.\n\n### Acting on review feedback\n\nApplying review, peer, or eval feedback to a skill is a material revision under the same standard. An item is not addressed because a sentence landed; it is addressed when a demonstrated gap is closed at its owning layer by the smallest mechanism. Skill prose is not code: a natural-language instruction can always be made more specific, so a reviewer can produce a valid-looking edge case against any condition indefinitely, and patching each one dilutes the instruction. Measured 2026-08-15 (#1397): a two-condition setup step absorbed 24 bot findings over nine rounds, most of them cases against text the previous round had added, before being restated as the two conditions it began as. Before editing:\n\n1. **Evidence** — classify each item as Change, Verify, or Consider using the guide's evidence rules. Do not edit the skill for Verify or Consider items. A case the stated condition already decides is Verify at most: answer it with the condition (`not-addressing` quoting it, or `replied` for a question), do not patch. \"Default to fixing\" is the right rule for code; on skill prose the default for a case-level finding is to point at the condition.\n2. **Owning layer** — for each Change, identify its owning layer: activation contract, outcome spine or skill boundary, runtime protocol, loading or placement, deterministic enforcement, or shared authoring rule. Several fixes in #1397 belonged in the callee skills (`ce-commit`, `ce-commit-push-pr`), not in the calling skill's prose.\n3. **Mechanism** — fix the gap at its owning layer. Add prose only when it is the smallest mechanism that closes the gap, and then only the smallest falsifiable unit per prose admission.\n4. **Reconcile** — reread the affected block; remove or rewrite text the change makes conflicting, duplicated, or obsolete. Resolve conflicting feedback items rather than stacking both.\n5. **Stop the accretion loop** — when a finding targets text an earlier round added, delete or restate that addition rather than qualifying it. On the second round against the same block, stop patching: restate the block as its goal, done condition, and safe direction and re-verify against every path the additions served. This holds whether the rounds arrive in one review or across a babysit loop's re-invocations.\n\nWhen evidence shows the same cause across skills, fix the shared guide, rule, or mechanism unless the skills' contracts materially differ. For a multi-item round, record one line per item in the existing PR body or work note: `item -> Change|Verify|Consider | owning layer | mechanism - why`. A single-item fix still follows the steps above; the written line is optional. Reviewer wording is a hypothesis about mechanism, not authority over it — the reviewer's one-line prose fix is sometimes exactly right.\n\n## Referencing Project Conventions in Skills\n\nWhen a skill needs to discover a project convention at runtime — the issue tracker, coding standards, commit format, lint command, scope constraints, etc. — describe **what to look for in the agent's existing context**, not **which file to open**.\n\n**On the read path, do not name instruction files (`AGENTS.md` / `CLAUDE.md` / `GEMINI.md` / `.cursor/rules`).** Phrase it as \"the project's active instructions and conventions already in your context.\" Three reasons:\n\n- **Redundant.** Every major harness auto-injects the project's root instruction file into context at session start (Claude Code loads `CLAUDE.md`, Codex `AGENTS.md`, Gemini `GEMINI.md`). Telling the agent to \"read `AGENTS.md`\" asks it to re-open content it already has.\n- **Brittle / not portable.** The filename differs per harness, and this plugin is authored once and converted to all of them. A hardcoded \"read `AGENTS.md` (or `CLAUDE.md`)\" silently finds nothing on a harness that uses a different name.\n- **Security smell.** Instructing an agent to go *read named instruction dotfiles* is the exact shape that prompt-injection defenses in some agent frameworks (e.g., Hermes) flag. Referencing context rather than filenames avoids tripping those guards.\n\n**Name a concrete file only where the skill must do something a context reference can't express:**\n\n- **Writing a convention back** (e.g., persisting `project_tracker: linear`) needs a target — name it minimally and as an example (\"the project's root agent-instructions file, e.g., `AGENTS.md`; if it `@`-includes another, write to the substantive one\").\n- **Reading content that is genuinely not auto-loaded** — a subdirectory-scoped instruction file governing the area being changed, an optional project doc like `STRATEGY.md` / `CONCEPTS.md` / `README.md`, or any file a *fresh subagent* (which does not inherit the parent's loaded instructions) must open to do its job. Auditing tools that must enumerate every standards file (e.g., `ce-code-review`'s project-standards reviewer globbing all `CLAUDE.md`/`AGENTS.md`) are a legitimate exception — they review the files, they don't re-read them for context.\n\n**Describe the capability, not the tool.** Pair this with naming the *category* of thing rather than a closed set: \"the project's issue tracker (e.g., GitHub Issues, Linear, Jira)\" and \"whatever interface that tracker exposes (connector/MCP, documented API, or a documented CLI)\" — never assume a specific CLI exists, and never treat a missing binary / env var / MCP server as proof the capability is unavailable.\n\n## Validating Agent and Skill Changes\n\nBehavioral changes to a plugin skill or skill-local persona (anything under `skills/`) need a different validation path than mechanical code changes, because of how Claude Code loads plugins.\n\n- **Use the `skill-creator` skill to test changes.** Skill-creator is purpose-built for this: it spawns a generic subagent and injects the agent or skill content into the subagent's prompt at dispatch time, so each run reads the current source from disk. Invoke `/skill-creator` and use its eval workflow rather than reaching for ad-hoc workarounds.\n\n- **Plugin agent and skill definitions both cache at session start.** Once a Claude Code session is open, dispatching a typed plugin agent runs the in-memory copy that was loaded when the session began. The same applies to skills: invoking a skill goes through the cached skill loader, so edits to skill scripts are also not tested via that path. File edits to either layer after session start do not propagate within the same session. Any iteration loop built around typed-agent dispatch or Skill-tool invocation in the same session is testing pre-edit content, not your changes.\n\n- **Do NOT edit `~/.claude/plugins/cache/` or `~/.claude/plugins/marketplaces/` to try to force a reload.** Those paths are user machine state, not repo-managed. Modifying them does not reliably bypass the in-session cache (it didn't, in observed behavior), risks being silently overwritten by plugin updates, and is the wrong layer to test from. The skill-creator pattern is the proper approach; if you genuinely need fresh-loaded behavior of the typed-agent dispatch path, restart the Claude Code session — but skill-creator is preferred for fast iteration.\n\n- **A version-matched cache is not automatically stale — confirm by content, not by version.** When this working tree is the local marketplace source, a session (re)start re-copies it into `~/.claude/plugins/cache/.../compound-engineering/<version>/` (a plain copy, no `.git`; `<version>` is the working tree's `.claude-plugin/plugin.json` version), so the loaded plugin can be identical to — and as current as — your edits. Do not assume the running copy is stale just because it lives under the cache path; equally, do not assume a matching `<version>` means it includes your latest change. Version match is necessary but not sufficient: edits within a release do not bump the version, so a matching segment proves only that the cache was built from this release, not that it captured your most recent edit. To know which copy is actually loaded, diff the specific cache file against the working-tree file — identical means the running plugin is your current edit and you can trust it; differing means the session predates the edit, so restart (or use skill-creator). Never infer \"stale\" or \"current\" from the version segment alone.\n\n- **Mechanical changes do not have this restriction.** Skill scripts (e.g., `extract-metadata.py`), parser logic, conversion code, and anything `bun test` exercises always run the current source. The caching issue only affects LLM-driven skill prose behavior dispatched through the plugin loader.\n\n## CI and Quality Gates\n\nPR CI (`.github/workflows/ci.yml`) is the merge gate. It runs, in order: PR-title lint (PRs only), `bun run release:validate`, `bun run plugin:validate`, and `bun run test`. Do not invent a parallel local-only mechanical suite — if a check is deterministic and should block merges, put it in one of those steps (usually `bun run test`).\n\nThe `test` script runs `bun test --parallel`, which distributes test *files* across worker processes (one file still runs its own tests serially, and `--parallel` implies `--isolate`). This is the single biggest lever on CI wall time, because most of the suite is spent blocked on subprocesses — `python3`, `bash`, `git`, and `bun run src/index.ts` — not on CPU. Keeping it in the package script rather than the workflow means CI and a contributor's local run cannot drift apart.\n\nThat makes cross-file isolation load-bearing rather than incidental: a test file may not depend on another file's leftovers, and any test that writes outside its own `mktemp` directory is a latent flake. There are no exceptions — a test that needs a dirty tree builds a throwaway git repo for it.\n\n**A test that runs a bundled script which inspects the repository must point that script at a throwaway repo, never this checkout.** Otherwise the developer's uncommitted work becomes test input. `tests/skills/ce-code-review-cross-model-routes.test.ts` ran the real review script against `git diff HEAD` in the checkout, so any uncommitted change over roughly 160KB crossed the script's large-diff threshold and failed 31 of its tests for reasons unrelated to the change under test. CI never saw it, because CI runs on a committed tree. The fixture pattern is `dirtyFixtureRepo()` in that file: `git init` a temp dir, two commits so `HEAD~1` resolves, then one staged edit.\n\n**Do not pin a worker count.** `--parallel` with no value tracks the runner's core count, which is what you want. Raising it looks free — the suite is idle-bound, so more workers should pack better — but it was measured on CI and it is not: at `--parallel=8` on a 4-core runner, wall time improved ~9% (102s -> 93s) while total test-CPU inflated from 223s to 343s, and five tests crossed the 5000ms default per-test timeout. That converts runner busyness into red builds. A file that legitimately runs for seconds should call `setDefaultTimeout` instead, as the subprocess-heavy suites do.\n\nA file never splits across workers, so an oversized file sets a floor. `tests/skills/ce-work-unit-workspace.test.ts` was 4,564 lines and 86 tests under one `describe`; it is now five `ce-work-unit-workspace-*.test.ts` files sharing `tests/skills/helpers/ce-work-workspace-harness.ts`. Measured with three `workflow_dispatch` runs per ref in the same window, the `Run tests` step went from a median of 88s (87/112/88) to 81s (83/80/81).\n\n**Splitting bought ~8% of CI wall time and most of the run-to-run variance** — baseline spread 25s, split spread 3s. That second effect is the durable one: a 60s serial file makes wall time depend on which worker takes it, so a busy runner produced the 112s outlier. Locally the same split is much larger (160s -> 75s), because a developer machine has enough cores for the long file to be the whole critical path.\n\n**Do not use `bun test --parallel=4` on a many-core laptop as a CI proxy.** It predicted a 26% CI win where the real number was 8%: capping bun to four workers still leaves the other cores absorbing the `git` / `python3` / `bash` subprocess load, so it does not behave like a 4-core runner. Dispatch the real workflow on both refs instead.\n\nAt 81s the suite is nearer CPU-bound on a 4-core runner than bounded by its slowest file, so further file splitting has small returns; `tests/ce-babysit-pr-snapshot.test.ts` (~36s) is the largest remaining file and was deliberately left intact.\n\n**Size a test file by its measured time, not its line count.** A file is a wall-time problem when it approaches the suite's slowest-file ceiling. Roughly a thousand lines under one `describe` is a smell worth measuring, never a threshold to split at on sight — splitting a file that already runs well under the ceiling buys nothing. Get per-file times before deciding:\n\n```bash\nbun test --parallel --reporter=junit --reporter-outfile=/tmp/t.xml\n```\n\nThe `ce-work-unit-workspace-*` shards run 10-23s each against that ~36s ceiling, so two of them sitting just over a thousand lines is fine and they are deliberately left whole. Put shared fixtures in `tests/skills/helpers/`.\n\n### What belongs where\n\n| Kind of check | Where it lives | Notes |\n|---|---|---|\n| Deterministic invariants (frontmatter, parity, path safety, script behavior, converter/writer output, greppable skill contracts) | `bun test` / `release:validate` / `plugin:validate` | Must pass in CI |\n| Skill *prose behavior* (routing judgment, restraint, cross-model peer outcomes) | `skill-creator` eval, local / PR evidence | Not a CI job; non-deterministic and needs a model |\n\nThat split is intentional. See `docs/solutions/skill-design/portable-agent-skill-authoring.md` (\"Evaluate proportionally\"). Mechanical checks belong in CI; behavioral agent evals are best-effort evidence, not an exhaustive CI matrix.\n\n### Right-size new mechanical guards\n\nWhen a review bot or human finds a greppable invariant that `bun test` missed:\n\n1. Prefer **tightening an existing guard** over adding a new suite (e.g. widen a regex that already documents the rule).\n2. Pin the **smallest falsifiable unit** — a token, enum, path, heading, or one fixture that would have failed on the regressing diff. Do not snapshot whole skill bodies or pin incidental wording.\n3. If the failure needs an LLM to judge, keep it in skill-creator; do not fake it as a brittle string test.\n\n### Maintaining `plugin:validate`\n\n- `package.json` `plugin:validate` must validate **both** the marketplace catalog and the plugin manifest, with `--strict` on each. Paths: `.claude-plugin/marketplace.json` and `.claude-plugin/plugin.json`. Do **not** use `claude plugin validate .` — that resolves this repo as a marketplace only (because `.claude-plugin/marketplace.json` exists with `source: \"./\"`) and skips plugin-root checks.\n- CI pins `@anthropic-ai/claude-code` for reproducible schema rules. Bump the pin deliberately when adopting new upstream rules; do not float `@latest`.\n- Root `CLAUDE.md` must remain a **symlink** to `AGENTS.md` (path stays at the repo root where contributors expect it). Upstream warns on a regular-file plugin-root `CLAUDE.md` because it is not loaded as end-user project context; the symlink avoids that warning so `--strict` can stay on. Do not replace the symlink with a regular `@AGENTS.md` shim or relocate the file just for validators.\n- If `--strict` starts failing again on `CLAUDE.md` after an upstream bump, check whether the symlink was materialized into a regular file (Windows/`core.symlinks=false` checkouts) or whether the validator started following symlinks — fix the layout or pin, do not silently drop `--strict`.\n\n### When CI comments look \"stale\"\n\nIf CI claims a deferred warning or a missing gate, reproduce with the **pinned** `claude` version against `.claude-plugin/plugin.json` before treating the comment as current. Marketplace-only validation can hide plugin warnings.\n\n## Coding Conventions\n\n- Prefer explicit mappings over implicit magic when converting between platforms.\n- Keep target-specific behavior in dedicated converters/writers instead of scattering conditionals across unrelated files.\n- Preserve stable output paths and merge semantics for installed targets; do not casually change generated file locations.\n- When adding or changing a target, update fixtures/tests alongside implementation rather than treating docs or examples as sufficient proof.\n\n## Commit Conventions\n\n- **Prefix is based on intent, not file type.** Use conventional prefixes (`feat:`, `fix:`, `docs:`, `refactor:`, etc.) but classify by what the change does, not the file extension. Files under `skills/` and plugin manifests are product code even though they are Markdown or JSON. Reserve `docs:` for files whose sole purpose is documentation (`README.md`, `docs/`, `CHANGELOG.md`).\n- **Type selection — classify by intent, not diff shape.** Where `fix:` and `feat:` could both seem to fit, default to `fix:`: a change that remedies broken or missing behavior is `fix:` even when implemented by adding code, and net additions do not turn a fix into a `feat:`. Reserve `feat:` for capabilities the user could not previously accomplish where nothing was broken. Other conventional types (`chore:`, `refactor:`, `docs:`, `perf:`, `test:`, `ci:`, `build:`, `style:`) remain primary when they describe the change more precisely than either. Heuristic: if a regression test you could write today would have failed *before* the change, it's `fix:`. The user may override this default for a specific change.\n- **Include a component scope.** The scope appears verbatim in the changelog. Pick the narrowest useful label: skill/agent name (`document-review`, `learnings-researcher`), CLI or marketplace area (`cli`, `marketplace`), or shared area when cross-cutting (`review`, `research`, `converters`). Never use `compound-engineering` — it's the entire plugin and tells the reader nothing. Omit scope only when no single label adds clarity.\n- **Never use `!` or a `BREAKING CHANGE:` footer without explicit user confirmation.** These markers trigger release-please's automatic major version bump — a decision the user may not want even when a change is technically breaking. If a change appears breaking, surface that to the user and let them decide whether to apply the marker.\n\n## Adding a New Target Provider\n\nOnly add a provider when the target format is stable, documented, and has a clear mapping for tools/permissions/hooks. Use this checklist:\n\n1. **Define the target entry**\n   - Add a new handler in `src/targets/index.ts` with `implemented: false` until complete.\n   - Use a dedicated writer module (e.g., `src/targets/codex.ts`).\n\n2. **Define types and mapping**\n   - Add provider-specific types under `src/types/`.\n   - Implement conversion logic in `src/converters/` (from Claude → provider).\n   - Keep mappings explicit: tools, permissions, hooks/events, model naming.\n\n3. **Wire the CLI**\n   - Ensure `convert` and `install` support `--to <provider>` and `--also`.\n   - Keep behavior consistent with OpenCode (write to a clean provider root).\n\n4. **Tests (required)**\n   - Extend fixtures in `tests/fixtures/sample-plugin`.\n   - Add spec coverage for mappings in `tests/converter.test.ts`.\n   - Add a writer test for the new provider output tree.\n   - Add a CLI test for the provider (similar to `tests/cli.test.ts`).\n\n5. **Docs**\n   - Update README with the new `--to` option and output locations.\n\n## Specialist Prompt Assets in Skills\n\nThe compound-engineering plugin no longer ships standalone agent definitions under `agents/`. When a skill needs a specialist persona, store it inside that skill directory, usually under `references/agents/` or `references/personas/`, and have the calling skill dispatch a generic subagent with that file's contents in the prompt.\n\nInternal prompt asset file names should be descriptive and unprefixed because they are not externally exposed agent names.\n\nExample:\n- `references/agents/learnings-researcher.md` (correct)\n- `references/agents/ce-learnings-researcher.md` (wrong for an internal prompt asset)\n\nThese prompt assets must not include YAML frontmatter. Model selection, tool constraints, and dispatch policy belong in the calling skill's `SKILL.md`, not in the prompt asset.\n\n## File References in Skills\n\nEach skill directory is a self-contained unit. A SKILL.md file must only reference files within its own directory tree (e.g., `references/`, `assets/`, `scripts/`) using relative paths from the skill root. Never reference files outside the skill directory — whether by relative traversal or absolute path.\n\nBroken patterns:\n\n- `../other-skill/references/schema.yaml` — relative traversal into a sibling skill\n- `/home/user/compound-engineering-plugin/skills/other-skill/file.md` — absolute path to another skill\n- `~/.claude/plugins/cache/marketplace/compound-engineering/1.0.0/skills/other-skill/file.md` — absolute path to an installed plugin location\n\nWhy this matters:\n\n- **Runtime resolution:** Skills execute from the user's working directory, not the skill directory. Cross-directory paths and absolute paths will not resolve as expected.\n- **Unpredictable install paths:** Plugins installed from the marketplace are cached at versioned paths. Absolute paths that worked in the source repo will not match the installed layout, and the version segment changes on every release.\n- **Converter portability:** The CLI copies each skill directory as an isolated unit when converting to other agent platforms. Cross-directory references break because sibling directories are not included in the copy.\n\nIf two skills need the same supporting file, duplicate it into each skill's directory. Prefer small, self-contained reference files over shared dependencies.\n\n> **Note (March 2026):** This constraint reflects current Claude Code skill resolution behavior and known path-resolution bugs ([#11011](https://github.com/anthropics/claude-code/issues/11011), [#17741](https://github.com/anthropics/claude-code/issues/17741), [#12541](https://github.com/anthropics/claude-code/issues/12541)). If Anthropic introduces a shared-files mechanism or cross-skill imports in the future, this guidance should be revisited with supporting documentation.\n\n## Lean Repo Grounding\n\nUse the project's active instructions already in the main agent's context, then go directly to task-specific current evidence. Pass fresh subagents the relevant project and task context, or have them read the applicable current instruction source when operational rules affect their work. If a task cannot be scoped from that context, use one targeted probe. Do not create a reusable generic repo profile or run a default root, stack, or layout scan.\n\n## Platform-Specific Variables in Skills\n\nThis plugin is authored once and converted for multiple agent platforms (Claude Code, Codex, Gemini CLI, etc.). Do not use platform-specific environment variables or string substitutions (e.g., `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_SKILL_DIR}`, `${CLAUDE_SESSION_ID}`, `CODEX_SANDBOX`, `CODEX_SESSION_ID`) in skill content without a graceful fallback that works when the variable is unavailable or unresolved.\n\nHow a bundled-file reference resolves depends on *who* resolves it and whether a shell is involved, so references fall into three tiers. Do not assume a bare `scripts/…` path behaves the same in all three.\n\n**Tier 1 — Read-time file references (relative, no anchor):** When skill *content* points the agent at a co-located file to read (e.g., \"read `references/schema.yaml`\"), use a relative path from the skill root. The skill loader resolves these against the skill's own directory on all major platforms — no variable prefix needed. This is the rule in *File References in Skills* above.\n\n**Tier 2 — Prose pointers to a bundled file the agent acts on (relative + a \"from this skill's directory\" cue):** When skill prose names a bundled file the agent will use but does *not* put it in an executed shell command (e.g., \"drive the loop with `scripts/hitl-loop.template.sh`\" or \"generate the package with `scripts/review-package BASE HEAD`\"), use a relative path plus an explicit \"from this skill's directory\" phrase. The cue tells the agent what to resolve against without the verbosity of an anchor.\n\n**Tier 3 — Executed shell commands (the `SKILL_DIR` anchor):** When skill content puts a bundled script in a command the agent runs through the Bash tool — a fenced ` ```bash ` block **or** an inline `bash …` / `python …` — anchor it to the skill dir. The Bash tool's working directory is the user's **project**, not the skill directory, on Claude Code, Codex, and Cursor alike, so a bare `bash scripts/my-script.sh` resolves to `<project>/scripts/…`. Relative paths here *often still work* — a capable agent resolves them against the skill dir it loaded (which is how the agentskills.io spec and other ecosystems ship them) — but that relies on the agent translating the path, and the failure mode is a fenced block copied **verbatim** into a Bash call, which runs literally and misses (`exit 127`; recovery is a wasted round-trip that weaker models / mid-tier subagents botch). Anchoring bakes the resolution into the command, so it is **deterministic**. Use the anchor for executed shell as the house default — a conservative choice, not a claim that bare relative *cannot* work (recurring bug class: #764 `ce-worktree`, #811 `ce-code-review`, #898 `ce-compound`):\n\n```\n# set inline in the SAME command (shell state does not persist between Bash calls):\nSKILL_DIR=\"<absolute path of the directory containing the SKILL.md you just read>\";\nbash \"$SKILL_DIR/scripts/my-script.sh\" ARG\n```\n\n**Keep the trailing `;` on the assignment line.** Some hosts (observed on Codex) flatten a fenced multi-line block into a single line by replacing the newline with a space before executing it. Without the `;`, `SKILL_DIR=\"…\"` + newline + `bash \"$SKILL_DIR/…\"` collapses to the env-var-prefix form `SKILL_DIR=\"…\" bash \"$SKILL_DIR/…\"`, where the shell expands `$SKILL_DIR` *before* the prefix assignment takes effect — so it expands to empty and the script path becomes `/scripts/my-script.sh` (`No such file or directory`). The trailing `;` makes the assignment a complete statement that survives flattening; it is load-bearing, not a style choice, so do not remove it.\n\nAn existence guard (`if [ -f \"$SKILL_DIR/scripts/my-script.sh\" ]; then … else echo \"not found — re-check the SKILL.md path\"; fi`) is optional — useful when there's a real fallback, but see the permission caveat below before guarding a pinned call.\n\n`SKILL_DIR` is a **model-filled** value, not a harness variable: every harness loads SKILL.md from a real absolute path the agent knows, so the skill instructs the agent to set `SKILL_DIR` to that directory. This works identically on Claude Code, Codex, and Cursor precisely because it depends on no host-specific variable — `SKILL_DIR`, `CLAUDE_SKILL_DIR`, `CODEX_SKILL_DIR`, `AGENT_SKILL_DIR` are **not** env vars on any of them, yet the script runs because the agent supplies the path. This is the production pattern used by widely-installed cross-host skills (e.g. `last30days`). Two constraints: (1) shell state does **not** persist between separate Bash-tool calls, so `SKILL_DIR` cannot be set once and reused — each invocation must carry the absolute path (set it inline in the same command). (2) A script that needs its *own* directory (to read a sibling file) derives it from `BASH_SOURCE`, not `SKILL_DIR`, since `SKILL_DIR` is the orchestrator's shell var and is not exported to the child process — see `skills/ce-code-review/scripts/cross-model-adversarial-review.sh` for the reference implementation. `last30days` adopted this anchor for its critical multi-host engine after a path-resolution regression; it is the right tool when a script must run *reliably*, which is why it is the tier-3 default — but tiers 1 and 2 deliberately stay lighter.\n\n**Avoid `${CLAUDE_SKILL_DIR}` here — in this cross-agent plugin it is a footgun, not a neutral alternative.** Every skill in this repo is authored once and installed across Claude Code, Codex, Cursor, and Gemini, and `${CLAUDE_SKILL_DIR}` is a Claude-Code-only SKILL.md *content* substitution (not an env var) that is **empty on every other host**. So a `${CLAUDE_SKILL_DIR}`-guarded call's `then` branch quietly never fires off-Claude — the **genuine silent skip** — and a Claude-only mechanism breaks on Codex/Cursor because the converter doesn't rewrite these paths and the native Codex install loads raw `SKILL.md` (no `ce_platforms` filtering). The model-filled `SKILL_DIR` anchor works on every host, so it is the right replacement wherever a `${CLAUDE_SKILL_DIR}`-guarded executed-shell call exists today (tier 3). Do not reach for `${CLAUDE_SKILL_DIR}` as a \"portable\" option — it isn't. Reach for it only for behavior that is genuinely Claude-Code-only and will *never* run on another harness — which, given this plugin's cross-host install model, is essentially never; treat any new use as a smell to justify or remove.\n\nSo: a skill's *core* behavior **can** live in a bundled script across hosts — invoke it via the `SKILL_DIR`-from-read-path anchor. You no longer need to avoid bundled scripts for portability; anchor them instead. Read-time references (`references/*.md`) still resolve against the skill dir on all targets and need no anchor.\n\n**Permission caveat (Claude Code).** Claude Code's permission checker evaluates every subcommand of a compound command, and a bare `[ -f … ]` test is not pre-approved — so wrapping a pinned `bash \"…sh\"` call in an `if … then … fi` guard defeats a narrow `Bash(bash *…sh)` allow-rule and prompts on every run. If a bundled-script call must stay auto-approved via such a pin, keep it a single pinned command rather than guarding it inline. Note the model-filled `SKILL_DIR` anchor produces a *dynamic* absolute path that won't match a static `Bash(bash /…/scripts/x.sh)` pin regardless of guarding — so for the anchor, expect a one-time approval prompt per distinct command (or use a broader allow-rule); the static-pin trick mainly applies to the fixed `${CLAUDE_SKILL_DIR}` form.\n\n**Do not use `!` load-time pre-resolution in skills.** The `!`cmd`` SKILL.md syntax runs `cmd` at skill load and inlines its stdout, but it is banned here (enforced by `tests/skill-shell-safety.test.ts`) for two unfixable reasons: it runs **only on Claude Code** — on Codex, Cursor, Gemini, and Grok the line is inert literal text — and on Claude Code a command that exits **non-zero aborts skill load** with a user-facing error. Every real use was git context (`git rev-parse …`, `gh pr view …`) whose non-zero exit is a *normal* state (no PR yet, no `origin/HEAD`, detached HEAD, not a repo), so the ordinary case broke the skill. The POSIX guards that force exit 0 (`2>/dev/null || echo SENTINEL`) then fail to parse under Windows PowerShell 5.1, which broke skill load there instead (issue #1066). No single command string both exits 0 on the expected-failure states and parses under both POSIX sh and PowerShell, so the construct cannot be made safe.\n\n**Gather context at runtime instead.** Have the agent run one argv-style command per shell tool call (`git …`, `gh …`) — no `;`, `&&`, `||`, pipes, `$(…)`, or redirects — and interpret each exit status as control flow. This parses identically under POSIX sh and PowerShell because it is a single external-program invocation, and a non-zero exit becomes data the agent reads rather than a load-time abort. See `ce-commit` / `ce-commit-push-pr` for the pattern.\n\n**When a platform variable is unavoidable:** resolve it at runtime with a single shell tool call and include explicit fallback instructions, so the agent knows what to do if the value is empty, a literal command string, or an error — e.g. run `jq -r .version \"${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json\"`; if it resolved to a semantic version use it, otherwise fall back to the versionless behavior. This applies equally to any platform's variables — a skill converted from Codex, Gemini, or any other platform will have the same problem if it assumes platform-only variables exist without a fallback.\n\n## Repository Docs Convention\n\n- **Plans** live in `docs/plans/` — unified plan artifacts. New `ce-brainstorm` outputs are requirements-only unified plans (`artifact_readiness: requirements-only`); `ce-plan` enriches them to implementation-ready plans (`artifact_readiness: implementation-ready`). Historical `docs/brainstorms/*-requirements.*` files remain readable legacy inputs and should not be migrated just because a new plan is created.\n- **Brainstorm evidence / legacy requirements** may live in `docs/brainstorms/` — historical requirements docs and specialized analysis artifacts such as `docs/brainstorms/riffrec-feedback/`. Do not treat this as the canonical output path for new `ce-brainstorm` artifacts.\n- **Solutions** live in `docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas.\n- **Specs** live in `docs/specs/` — target platform format specifications.\n\n### Solution categories (`docs/solutions/`)\n\nThis repo builds a plugin *for* developers. Categorize solutions from the perspective of the end user (a developer using the plugin), not a contributor to this repo.\n\n- **`developer-experience/`** — Issues with contributing to *this repo*: local dev setup, shell aliases, test ergonomics, CI friction. If the fix only matters to someone with a checkout of this repo, it belongs here.\n- **`integrations/`** — Issues where plugin output doesn't work correctly on a target platform or OS. Cross-platform bugs, target writer output problems, and converter compatibility issues go here.\n- **`workflow/`**, **`skill-design/`** — Plugin skill and agent design patterns, workflow improvements.\n\nWhen in doubt: if the bug affects someone running `bun install compound-engineering` or `bun convert`, it's an integration or product issue, not developer-experience.\n","GEMINI.md":"# Compound Engineering\n\nThis Gemini extension provides the Compound Engineering skill set for planning,\nreview, implementation, debugging, and release workflows.\n\nUse the installed skills when they match the user's request. Treat this file as\nruntime context for extension users, not contributor guidance for this source\nrepository. Do not apply this repository's `AGENTS.md` maintainer workflow to\nthe user's project.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# Agent Instructions\n\nThis repository is the root of the `compound-engineering` coding-agent plugin and the marketplace/catalog metadata used to distribute it.\n\nIt also contains:\n- the Bun/TypeScript CLI that converts Claude Code plugins into other agent platform formats\n- shared release and metadata infrastructure for the CLI, marketplace, and plugin\n\n`AGENTS.md` is the canonical repo instruction file. Root `CLAUDE.md` is a symlink to `AGENTS.md` so Claude Code and other tools that look for `CLAUDE.md` still find it at the expected path. Keep that symlink (do not replace it with a regular file): a real root `CLAUDE.md` makes `claude plugin validate --strict` fail because this checkout is also the plugin root.\n\n## Quick Start\n\n```bash\nbun install\nbun run test              # full test suite (also runs in CI; `--parallel` across worker processes)\nbun run release:validate  # plugin/marketplace consistency (also runs in CI)\nbun run plugin:validate   # Claude marketplace + plugin schema (also runs in CI; needs `claude` on PATH)\n```\n\n### Codex Local Plugin Development\n\nWhen testing current skill files in Codex, run the repository workflow from the checkout or worktree you intend to test:\n\n```bash\nbun run codex:dev -- local    # link this worktree's skills and remove CE plugin installs\nbun run codex:dev -- status   # show local/remote state and checkout provenance\nbun run codex:dev -- remote   # restore the official marketplace-backed plugin\nbun run codex:dev -- remove   # remove both supported CE installation surfaces\n```\n\n`refresh` is an idempotent alias for `local`. Local mode manages only the exact `$CODEX_HOME/skills/compound-engineering-local` symlink and Compound Engineering plugin IDs; it must not alter unrelated user skills. The symlink includes modified and untracked files from the selected worktree. Start a new Codex session after switching installation modes. Current Codex versions detect direct skill edits automatically; restart only if an edit does not appear. For live local testing, use this workflow instead of adding the repository as a marketplace: a marketplace install caches a snapshot, while local mode links the current skill files.\n\n## Working Agreement\n\n- **Branching:** Create a feature branch for any non-trivial change. If already on the correct branch for the task, keep using it; do not create additional branches or worktrees unless explicitly requested.\n- **Merge policy:** All changes to `main` go through pull requests. Direct pushes and direct merges are not allowed; branch protection on `main` enforces this by requiring the `test` status check to pass. The direct path bypasses `release:validate`, the test suite, and PR title validation — past direct merges have caused version drift requiring multi-PR recovery (see `docs/solutions/workflow/release-please-version-drift-recovery.md`).\n- **Contribution gate (non-maintainers):** If you are not a repository maintainer or admin, do not open a PR without a linked issue — file the issue first and reference it from the PR. Adding a **new skill** has a stricter gate: non-maintainers and non-admins must raise a discussion in an issue and get explicit maintainer approval **before** starting the work; do not open a new-skill PR that has not been approved this way. Maintainers and admins are exempt from both gates but still follow the merge policy above.\n- **PR disclosure:** `.github/pull_request_template.md` ends with `## Security Disclosure` and `## Agent Disclosure` sections. Fill both when opening a PR — including PRs authored via `gh pr create --body`/`--body-file`, which bypass the template so nothing pre-fills them. State any security-relevant changes (or \"No security-relevant changes\"), and the model that did the bulk of the work — your harness plus the most specific model identity your own context gives you, e.g. `Claude Code · claude-opus-4-8` or `Codex CLI · GPT-5`. Copy an exact model ID verbatim when your harness states one; when it exposes only a generic family, report the family and stop. Measured 2026-07-24: Codex and Cursor agents cannot see their running model at all (Codex's \"based on GPT-5\" is fixed boilerplate), so do not upgrade a family to a version, and do not read config files for one — the configured default is often not the model actually running. Never invent a version or variant. The body above those sections stays freeform — add whatever sections best explain the change.\n- **Safety:** Do not delete or overwrite user data. Avoid destructive commands.\n- **Testing:** Run `bun run test` after changes that affect parsing, conversion, output, skill conventions, or other mechanical guards. Local `bun run test` is the same suite CI runs — there is no separate local-only unit-test lane. Prefer it over bare `bun test`: the package script carries `--parallel`, which is where the suite's speed comes from. Bare `bun test <file>` is still the right tool for iterating on one file.\n- **Release versioning:** Releases are prepared by release automation, not normal feature PRs. The repo has one root plugin/package release component (`compound-engineering`) plus marketplace components (`marketplace`, `cursor-marketplace`). GitHub release PRs and GitHub Releases are the canonical release-notes surface for new releases; root `CHANGELOG.md` is only a pointer to that history. Use conventional titles such as `feat:` and `fix:` so release automation can classify change intent, but do not hand-bump release-owned versions or hand-author release notes in routine PRs.\n- **Output Paths:** Keep OpenCode output at `opencode.json` and `.opencode/{agents,skills,plugins}`. For OpenCode, commands go to `~/.config/opencode/commands/<name>.md`; `opencode.json` is deep-merged (never overwritten wholesale).\n- **Scratch Space:** Default to OS temp. Use `.context/` only when explicitly justified by the rules below.\n  - **Default: OS temp** — covers most scratch, including per-run throwaway AND cross-invocation reusable, regardless of whether a repo is present or whether other skills may read the files. A stable OS-temp prefix handles cross-skill and cross-invocation coordination equally well as an in-repo path; repo-adjacency is rarely the relevant property.\n    - **Per-run throwaway**: `mktemp -d \"${TMPDIR:-/tmp}/<prefix>-XXXXXX\"` (OS handles cleanup). Use for files consumed once and discarded — captured screenshots, stitched GIFs, intermediate build outputs, recordings, delegation prompts/results, single-run checkpoints. Always pass an explicit template under `${TMPDIR:-/tmp}`. Do not use bare `mktemp`, bare `mktemp -d`, `mktemp -t`, or `mktemp -d -t`: those forms ignore `$TMPDIR` on macOS and can resolve outside a sandbox's writable temp directory.\n    - **Cross-invocation reusable**: use a stable, effective-user-owned prefix under `/tmp/compound-engineering-<effective-uid>/<skill-name>/` — **not** `mktemp -d` — so later invocations by the same OS user can find prior outputs without sharing a writable root with other users. Derive the effective UID with `id -u`, reject a symlink or path not owned by the current user, and create or repair the top-level root to mode `0700` before use. **Probe before committing to `/tmp`:** when that root cannot be created, is not yours, or is not writable, use `${TMPDIR:-/tmp}/compound-engineering-<effective-uid>` instead — the same rule, in the same order, in every shell preamble and Python default, so a later invocation resolves the same root. Claude Code's macOS sandbox allowlists writes under `$TMPDIR` (`/tmp/claude-<uid>`) but not `/tmp` itself, so without the fallback every skill's scratch setup aborts there with `Operation not permitted`; and an existing root from an unsandboxed session passes `mkdir -p` as a no-op yet refuses the first write, which is why the probe is a writability check (`[ -w ]`), not creation alone. Copy the block from any shipped skill (for example `skills/ce-compound/SKILL.md`) rather than re-deriving it; `tests/scratch-root-preamble-executes.test.ts` runs every copy, including the fallback. The default layout is one `<scratch-root>/<skill-name>/<run-id>/` directory per run; use it for caches keyed by session, checkpoints meant to survive context compaction, intermediate state, and outputs whose lifecycle or mutation belongs to one run.\n      - **Discoverable collection exception**: omit the per-run directory only when later invocations intentionally enumerate multiple sibling **final artifacts** as core product behavior and run isolation would materially worsen discovery or the user-facing path. Use a stable collection namespace (for example, repository identity plus a `general` fallback), descriptive immutable filenames, metadata that supports ranking, and no-overwrite collision handling that atomically reserves the final filename and retries with the next suffix on collision; never check availability and then write. Do not use this exception for caches, checkpoints, intermediate files, or merely to shorten a path.\n      - Prefer `/tmp` over `$TMPDIR` so paths stay accessible: `$TMPDIR` on unsandboxed macOS resolves to `/var/folders/64/.../T/`, which is hostile for users who want to inspect checkpoints, grep them, or copy them out — which is why `$TMPDIR` is the fallback, taken only when `/tmp` cannot host the root, and never the first choice. The explicit effective-UID segment supplies the required cross-user boundary while preserving a readable path. Agents running as the same OS user intentionally remain in one discretionary-access-control principal.\n  - **Exception: `.context/`** — use only when the artifact is genuinely bound to the CWD repo AND meets at least one of:\n    - (a) **User-curated**: the user is expected to inspect, manipulate, or manually curate the artifact outside the skill (e.g., a per-repo TODO database, a per-spec optimization log that survives across sessions on the same checkout).\n    - (b) **Repo+branch-inseparable**: the artifact's meaning is inseparable from this specific repo or branch (e.g., branch-specific resume state that a user expects to pick up again in the same checkout).\n    - (c) **Path is core UX**: surfacing the artifact path back to the user is a core part of the skill's output and that path is easier to communicate as a repo-relative location than an OS-temp one.\n    Namespace under `.context/compound-engineering/<workflow-or-skill-name>/`, add a per-run subdirectory when concurrent runs are plausible, and decide cleanup behavior per the artifact's lifecycle (per-run scratch clears on success; user-curated state persists). \"Shared between skills\" is not by itself sufficient — OS temp handles that equally well.\n  - **Durable outputs** (plans, specs, learnings, docs, final deliverables) belong in `docs/` or another repo-tracked location, not in either scratch tier.\n  - **Cross-platform note:** `/tmp` is writable on macOS (symlink to `/private/tmp`), Linux, and WSL. For per-run throwaway files, use an explicit `${TMPDIR:-/tmp}` template so macOS and sandboxed hosts honor the selected temp parent. Skills authored here assume Unix-like shells (bash on macOS/Linux, or Git Bash on Windows). Native Windows is a supported target for Python interpreter resolution and peer-job detach — never hardcode `python3`; probe execution per `docs/solutions/conventions/resolve-python-interpreter-not-python3.md`.\n- **Character encoding:**\n  - **Identifiers** (file names, agent names, command names): ASCII only -- converters and regex patterns depend on it.\n  - **Markdown tables:** Use pipe-delimited (`| col | col |`), never box-drawing characters.\n  - **Prose and skill content:** Unicode is fine (emoji, punctuation, etc.). Prefer ASCII arrows (`->`, `<-`) over Unicode arrows in code blocks and terminal examples.\n\n## Directory Layout\n\n```\nsrc/              CLI entry point, parsers, converters, target writers\nskills/           Compound Engineering plugin skills\n.claude-plugin/   Claude plugin manifest and marketplace catalog metadata\n.codex-plugin/    Codex plugin manifest\n.cursor-plugin/   Cursor plugin manifest and marketplace catalog metadata\n.opencode/        OpenCode package entrypoint and install docs\n.pi/              Pi extension entrypoint\ntests/            Converter, writer, and CLI tests + fixtures\ndocs/             Requirements, plans, solutions, and target specs\nCONCEPTS.md       Shared domain vocabulary (glossary of project-specific terms)\n```\n\n## Repo Surfaces\n\nChanges in this repo may affect one or more of these surfaces:\n\n- root plugin content under `skills/`, `AGENTS.md`, `README.md`, and platform manifests\n- marketplace catalogs under `.claude-plugin/`, `.cursor-plugin/`, and `.agents/plugins/`\n- the converter/install CLI in `src/` and `package.json`\n\nDo not assume a repo change is \"just CLI\" or \"just plugin\" without checking which surface owns the affected files.\n\n## Plugin Maintenance\n\nWhen changing plugin content:\n\n- Update substantive docs like `README.md` when the plugin behavior, inventory, or usage changes.\n- When adding a user-facing skill, document it: create a `docs/skills/<skill-name>.md` page (purpose, novel mechanics, when to use, chain position — follow the shape of the existing pages) and add a catalog row under the right category in `docs/skills/README.md`, alongside the root `README.md` inventory row and the skill-count bump in `tests/release-metadata.test.ts`. Keep these in sync when a skill's purpose or inventory changes. This is convention, not yet validated by a test, so it is easy to miss — most skills have a page; the few that don't (e.g. `lfg`, `ce-dogfood-beta`) are the exception, not the rule.\n- When adding, removing, renaming, or changing the meaning/default/consumer of a `.compound-engineering/config.yaml` option, update `skills/ce-setup/references/config-template.yaml`, its byte-identical `.compound-engineering/config.example.yaml` copy, the centralized `docs/skills/configuration.md` reference, and the affected consumer skill docs in the same change. Ordinary keys may also live in optional checkout-local `config.local.yaml` (overrides the repo file). `docs_root` belongs only in `config.yaml`. Durable team instructions still belong in the project's normal agent-instructions mechanism.\n- Do not hand-bump release-owned versions in plugin or marketplace manifests.\n- Do not hand-add release entries to `CHANGELOG.md` or treat it as the canonical source for new releases.\n- Run `bun run release:validate` if agents, commands, skills, MCP servers, or release-owned descriptions/counts may have changed.\n- When removing a skill, agent, or command, add its name to both cleanup registries so stale flat-install artifacts are swept on upgrade:\n  - `STALE_SKILL_DIRS` / `STALE_AGENT_NAMES` / `STALE_PROMPT_FILES` in `src/utils/legacy-cleanup.ts`\n  - `EXTRA_LEGACY_ARTIFACTS_BY_PLUGIN[\"compound-engineering\"]` in `src/data/plugin-legacy-artifacts.ts`\n\nUseful validation commands:\n\n```bash\nbun run release:validate\ncat .claude-plugin/marketplace.json | jq .\ncat .claude-plugin/plugin.json | jq .\n```\n\n## Runtime vs Authoring Context\n\n`AGENTS.md`, `CLAUDE.md` (symlink to `AGENTS.md`), and `GEMINI.md` are authoring context for this source repository. Skills are installed into end-user environments, where they run against the user's local instruction files, not this repo's. Behavioral rules that must affect a skill at runtime belong in that skill's `SKILL.md` or files under its own `references/` directory.\n\n## Working on Skills\n\nThis repository authors each skill once and distributes it across multiple agent models and harnesses. A skill is a set of goals, not a state machine: it hands the agent the goal, the done condition, the safe failure direction, and the facts it cannot derive from the repo in front of it, then gets out of the way. The reading agent is expected to reason to the mechanics from there. Before creating, materially revising, or reviewing a skill or skill-local persona, read `docs/solutions/skill-design/portable-agent-skill-authoring.md`, applying only the sections relevant to the skill; the rules in this file supplement it and take precedence where they are more specific. The four activities below — authoring, editing, reviewing, and acting on review — are one lifecycle and share one standard, so a rule stated for one applies to the others unless it says otherwise.\n\n### Authoring a new skill\n\n- **State conditions, not procedures.** Write what must be true (goal, done, safe direction) and the non-derivable facts. When you find yourself adding cases to a block to make it hold, name the condition those cases are proxies for and state that instead. Read `docs/solutions/skill-design/skill-gates-state-conditions-not-prescribed-git-commands.md` for the canonical failure.\n- **Prescribe a mechanism only where it is owned.** A skill spells out commands, exit semantics, or state transitions for the mechanics it owns (`ce-commit-push-pr` owns PR detection) and for deterministic work that is cheap to compute but costly to reason to (a data-processing script, a path anchor). A skill that delegates that work states the condition that must hold, the safe failure direction, and the non-derivable facts about the callee -- never a re-derivation of the callee's commands. Prescribed commands in a delegating skill encode one configuration and are wrong for the next one.\n- **Prose admission.** Keep a line only when it states a falsifiable constraint, counters a known default tendency or observed shortcut, or supplies domain knowledge that materially changes a decision. Do not keep vague effort or quality language (\"be thorough\") as a standalone instruction; replace it with an observable rule, or retain a targeted effort cue only when it counters a documented runtime tendency and has been evaluated there. Do not append motivational rationale to a directive that already stands on its own. Repeat an instruction only at a demonstrated drift point where placement changes whether it fires, and protect genuinely required always-loaded duplicates with a parity test.\n- **User-facing invocations.** Keep agent-to-agent or skill-to-skill routing semantic: format formal skill names as inline code (for example, `ce-plan`) and invoke the named skill through the active harness's callable skill mechanism. When a skill prints or copies a user-runnable invocation, default to `/skill-name`; use `$skill-name` only when the active harness is Codex or explicitly documents dollar-prefixed skill invocation. On oh-my-pi (`omp`), keep the default form for model-visible targets; use native `/skill:<name>` only when the target is not model-visible because it declares `disable-model-invocation` or `hide` (for example, `/skill:ce-polish`). In prose, render only the invocation as inline code; use a fenced block only when the command stands alone. Output exactly one form. Do not apply this rendering rule to built-in commands such as `/goal`. At runtime, put the smallest self-contained rendering rule immediately before the smallest section that contains all affected user-copy seams; do not repeat it in every step, only in a separately loaded reference that independently owns output.\n- **Loading and placement.** Keep every load-bearing action, route, and reference-load instruction inline at the point where it must fire (`docs/solutions/skill-design/post-menu-routing-belongs-inline.md`). Do not inline a summary complete enough to suppress loading the authoritative reference. Extract a block to `references/` when it is conditional or late-sequence and a meaningful share of the skill (~20%+), replacing it with a 1-3 line condition and backtick path. Never use `@` for an extracted block; it inlines at load time and defeats extraction.\n\n### Editing an existing skill\n\nSkills predate the current authoring standard and evolve toward it; the standard is the guide above, not the text around your edit.\n\n- **Bring the block you touch up to the standard.** When a change lands in a block written as a procedure, a menu, or an enumeration of cases, restate that block as its conditions as part of the change rather than matching the old shape. Matching a neighboring procedure because it is there is how procedures propagate.\n- **Scope the rewrite to your change plus what it makes wrong.** Reconcile the blocks your change contradicts or duplicates; leave untouched blocks alone even when they fall short of the standard, and name them in the PR as follow-up. A repo-wide modernization is its own change, requested explicitly.\n- **Repeated case-specific repair is the defect signal.** When a block keeps absorbing \"add the case we just found\" -- in authoring, in a review round, or in your own fix to a finding -- the representation is wrong, usually a procedure that should be a condition. Delete the additions and restate the goal, then re-verify the shorter rule against every path the additions served: a condensed rule that no longer names a path is a new defect, not a simplification.\n\n### Reviewing a skill change (bots and humans)\n\nReview bots read this file when reviewing a PR here. On `skills/**`:\n\n- **A finding is a gap in the goal, the done condition, or the safe failure direction, or a mechanism at the wrong owning layer** — commands prescribed in a skill that delegates that work, a rule placed where it will not fire, a Claude-only construct in a cross-host skill, a rendering that breaks on another harness.\n- **A case a stated condition already covers is not a finding.** Before filing \"what if X\" against a rule, check whether the rule's condition decides X. \"Rename only on positive proof the branch was never published; any other result keeps the name\" already decides an unreachable remote. If it does, do not file; if the condition is wrong or missing, file *that*.\n- **State the requested fix as a condition or an owning-layer move, never as a case to add.** \"This probe fails open on network error\" is a correct observation; the fix to request is \"state the condition\" or \"delete the probe\", not \"also check the exit code\". \"Command X fails in state Y\" against a delegating skill is a finding about the representation; the fix is to drop the command and state the condition, not to correct the command.\n- **A block restated to the standard is the expected shape of an edit**, not scope creep, when the restatement covers every path the old text served.\n- Ordinary code under `src/`, `tests/`, and `scripts/` gets ordinary code review; these rules are about instruction prose.\n\n### Acting on review feedback\n\nApplying review, peer, or eval feedback to a skill is a material revision under the same standard. An item is not addressed because a sentence landed; it is addressed when a demonstrated gap is closed at its owning layer by the smallest mechanism. Skill prose is not code: a natural-language instruction can always be made more specific, so a reviewer can produce a valid-looking edge case against any condition indefinitely, and patching each one dilutes the instruction. Measured 2026-08-15 (#1397): a two-condition setup step absorbed 24 bot findings over nine rounds, most of them cases against text the previous round had added, before being restated as the two conditions it began as. Before editing:\n\n1. **Evidence** — classify each item as Change, Verify, or Consider using the guide's evidence rules. Do not edit the skill for Verify or Consider items. A case the stated condition already decides is Verify at most: answer it with the condition (`not-addressing` quoting it, or `replied` for a question), do not patch. \"Default to fixing\" is the right rule for code; on skill prose the default for a case-level finding is to point at the condition.\n2. **Owning layer** — for each Change, identify its owning layer: activation contract, outcome spine or skill boundary, runtime protocol, loading or placement, deterministic enforcement, or shared authoring rule. Several fixes in #1397 belonged in the callee skills (`ce-commit`, `ce-commit-push-pr`), not in the calling skill's prose.\n3. **Mechanism** — fix the gap at its owning layer. Add prose only when it is the smallest mechanism that closes the gap, and then only the smallest falsifiable unit per prose admission.\n4. **Reconcile** — reread the affected block; remove or rewrite text the change makes conflicting, duplicated, or obsolete. Resolve conflicting feedback items rather than stacking both.\n5. **Stop the accretion loop** — when a finding targets text an earlier round added, delete or restate that addition rather than qualifying it. On the second round against the same block, stop patching: restate the block as its goal, done condition, and safe direction and re-verify against every path the additions served. This holds whether the rounds arrive in one review or across a babysit loop's re-invocations.\n\nWhen evidence shows the same cause across skills, fix the shared guide, rule, or mechanism unless the skills' contracts materially differ. For a multi-item round, record one line per item in the existing PR body or work note: `item -> Change|Verify|Consider | owning layer | mechanism - why`. A single-item fix still follows the steps above; the written line is optional. Reviewer wording is a hypothesis about mechanism, not authority over it — the reviewer's one-line prose fix is sometimes exactly right.\n\n## Referencing Project Conventions in Skills\n\nWhen a skill needs to discover a project convention at runtime — the issue tracker, coding standards, commit format, lint command, scope constraints, etc. — describe **what to look for in the agent's existing context**, not **which file to open**.\n\n**On the read path, do not name instruction files (`AGENTS.md` / `CLAUDE.md` / `GEMINI.md` / `.cursor/rules`).** Phrase it as \"the project's active instructions and conventions already in your context.\" Three reasons:\n\n- **Redundant.** Every major harness auto-injects the project's root instruction file into context at session start (Claude Code loads `CLAUDE.md`, Codex `AGENTS.md`, Gemini `GEMINI.md`). Telling the agent to \"read `AGENTS.md`\" asks it to re-open content it already has.\n- **Brittle / not portable.** The filename differs per harness, and this plugin is authored once and converted to all of them. A hardcoded \"read `AGENTS.md` (or `CLAUDE.md`)\" silently finds nothing on a harness that uses a different name.\n- **Security smell.** Instructing an agent to go *read named instruction dotfiles* is the exact shape that prompt-injection defenses in some agent frameworks (e.g., Hermes) flag. Referencing context rather than filenames avoids tripping those guards.\n\n**Name a concrete file only where the skill must do something a context reference can't express:**\n\n- **Writing a convention back** (e.g., persisting `project_tracker: linear`) needs a target — name it minimally and as an example (\"the project's root agent-instructions file, e.g., `AGENTS.md`; if it `@`-includes another, write to the substantive one\").\n- **Reading content that is genuinely not auto-loaded** — a subdirectory-scoped instruction file governing the area being changed, an optional project doc like `STRATEGY.md` / `CONCEPTS.md` / `README.md`, or any file a *fresh subagent* (which does not inherit the parent's loaded instructions) must open to do its job. Auditing tools that must enumerate every standards file (e.g., `ce-code-review`'s project-standards reviewer globbing all `CLAUDE.md`/`AGENTS.md`) are a legitimate exception — they review the files, they don't re-read them for context.\n\n**Describe the capability, not the tool.** Pair this with naming the *category* of thing rather than a closed set: \"the project's issue tracker (e.g., GitHub Issues, Linear, Jira)\" and \"whatever interface that tracker exposes (connector/MCP, documented API, or a documented CLI)\" — never assume a specific CLI exists, and never treat a missing binary / env var / MCP server as proof the capability is unavailable.\n\n## Validating Agent and Skill Changes\n\nBehavioral changes to a plugin skill or skill-local persona (anything under `skills/`) need a different validation path than mechanical code changes, because of how Claude Code loads plugins.\n\n- **Use the `skill-creator` skill to test changes.** Skill-creator is purpose-built for this: it spawns a generic subagent and injects the agent or skill content into the subagent's prompt at dispatch time, so each run reads the current source from disk. Invoke `/skill-creator` and use its eval workflow rather than reaching for ad-hoc workarounds.\n\n- **Plugin agent and skill definitions both cache at session start.** Once a Claude Code session is open, dispatching a typed plugin agent runs the in-memory copy that was loaded when the session began. The same applies to skills: invoking a skill goes through the cached skill loader, so edits to skill scripts are also not tested via that path. File edits to either layer after session start do not propagate within the same session. Any iteration loop built around typed-agent dispatch or Skill-tool invocation in the same session is testing pre-edit content, not your changes.\n\n- **Do NOT edit `~/.claude/plugins/cache/` or `~/.claude/plugins/marketplaces/` to try to force a reload.** Those paths are user machine state, not repo-managed. Modifying them does not reliably bypass the in-session cache (it didn't, in observed behavior), risks being silently overwritten by plugin updates, and is the wrong layer to test from. The skill-creator pattern is the proper approach; if you genuinely need fresh-loaded behavior of the typed-agent dispatch path, restart the Claude Code session — but skill-creator is preferred for fast iteration.\n\n- **A version-matched cache is not automatically stale — confirm by content, not by version.** When this working tree is the local marketplace source, a session (re)start re-copies it into `~/.claude/plugins/cache/.../compound-engineering/<version>/` (a plain copy, no `.git`; `<version>` is the working tree's `.claude-plugin/plugin.json` version), so the loaded plugin can be identical to — and as current as — your edits. Do not assume the running copy is stale just because it lives under the cache path; equally, do not assume a matching `<version>` means it includes your latest change. Version match is necessary but not sufficient: edits within a release do not bump the version, so a matching segment proves only that the cache was built from this release, not that it captured your most recent edit. To know which copy is actually loaded, diff the specific cache file against the working-tree file — identical means the running plugin is your current edit and you can trust it; differing means the session predates the edit, so restart (or use skill-creator). Never infer \"stale\" or \"current\" from the version segment alone.\n\n- **Mechanical changes do not have this restriction.** Skill scripts (e.g., `extract-metadata.py`), parser logic, conversion code, and anything `bun test` exercises always run the current source. The caching issue only affects LLM-driven skill prose behavior dispatched through the plugin loader.\n\n## CI and Quality Gates\n\nPR CI (`.github/workflows/ci.yml`) is the merge gate. It runs, in order: PR-title lint (PRs only), `bun run release:validate`, `bun run plugin:validate`, and `bun run test`. Do not invent a parallel local-only mechanical suite — if a check is deterministic and should block merges, put it in one of those steps (usually `bun run test`).\n\nThe `test` script runs `bun test --parallel`, which distributes test *files* across worker processes (one file still runs its own tests serially, and `--parallel` implies `--isolate`). This is the single biggest lever on CI wall time, because most of the suite is spent blocked on subprocesses — `python3`, `bash`, `git`, and `bun run src/index.ts` — not on CPU. Keeping it in the package script rather than the workflow means CI and a contributor's local run cannot drift apart.\n\nThat makes cross-file isolation load-bearing rather than incidental: a test file may not depend on another file's leftovers, and any test that writes outside its own `mktemp` directory is a latent flake. There are no exceptions — a test that needs a dirty tree builds a throwaway git repo for it.\n\n**A test that runs a bundled script which inspects the repository must point that script at a throwaway repo, never this checkout.** Otherwise the developer's uncommitted work becomes test input. `tests/skills/ce-code-review-cross-model-routes.test.ts` ran the real review script against `git diff HEAD` in the checkout, so any uncommitted change over roughly 160KB crossed the script's large-diff threshold and failed 31 of its tests for reasons unrelated to the change under test. CI never saw it, because CI runs on a committed tree. The fixture pattern is `dirtyFixtureRepo()` in that file: `git init` a temp dir, two commits so `HEAD~1` resolves, then one staged edit.\n\n**Do not pin a worker count.** `--parallel` with no value tracks the runner's core count, which is what you want. Raising it looks free — the suite is idle-bound, so more workers should pack better — but it was measured on CI and it is not: at `--parallel=8` on a 4-core runner, wall time improved ~9% (102s -> 93s) while total test-CPU inflated from 223s to 343s, and five tests crossed the 5000ms default per-test timeout. That converts runner busyness into red builds. A file that legitimately runs for seconds should call `setDefaultTimeout` instead, as the subprocess-heavy suites do.\n\nA file never splits across workers, so an oversized file sets a floor. `tests/skills/ce-work-unit-workspace.test.ts` was 4,564 lines and 86 tests under one `describe`; it is now five `ce-work-unit-workspace-*.test.ts` files sharing `tests/skills/helpers/ce-work-workspace-harness.ts`. Measured with three `workflow_dispatch` runs per ref in the same window, the `Run tests` step went from a median of 88s (87/112/88) to 81s (83/80/81).\n\n**Splitting bought ~8% of CI wall time and most of the run-to-run variance** — baseline spread 25s, split spread 3s. That second effect is the durable one: a 60s serial file makes wall time depend on which worker takes it, so a busy runner produced the 112s outlier. Locally the same split is much larger (160s -> 75s), because a developer machine has enough cores for the long file to be the whole critical path.\n\n**Do not use `bun test --parallel=4` on a many-core laptop as a CI proxy.** It predicted a 26% CI win where the real number was 8%: capping bun to four workers still leaves the other cores absorbing the `git` / `python3` / `bash` subprocess load, so it does not behave like a 4-core runner. Dispatch the real workflow on both refs instead.\n\nAt 81s the suite is nearer CPU-bound on a 4-core runner than bounded by its slowest file, so further file splitting has small returns; `tests/ce-babysit-pr-snapshot.test.ts` (~36s) is the largest remaining file and was deliberately left intact.\n\n**Size a test file by its measured time, not its line count.** A file is a wall-time problem when it approaches the suite's slowest-file ceiling. Roughly a thousand lines under one `describe` is a smell worth measuring, never a threshold to split at on sight — splitting a file that already runs well under the ceiling buys nothing. Get per-file times before deciding:\n\n```bash\nbun test --parallel --reporter=junit --reporter-outfile=/tmp/t.xml\n```\n\nThe `ce-work-unit-workspace-*` shards run 10-23s each against that ~36s ceiling, so two of them sitting just over a thousand lines is fine and they are deliberately left whole. Put shared fixtures in `tests/skills/helpers/`.\n\n### What belongs where\n\n| Kind of check | Where it lives | Notes |\n|---|---|---|\n| Deterministic invariants (frontmatter, parity, path safety, script behavior, converter/writer output, greppable skill contracts) | `bun test` / `release:validate` / `plugin:validate` | Must pass in CI |\n| Skill *prose behavior* (routing judgment, restraint, cross-model peer outcomes) | `skill-creator` eval, local / PR evidence | Not a CI job; non-deterministic and needs a model |\n\nThat split is intentional. See `docs/solutions/skill-design/portable-agent-skill-authoring.md` (\"Evaluate proportionally\"). Mechanical checks belong in CI; behavioral agent evals are best-effort evidence, not an exhaustive CI matrix.\n\n### Right-size new mechanical guards\n\nWhen a review bot or human finds a greppable invariant that `bun test` missed:\n\n1. Prefer **tightening an existing guard** over adding a new suite (e.g. widen a regex that already documents the rule).\n2. Pin the **smallest falsifiable unit** — a token, enum, path, heading, or one fixture that would have failed on the regressing diff. Do not snapshot whole skill bodies or pin incidental wording.\n3. If the failure needs an LLM to judge, keep it in skill-creator; do not fake it as a brittle string test.\n\n### Maintaining `plugin:validate`\n\n- `package.json` `plugin:validate` must validate **both** the marketplace catalog and the plugin manifest, with `--strict` on each. Paths: `.claude-plugin/marketplace.json` and `.claude-plugin/plugin.json`. Do **not** use `claude plugin validate .` — that resolves this repo as a marketplace only (because `.claude-plugin/marketplace.json` exists with `source: \"./\"`) and skips plugin-root checks.\n- CI pins `@anthropic-ai/claude-code` for reproducible schema rules. Bump the pin deliberately when adopting new upstream rules; do not float `@latest`.\n- Root `CLAUDE.md` must remain a **symlink** to `AGENTS.md` (path stays at the repo root where contributors expect it). Upstream warns on a regular-file plugin-root `CLAUDE.md` because it is not loaded as end-user project context; the symlink avoids that warning so `--strict` can stay on. Do not replace the symlink with a regular `@AGENTS.md` shim or relocate the file just for validators.\n- If `--strict` starts failing again on `CLAUDE.md` after an upstream bump, check whether the symlink was materialized into a regular file (Windows/`core.symlinks=false` checkouts) or whether the validator started following symlinks — fix the layout or pin, do not silently drop `--strict`.\n\n### When CI comments look \"stale\"\n\nIf CI claims a deferred warning or a missing gate, reproduce with the **pinned** `claude` version against `.claude-plugin/plugin.json` before treating the comment as current. Marketplace-only validation can hide plugin warnings.\n\n## Coding Conventions\n\n- Prefer explicit mappings over implicit magic when converting between platforms.\n- Keep target-specific behavior in dedicated converters/writers instead of scattering conditionals across unrelated files.\n- Preserve stable output paths and merge semantics for installed targets; do not casually change generated file locations.\n- When adding or changing a target, update fixtures/tests alongside implementation rather than treating docs or examples as sufficient proof.\n\n## Commit Conventions\n\n- **Prefix is based on intent, not file type.** Use conventional prefixes (`feat:`, `fix:`, `docs:`, `refactor:`, etc.) but classify by what the change does, not the file extension. Files under `skills/` and plugin manifests are product code even though they are Markdown or JSON. Reserve `docs:` for files whose sole purpose is documentation (`README.md`, `docs/`, `CHANGELOG.md`).\n- **Type selection — classify by intent, not diff shape.** Where `fix:` and `feat:` could both seem to fit, default to `fix:`: a change that remedies broken or missing behavior is `fix:` even when implemented by adding code, and net additions do not turn a fix into a `feat:`. Reserve `feat:` for capabilities the user could not previously accomplish where nothing was broken. Other conventional types (`chore:`, `refactor:`, `docs:`, `perf:`, `test:`, `ci:`, `build:`, `style:`) remain primary when they describe the change more precisely than either. Heuristic: if a regression test you could write today would have failed *before* the change, it's `fix:`. The user may override this default for a specific change.\n- **Include a component scope.** The scope appears verbatim in the changelog. Pick the narrowest useful label: skill/agent name (`document-review`, `learnings-researcher`), CLI or marketplace area (`cli`, `marketplace`), or shared area when cross-cutting (`review`, `research`, `converters`). Never use `compound-engineering` — it's the entire plugin and tells the reader nothing. Omit scope only when no single label adds clarity.\n- **Never use `!` or a `BREAKING CHANGE:` footer without explicit user confirmation.** These markers trigger release-please's automatic major version bump — a decision the user may not want even when a change is technically breaking. If a change appears breaking, surface that to the user and let them decide whether to apply the marker.\n\n## Adding a New Target Provider\n\nOnly add a provider when the target format is stable, documented, and has a clear mapping for tools/permissions/hooks. Use this checklist:\n\n1. **Define the target entry**\n   - Add a new handler in `src/targets/index.ts` with `implemented: false` until complete.\n   - Use a dedicated writer module (e.g., `src/targets/codex.ts`).\n\n2. **Define types and mapping**\n   - Add provider-specific types under `src/types/`.\n   - Implement conversion logic in `src/converters/` (from Claude → provider).\n   - Keep mappings explicit: tools, permissions, hooks/events, model naming.\n\n3. **Wire the CLI**\n   - Ensure `convert` and `install` support `--to <provider>` and `--also`.\n   - Keep behavior consistent with OpenCode (write to a clean provider root).\n\n4. **Tests (required)**\n   - Extend fixtures in `tests/fixtures/sample-plugin`.\n   - Add spec coverage for mappings in `tests/converter.test.ts`.\n   - Add a writer test for the new provider output tree.\n   - Add a CLI test for the provider (similar to `tests/cli.test.ts`).\n\n5. **Docs**\n   - Update README with the new `--to` option and output locations.\n\n## Specialist Prompt Assets in Skills\n\nThe compound-engineering plugin no longer ships standalone agent definitions under `agents/`. When a skill needs a specialist persona, store it inside that skill directory, usually under `references/agents/` or `references/personas/`, and have the calling skill dispatch a generic subagent with that file's contents in the prompt.\n\nInternal prompt asset file names should be descriptive and unprefixed because they are not externally exposed agent names.\n\nExample:\n- `references/agents/learnings-researcher.md` (correct)\n- `references/agents/ce-learnings-researcher.md` (wrong for an internal prompt asset)\n\nThese prompt assets must not include YAML frontmatter. Model selection, tool constraints, and dispatch policy belong in the calling skill's `SKILL.md`, not in the prompt asset.\n\n## File References in Skills\n\nEach skill directory is a self-contained unit. A SKILL.md file must only reference files within its own directory tree (e.g., `references/`, `assets/`, `scripts/`) using relative paths from the skill root. Never reference files outside the skill directory — whether by relative traversal or absolute path.\n\nBroken patterns:\n\n- `../other-skill/references/schema.yaml` — relative traversal into a sibling skill\n- `/home/user/compound-engineering-plugin/skills/other-skill/file.md` — absolute path to another skill\n- `~/.claude/plugins/cache/marketplace/compound-engineering/1.0.0/skills/other-skill/file.md` — absolute path to an installed plugin location\n\nWhy this matters:\n\n- **Runtime resolution:** Skills execute from the user's working directory, not the skill directory. Cross-directory paths and absolute paths will not resolve as expected.\n- **Unpredictable install paths:** Plugins installed from the marketplace are cached at versioned paths. Absolute paths that worked in the source repo will not match the installed layout, and the version segment changes on every release.\n- **Converter portability:** The CLI copies each skill directory as an isolated unit when converting to other agent platforms. Cross-directory references break because sibling directories are not included in the copy.\n\nIf two skills need the same supporting file, duplicate it into each skill's directory. Prefer small, self-contained reference files over shared dependencies.\n\n> **Note (March 2026):** This constraint reflects current Claude Code skill resolution behavior and known path-resolution bugs ([#11011](https://github.com/anthropics/claude-code/issues/11011), [#17741](https://github.com/anthropics/claude-code/issues/17741), [#12541](https://github.com/anthropics/claude-code/issues/12541)). If Anthropic introduces a shared-files mechanism or cross-skill imports in the future, this guidance should be revisited with supporting documentation.\n\n## Lean Repo Grounding\n\nUse the project's active instructions already in the main agent's context, then go directly to task-specific current evidence. Pass fresh subagents the relevant project and task context, or have them read the applicable current instruction source when operational rules affect their work. If a task cannot be scoped from that context, use one targeted probe. Do not create a reusable generic repo profile or run a default root, stack, or layout scan.\n\n## Platform-Specific Variables in Skills\n\nThis plugin is authored once and converted for multiple agent platforms (Claude Code, Codex, Gemini CLI, etc.). Do not use platform-specific environment variables or string substitutions (e.g., `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_SKILL_DIR}`, `${CLAUDE_SESSION_ID}`, `CODEX_SANDBOX`, `CODEX_SESSION_ID`) in skill content without a graceful fallback that works when the variable is unavailable or unresolved.\n\nHow a bundled-file reference resolves depends on *who* resolves it and whether a shell is involved, so references fall into three tiers. Do not assume a bare `scripts/…` path behaves the same in all three.\n\n**Tier 1 — Read-time file references (relative, no anchor):** When skill *content* points the agent at a co-located file to read (e.g., \"read `references/schema.yaml`\"), use a relative path from the skill root. The skill loader resolves these against the skill's own directory on all major platforms — no variable prefix needed. This is the rule in *File References in Skills* above.\n\n**Tier 2 — Prose pointers to a bundled file the agent acts on (relative + a \"from this skill's directory\" cue):** When skill prose names a bundled file the agent will use but does *not* put it in an executed shell command (e.g., \"drive the loop with `scripts/hitl-loop.template.sh`\" or \"generate the package with `scripts/review-package BASE HEAD`\"), use a relative path plus an explicit \"from this skill's directory\" phrase. The cue tells the agent what to resolve against without the verbosity of an anchor.\n\n**Tier 3 — Executed shell commands (the `SKILL_DIR` anchor):** When skill content puts a bundled script in a command the agent runs through the Bash tool — a fenced ` ```bash ` block **or** an inline `bash …` / `python …` — anchor it to the skill dir. The Bash tool's working directory is the user's **project**, not the skill directory, on Claude Code, Codex, and Cursor alike, so a bare `bash scripts/my-script.sh` resolves to `<project>/scripts/…`. Relative paths here *often still work* — a capable agent resolves them against the skill dir it loaded (which is how the agentskills.io spec and other ecosystems ship them) — but that relies on the agent translating the path, and the failure mode is a fenced block copied **verbatim** into a Bash call, which runs literally and misses (`exit 127`; recovery is a wasted round-trip that weaker models / mid-tier subagents botch). Anchoring bakes the resolution into the command, so it is **deterministic**. Use the anchor for executed shell as the house default — a conservative choice, not a claim that bare relative *cannot* work (recurring bug class: #764 `ce-worktree`, #811 `ce-code-review`, #898 `ce-compound`):\n\n```\n# set inline in the SAME command (shell state does not persist between Bash calls):\nSKILL_DIR=\"<absolute path of the directory containing the SKILL.md you just read>\";\nbash \"$SKILL_DIR/scripts/my-script.sh\" ARG\n```\n\n**Keep the trailing `;` on the assignment line.** Some hosts (observed on Codex) flatten a fenced multi-line block into a single line by replacing the newline with a space before executing it. Without the `;`, `SKILL_DIR=\"…\"` + newline + `bash \"$SKILL_DIR/…\"` collapses to the env-var-prefix form `SKILL_DIR=\"…\" bash \"$SKILL_DIR/…\"`, where the shell expands `$SKILL_DIR` *before* the prefix assignment takes effect — so it expands to empty and the script path becomes `/scripts/my-script.sh` (`No such file or directory`). The trailing `;` makes the assignment a complete statement that survives flattening; it is load-bearing, not a style choice, so do not remove it.\n\nAn existence guard (`if [ -f \"$SKILL_DIR/scripts/my-script.sh\" ]; then … else echo \"not found — re-check the SKILL.md path\"; fi`) is optional — useful when there's a real fallback, but see the permission caveat below before guarding a pinned call.\n\n`SKILL_DIR` is a **model-filled** value, not a harness variable: every harness loads SKILL.md from a real absolute path the agent knows, so the skill instructs the agent to set `SKILL_DIR` to that directory. This works identically on Claude Code, Codex, and Cursor precisely because it depends on no host-specific variable — `SKILL_DIR`, `CLAUDE_SKILL_DIR`, `CODEX_SKILL_DIR`, `AGENT_SKILL_DIR` are **not** env vars on any of them, yet the script runs because the agent supplies the path. This is the production pattern used by widely-installed cross-host skills (e.g. `last30days`). Two constraints: (1) shell state does **not** persist between separate Bash-tool calls, so `SKILL_DIR` cannot be set once and reused — each invocation must carry the absolute path (set it inline in the same command). (2) A script that needs its *own* directory (to read a sibling file) derives it from `BASH_SOURCE`, not `SKILL_DIR`, since `SKILL_DIR` is the orchestrator's shell var and is not exported to the child process — see `skills/ce-code-review/scripts/cross-model-adversarial-review.sh` for the reference implementation. `last30days` adopted this anchor for its critical multi-host engine after a path-resolution regression; it is the right tool when a script must run *reliably*, which is why it is the tier-3 default — but tiers 1 and 2 deliberately stay lighter.\n\n**Avoid `${CLAUDE_SKILL_DIR}` here — in this cross-agent plugin it is a footgun, not a neutral alternative.** Every skill in this repo is authored once and installed across Claude Code, Codex, Cursor, and Gemini, and `${CLAUDE_SKILL_DIR}` is a Claude-Code-only SKILL.md *content* substitution (not an env var) that is **empty on every other host**. So a `${CLAUDE_SKILL_DIR}`-guarded call's `then` branch quietly never fires off-Claude — the **genuine silent skip** — and a Claude-only mechanism breaks on Codex/Cursor because the converter doesn't rewrite these paths and the native Codex install loads raw `SKILL.md` (no `ce_platforms` filtering). The model-filled `SKILL_DIR` anchor works on every host, so it is the right replacement wherever a `${CLAUDE_SKILL_DIR}`-guarded executed-shell call exists today (tier 3). Do not reach for `${CLAUDE_SKILL_DIR}` as a \"portable\" option — it isn't. Reach for it only for behavior that is genuinely Claude-Code-only and will *never* run on another harness — which, given this plugin's cross-host install model, is essentially never; treat any new use as a smell to justify or remove.\n\nSo: a skill's *core* behavior **can** live in a bundled script across hosts — invoke it via the `SKILL_DIR`-from-read-path anchor. You no longer need to avoid bundled scripts for portability; anchor them instead. Read-time references (`references/*.md`) still resolve against the skill dir on all targets and need no anchor.\n\n**Permission caveat (Claude Code).** Claude Code's permission checker evaluates every subcommand of a compound command, and a bare `[ -f … ]` test is not pre-approved — so wrapping a pinned `bash \"…sh\"` call in an `if … then … fi` guard defeats a narrow `Bash(bash *…sh)` allow-rule and prompts on every run. If a bundled-script call must stay auto-approved via such a pin, keep it a single pinned command rather than guarding it inline. Note the model-filled `SKILL_DIR` anchor produces a *dynamic* absolute path that won't match a static `Bash(bash /…/scripts/x.sh)` pin regardless of guarding — so for the anchor, expect a one-time approval prompt per distinct command (or use a broader allow-rule); the static-pin trick mainly applies to the fixed `${CLAUDE_SKILL_DIR}` form.\n\n**Do not use `!` load-time pre-resolution in skills.** The `!`cmd`` SKILL.md syntax runs `cmd` at skill load and inlines its stdout, but it is banned here (enforced by `tests/skill-shell-safety.test.ts`) for two unfixable reasons: it runs **only on Claude Code** — on Codex, Cursor, Gemini, and Grok the line is inert literal text — and on Claude Code a command that exits **non-zero aborts skill load** with a user-facing error. Every real use was git context (`git rev-parse …`, `gh pr view …`) whose non-zero exit is a *normal* state (no PR yet, no `origin/HEAD`, detached HEAD, not a repo), so the ordinary case broke the skill. The POSIX guards that force exit 0 (`2>/dev/null || echo SENTINEL`) then fail to parse under Windows PowerShell 5.1, which broke skill load there instead (issue #1066). No single command string both exits 0 on the expected-failure states and parses under both POSIX sh and PowerShell, so the construct cannot be made safe.\n\n**Gather context at runtime instead.** Have the agent run one argv-style command per shell tool call (`git …`, `gh …`) — no `;`, `&&`, `||`, pipes, `$(…)`, or redirects — and interpret each exit status as control flow. This parses identically under POSIX sh and PowerShell because it is a single external-program invocation, and a non-zero exit becomes data the agent reads rather than a load-time abort. See `ce-commit` / `ce-commit-push-pr` for the pattern.\n\n**When a platform variable is unavoidable:** resolve it at runtime with a single shell tool call and include explicit fallback instructions, so the agent knows what to do if the value is empty, a literal command string, or an error — e.g. run `jq -r .version \"${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json\"`; if it resolved to a semantic version use it, otherwise fall back to the versionless behavior. This applies equally to any platform's variables — a skill converted from Codex, Gemini, or any other platform will have the same problem if it assumes platform-only variables exist without a fallback.\n\n## Repository Docs Convention\n\n- **Plans** live in `docs/plans/` — unified plan artifacts. New `ce-brainstorm` outputs are requirements-only unified plans (`artifact_readiness: requirements-only`); `ce-plan` enriches them to implementation-ready plans (`artifact_readiness: implementation-ready`). Historical `docs/brainstorms/*-requirements.*` files remain readable legacy inputs and should not be migrated just because a new plan is created.\n- **Brainstorm evidence / legacy requirements** may live in `docs/brainstorms/` — historical requirements docs and specialized analysis artifacts such as `docs/brainstorms/riffrec-feedback/`. Do not treat this as the canonical output path for new `ce-brainstorm` artifacts.\n- **Solutions** live in `docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas.\n- **Specs** live in `docs/specs/` — target platform format specifications.\n\n### Solution categories (`docs/solutions/`)\n\nThis repo builds a plugin *for* developers. Categorize solutions from the perspective of the end user (a developer using the plugin), not a contributor to this repo.\n\n- **`developer-experience/`** — Issues with contributing to *this repo*: local dev setup, shell aliases, test ergonomics, CI friction. If the fix only matters to someone with a checkout of this repo, it belongs here.\n- **`integrations/`** — Issues where plugin output doesn't work correctly on a target platform or OS. Cross-platform bugs, target writer output problems, and converter compatibility issues go here.\n- **`workflow/`**, **`skill-design/`** — Plugin skill and agent design patterns, workflow improvements.\n\nWhen in doubt: if the bug affects someone running `bun install compound-engineering` or `bun convert`, it's an integration or product issue, not developer-experience.\n","category":"root","tokens":14155},{"name":"GEMINI.md","path":"GEMINI.md","title":"GEMINI.md","content":"# Compound Engineering\n\nThis Gemini extension provides the Compound Engineering skill set for planning,\nreview, implementation, debugging, and release workflows.\n\nUse the installed skills when they match the user's request. Treat this file as\nruntime context for extension users, not contributor guidance for this source\nrepository. Do not apply this repository's `AGENTS.md` maintainer workflow to\nthe user's project.\n","category":"root","tokens":105}]}