This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow-steward.git
The following commit(s) were added to refs/heads/main by this push:
new 544e8f9 feat(setup): self-adopt framework skills into .agents/skills/
(#458)
544e8f9 is described below
commit 544e8f9f97caa40d6314f0ee04bb47492799dc3a
Author: Yeonguk Choo <[email protected]>
AuthorDate: Sat Jun 6 01:27:05 2026 +0900
feat(setup): self-adopt framework skills into .agents/skills/ (#458)
* feat(setup): make adopt vendor-neutral across agents
* feat(setup): self-adopt framework skills into .agents/skills/
---
.agents/skills/magpie-audit-finding-fix | 1 +
.agents/skills/magpie-ci-runner-audit | 1 +
.agents/skills/magpie-committer-onboarding | 1 +
.agents/skills/magpie-contributor-activity-sweep | 1 +
.agents/skills/magpie-contributor-nomination | 1 +
.agents/skills/magpie-good-first-issue-author | 1 +
.agents/skills/magpie-issue-fix-workflow | 1 +
.agents/skills/magpie-issue-reassess | 1 +
.agents/skills/magpie-issue-reassess-stats | 1 +
.agents/skills/magpie-issue-reproducer | 1 +
.agents/skills/magpie-issue-triage | 1 +
.agents/skills/magpie-list-skills | 1 +
.agents/skills/magpie-optimize-skill | 1 +
.agents/skills/magpie-pairing-multi-agent-review | 1 +
.agents/skills/magpie-pairing-self-review | 1 +
.agents/skills/magpie-pr-management-code-review | 1 +
.agents/skills/magpie-pr-management-mentor | 1 +
.agents/skills/magpie-pr-management-quick-merge | 1 +
.agents/skills/magpie-pr-management-stats | 1 +
.agents/skills/magpie-pr-management-triage | 1 +
.agents/skills/magpie-security-cve-allocate | 1 +
.agents/skills/magpie-security-issue-deduplicate | 1 +
.agents/skills/magpie-security-issue-fix | 1 +
.agents/skills/magpie-security-issue-import | 1 +
.../skills/magpie-security-issue-import-from-md | 1 +
.../skills/magpie-security-issue-import-from-pr | 1 +
.../magpie-security-issue-import-via-forwarder | 1 +
.agents/skills/magpie-security-issue-invalidate | 1 +
.agents/skills/magpie-security-issue-sync | 1 +
.agents/skills/magpie-security-issue-triage | 1 +
.../skills/magpie-security-tracker-stats-dashboard | 1 +
.agents/skills/magpie-setup | 1 +
.agents/skills/magpie-setup-isolated-setup-doctor | 1 +
.agents/skills/magpie-setup-isolated-setup-install | 1 +
.agents/skills/magpie-setup-isolated-setup-update | 1 +
.agents/skills/magpie-setup-isolated-setup-verify | 1 +
.agents/skills/magpie-setup-override-upstream | 1 +
.agents/skills/magpie-setup-shared-config-sync | 1 +
.agents/skills/magpie-write-skill | 1 +
.gitignore | 9 +-
skills/setup/SKILL.md | 12 +-
skills/setup/adopt.md | 84 +++++++---
skills/setup/agents.md | 179 +++++++++++++++++++++
skills/setup/conventions.md | 10 ++
skills/setup/unadopt.md | 63 +++++---
skills/setup/upgrade.md | 143 ++++++++++------
skills/setup/verify.md | 94 ++++++-----
skills/setup/worktree-init.md | 64 +++++---
48 files changed, 537 insertions(+), 160 deletions(-)
diff --git a/.agents/skills/magpie-audit-finding-fix
b/.agents/skills/magpie-audit-finding-fix
new file mode 120000
index 0000000..c32be1b
--- /dev/null
+++ b/.agents/skills/magpie-audit-finding-fix
@@ -0,0 +1 @@
+../../skills/audit-finding-fix
\ No newline at end of file
diff --git a/.agents/skills/magpie-ci-runner-audit
b/.agents/skills/magpie-ci-runner-audit
new file mode 120000
index 0000000..d964738
--- /dev/null
+++ b/.agents/skills/magpie-ci-runner-audit
@@ -0,0 +1 @@
+../../skills/ci-runner-audit
\ No newline at end of file
diff --git a/.agents/skills/magpie-committer-onboarding
b/.agents/skills/magpie-committer-onboarding
new file mode 120000
index 0000000..9f421b7
--- /dev/null
+++ b/.agents/skills/magpie-committer-onboarding
@@ -0,0 +1 @@
+../../skills/committer-onboarding
\ No newline at end of file
diff --git a/.agents/skills/magpie-contributor-activity-sweep
b/.agents/skills/magpie-contributor-activity-sweep
new file mode 120000
index 0000000..4c4ad61
--- /dev/null
+++ b/.agents/skills/magpie-contributor-activity-sweep
@@ -0,0 +1 @@
+../../skills/contributor-activity-sweep
\ No newline at end of file
diff --git a/.agents/skills/magpie-contributor-nomination
b/.agents/skills/magpie-contributor-nomination
new file mode 120000
index 0000000..84b98fd
--- /dev/null
+++ b/.agents/skills/magpie-contributor-nomination
@@ -0,0 +1 @@
+../../skills/contributor-nomination
\ No newline at end of file
diff --git a/.agents/skills/magpie-good-first-issue-author
b/.agents/skills/magpie-good-first-issue-author
new file mode 120000
index 0000000..360135b
--- /dev/null
+++ b/.agents/skills/magpie-good-first-issue-author
@@ -0,0 +1 @@
+../../skills/good-first-issue-author
\ No newline at end of file
diff --git a/.agents/skills/magpie-issue-fix-workflow
b/.agents/skills/magpie-issue-fix-workflow
new file mode 120000
index 0000000..7bb0e8d
--- /dev/null
+++ b/.agents/skills/magpie-issue-fix-workflow
@@ -0,0 +1 @@
+../../skills/issue-fix-workflow
\ No newline at end of file
diff --git a/.agents/skills/magpie-issue-reassess
b/.agents/skills/magpie-issue-reassess
new file mode 120000
index 0000000..8e91821
--- /dev/null
+++ b/.agents/skills/magpie-issue-reassess
@@ -0,0 +1 @@
+../../skills/issue-reassess
\ No newline at end of file
diff --git a/.agents/skills/magpie-issue-reassess-stats
b/.agents/skills/magpie-issue-reassess-stats
new file mode 120000
index 0000000..a5b2632
--- /dev/null
+++ b/.agents/skills/magpie-issue-reassess-stats
@@ -0,0 +1 @@
+../../skills/issue-reassess-stats
\ No newline at end of file
diff --git a/.agents/skills/magpie-issue-reproducer
b/.agents/skills/magpie-issue-reproducer
new file mode 120000
index 0000000..c8cdab9
--- /dev/null
+++ b/.agents/skills/magpie-issue-reproducer
@@ -0,0 +1 @@
+../../skills/issue-reproducer
\ No newline at end of file
diff --git a/.agents/skills/magpie-issue-triage
b/.agents/skills/magpie-issue-triage
new file mode 120000
index 0000000..a32c627
--- /dev/null
+++ b/.agents/skills/magpie-issue-triage
@@ -0,0 +1 @@
+../../skills/issue-triage
\ No newline at end of file
diff --git a/.agents/skills/magpie-list-skills
b/.agents/skills/magpie-list-skills
new file mode 120000
index 0000000..a6c207d
--- /dev/null
+++ b/.agents/skills/magpie-list-skills
@@ -0,0 +1 @@
+../../skills/list-skills
\ No newline at end of file
diff --git a/.agents/skills/magpie-optimize-skill
b/.agents/skills/magpie-optimize-skill
new file mode 120000
index 0000000..8cddd77
--- /dev/null
+++ b/.agents/skills/magpie-optimize-skill
@@ -0,0 +1 @@
+../../skills/optimize-skill
\ No newline at end of file
diff --git a/.agents/skills/magpie-pairing-multi-agent-review
b/.agents/skills/magpie-pairing-multi-agent-review
new file mode 120000
index 0000000..ddb431c
--- /dev/null
+++ b/.agents/skills/magpie-pairing-multi-agent-review
@@ -0,0 +1 @@
+../../skills/pairing-multi-agent-review
\ No newline at end of file
diff --git a/.agents/skills/magpie-pairing-self-review
b/.agents/skills/magpie-pairing-self-review
new file mode 120000
index 0000000..b2fccf7
--- /dev/null
+++ b/.agents/skills/magpie-pairing-self-review
@@ -0,0 +1 @@
+../../skills/pairing-self-review
\ No newline at end of file
diff --git a/.agents/skills/magpie-pr-management-code-review
b/.agents/skills/magpie-pr-management-code-review
new file mode 120000
index 0000000..4b543a0
--- /dev/null
+++ b/.agents/skills/magpie-pr-management-code-review
@@ -0,0 +1 @@
+../../skills/pr-management-code-review
\ No newline at end of file
diff --git a/.agents/skills/magpie-pr-management-mentor
b/.agents/skills/magpie-pr-management-mentor
new file mode 120000
index 0000000..02c3493
--- /dev/null
+++ b/.agents/skills/magpie-pr-management-mentor
@@ -0,0 +1 @@
+../../skills/pr-management-mentor
\ No newline at end of file
diff --git a/.agents/skills/magpie-pr-management-quick-merge
b/.agents/skills/magpie-pr-management-quick-merge
new file mode 120000
index 0000000..a504c5a
--- /dev/null
+++ b/.agents/skills/magpie-pr-management-quick-merge
@@ -0,0 +1 @@
+../../skills/pr-management-quick-merge
\ No newline at end of file
diff --git a/.agents/skills/magpie-pr-management-stats
b/.agents/skills/magpie-pr-management-stats
new file mode 120000
index 0000000..b1a1338
--- /dev/null
+++ b/.agents/skills/magpie-pr-management-stats
@@ -0,0 +1 @@
+../../skills/pr-management-stats
\ No newline at end of file
diff --git a/.agents/skills/magpie-pr-management-triage
b/.agents/skills/magpie-pr-management-triage
new file mode 120000
index 0000000..4ae07e2
--- /dev/null
+++ b/.agents/skills/magpie-pr-management-triage
@@ -0,0 +1 @@
+../../skills/pr-management-triage
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-cve-allocate
b/.agents/skills/magpie-security-cve-allocate
new file mode 120000
index 0000000..6cc475b
--- /dev/null
+++ b/.agents/skills/magpie-security-cve-allocate
@@ -0,0 +1 @@
+../../skills/security-cve-allocate
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-deduplicate
b/.agents/skills/magpie-security-issue-deduplicate
new file mode 120000
index 0000000..63f6e88
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-deduplicate
@@ -0,0 +1 @@
+../../skills/security-issue-deduplicate
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-fix
b/.agents/skills/magpie-security-issue-fix
new file mode 120000
index 0000000..40c0839
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-fix
@@ -0,0 +1 @@
+../../skills/security-issue-fix
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-import
b/.agents/skills/magpie-security-issue-import
new file mode 120000
index 0000000..d80190b
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-import
@@ -0,0 +1 @@
+../../skills/security-issue-import
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-import-from-md
b/.agents/skills/magpie-security-issue-import-from-md
new file mode 120000
index 0000000..3c96cda
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-import-from-md
@@ -0,0 +1 @@
+../../skills/security-issue-import-from-md
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-import-from-pr
b/.agents/skills/magpie-security-issue-import-from-pr
new file mode 120000
index 0000000..41c4085
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-import-from-pr
@@ -0,0 +1 @@
+../../skills/security-issue-import-from-pr
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-import-via-forwarder
b/.agents/skills/magpie-security-issue-import-via-forwarder
new file mode 120000
index 0000000..213f546
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-import-via-forwarder
@@ -0,0 +1 @@
+../../skills/security-issue-import-via-forwarder
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-invalidate
b/.agents/skills/magpie-security-issue-invalidate
new file mode 120000
index 0000000..7b07030
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-invalidate
@@ -0,0 +1 @@
+../../skills/security-issue-invalidate
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-sync
b/.agents/skills/magpie-security-issue-sync
new file mode 120000
index 0000000..b4f409e
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-sync
@@ -0,0 +1 @@
+../../skills/security-issue-sync
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-issue-triage
b/.agents/skills/magpie-security-issue-triage
new file mode 120000
index 0000000..162016a
--- /dev/null
+++ b/.agents/skills/magpie-security-issue-triage
@@ -0,0 +1 @@
+../../skills/security-issue-triage
\ No newline at end of file
diff --git a/.agents/skills/magpie-security-tracker-stats-dashboard
b/.agents/skills/magpie-security-tracker-stats-dashboard
new file mode 120000
index 0000000..ea7df87
--- /dev/null
+++ b/.agents/skills/magpie-security-tracker-stats-dashboard
@@ -0,0 +1 @@
+../../skills/security-tracker-stats-dashboard
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup b/.agents/skills/magpie-setup
new file mode 120000
index 0000000..8f69d9c
--- /dev/null
+++ b/.agents/skills/magpie-setup
@@ -0,0 +1 @@
+../../skills/setup
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-isolated-setup-doctor
b/.agents/skills/magpie-setup-isolated-setup-doctor
new file mode 120000
index 0000000..44f2819
--- /dev/null
+++ b/.agents/skills/magpie-setup-isolated-setup-doctor
@@ -0,0 +1 @@
+../../skills/setup-isolated-setup-doctor
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-isolated-setup-install
b/.agents/skills/magpie-setup-isolated-setup-install
new file mode 120000
index 0000000..794d890
--- /dev/null
+++ b/.agents/skills/magpie-setup-isolated-setup-install
@@ -0,0 +1 @@
+../../skills/setup-isolated-setup-install
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-isolated-setup-update
b/.agents/skills/magpie-setup-isolated-setup-update
new file mode 120000
index 0000000..fabb0d7
--- /dev/null
+++ b/.agents/skills/magpie-setup-isolated-setup-update
@@ -0,0 +1 @@
+../../skills/setup-isolated-setup-update
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-isolated-setup-verify
b/.agents/skills/magpie-setup-isolated-setup-verify
new file mode 120000
index 0000000..d76e89a
--- /dev/null
+++ b/.agents/skills/magpie-setup-isolated-setup-verify
@@ -0,0 +1 @@
+../../skills/setup-isolated-setup-verify
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-override-upstream
b/.agents/skills/magpie-setup-override-upstream
new file mode 120000
index 0000000..a9cee55
--- /dev/null
+++ b/.agents/skills/magpie-setup-override-upstream
@@ -0,0 +1 @@
+../../skills/setup-override-upstream
\ No newline at end of file
diff --git a/.agents/skills/magpie-setup-shared-config-sync
b/.agents/skills/magpie-setup-shared-config-sync
new file mode 120000
index 0000000..f850cf0
--- /dev/null
+++ b/.agents/skills/magpie-setup-shared-config-sync
@@ -0,0 +1 @@
+../../skills/setup-shared-config-sync
\ No newline at end of file
diff --git a/.agents/skills/magpie-write-skill
b/.agents/skills/magpie-write-skill
new file mode 120000
index 0000000..f512e9d
--- /dev/null
+++ b/.agents/skills/magpie-write-skill
@@ -0,0 +1 @@
+../../skills/write-skill
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1f5f751..2e03f05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,11 +8,14 @@ logs
# Skill source lives in skills/. The framework self-adopts by
# committing magpie-<skill> symlinks that point into ../../skills/<skill>/
# (see skills/setup/adopt.md → "Local self-adoption"). They live under
-# BOTH .claude/skills/ (Claude Code) and .github/skills/ (GitHub's skill
-# loader). Those symlinks ARE committed — their targets are in-repo and
-# always resolve on a fresh clone, so every contributor gets the skills
+# the universal .agents/skills/ path (Codex, Cursor, Gemini CLI, Copilot,
+# …) plus BOTH .claude/skills/ (Claude Code) and .github/skills/ (GitHub's
+# skill loader). Those symlinks ARE committed — their targets are in-repo
+# and always resolve on a fresh clone, so every contributor gets the skills
# active with no setup step. Everything else dropped into these dirs
# stays local.
+.agents/skills/*
+!/.agents/skills/magpie-*
.claude/skills/*
!/.claude/skills/magpie-*
.github/skills/*
diff --git a/skills/setup/SKILL.md b/skills/setup/SKILL.md
index d671d9c..10c1953 100644
--- a/skills/setup/SKILL.md
+++ b/skills/setup/SKILL.md
@@ -100,9 +100,11 @@ Apache Magpie framework checkout itself — a remote
snapshot of the
framework into itself would be circular. Instead it **self-adopts**
with `method:local`: each `magpie-<skill>` is a **committed**
symlink into the in-repo `../../skills/<skill>/` source — written
-under both `.claude/skills/` (Claude Code) and `.github/skills/`
-(GitHub's skill loader) — with no snapshot, no remote fetch, and
-no copy. This makes
+under **every active agent target** ([`agents.md`](agents.md)):
+`.agents/skills/` (the universal path shared by Codex, Cursor,
+Gemini CLI, Copilot, …), `.claude/skills/` (Claude Code), and
+`.github/skills/` (GitHub's skill loader) — with no snapshot, no
+remote fetch, and no copy. This makes
the framework's own skills callable while developing the framework,
and every contributor gets them active on a fresh clone with no
setup step. `adopt` detects the framework checkout structurally and
@@ -170,7 +172,8 @@ proposed `/magpie-setup upgrade`.
| [`adopt.md`](adopt.md) | First-time adoption walk-through — recognise
existing-snapshot vs needs-bootstrap, write the two lock files, ask the user
which skill families to wire up, create the gitignored symlinks, scaffold
`.apache-magpie-overrides/`, install the post-checkout hook, update project
docs. The default sub-action. |
| [`upgrade.md`](upgrade.md) | Refresh the gitignored snapshot per the
committed lock, reconcile any agentic overrides + symlinks against the new
framework structure, surface conflicts. Drives the on-drift remediation flow. |
| [`verify.md`](verify.md) | Read-only health check — snapshot present +
intact, both lock files in sync, symlinks point at live targets, `.gitignore`
correct, `.apache-magpie-overrides/` exists, drift status (committed vs local),
the `setup` skill itself is current. |
-| [`conventions.md`](conventions.md) | Adopter skills-dir convention
auto-detection — four patterns: A (flat `.claude/skills/<n>/`), B (per-skill
`.claude/skills/<n>` → `.github/skills/<n>/` double-symlink), C (none yet), D
(single directory symlink where one of `.claude/skills` / `.github/skills` is
itself a symlink to the other; two orientations). |
+| [`agents.md`](agents.md) | The agent-target registry — *which* directories
framework-skill symlinks land in across vendors. Defines the universal
`.agents/skills/` path (shared by Codex, Cursor, Gemini CLI, Copilot, OpenCode,
…), the `claude-code` + `github` pair, holdout natives (Windsurf, Goose),
active-target selection, SKILL.md format portability, and the Claude-Code-only
layer (sandbox/hooks). The source of truth every sub-action consults for the
target set. |
+| [`conventions.md`](conventions.md) | Adopter skills-dir convention
auto-detection for the **`.claude/` ↔ `.github/` pair** — four patterns: A
(flat `.claude/skills/<n>/`), B (per-skill `.claude/skills/<n>` →
`.github/skills/<n>/` double-symlink), C (none yet), D (single directory
symlink where one of `.claude/skills` / `.github/skills` is itself a symlink to
the other; two orientations). Describes the *layout of that one coupled pair*;
[`agents.md`](agents.md) is the wider which-target [...]
| [`overrides.md`](overrides.md) | Agentic-override file management — open /
scaffold an override for a framework skill, list existing overrides, help
reconcile when the framework changes the underlying skill's structure on
upgrade. |
| [`unadopt.md`](unadopt.md) | Reverse the adoption — remove snapshot, locks,
symlinks, post-checkout hook, `.gitignore` entries, the adoption sections in
`README.md` / `AGENTS.md` / `CONTRIBUTING.md`, and the committed `setup` skill
itself. Preserves `.apache-magpie-overrides/` by default; `--purge-overrides`
removes it too. Surfaces the full removal plan before any write. |
@@ -358,6 +361,7 @@ first, then continue.
|---|---|
| `from:<git-ref>` / `from:<version>` | Adopt or upgrade from a specific
framework ref or version. Used during `adopt` (overrides the user prompt) and
`upgrade` (overrides the committed lock for *this run only* — does NOT update
the committed lock). |
| `method:<git-branch\|git-tag\|svn-zip\|local>` | Pick the install method
explicitly. Default during `adopt`: prompt the user. **`local`** is
**framework-checkout only** — it self-adopts by linking the in-repo `skills/`
source directly instead of fetching a snapshot (see [`adopt.md` → Local
self-adoption](adopt.md#local-self-adoption-methodlocal)). |
+| `agents:<list>` | Comma-separated **agent targets** to wire symlinks into
([`agents.md`](agents.md) registry ids: `universal`, `claude-code`, `github`,
`windsurf`, `goose`, …). Default on `adopt`/`upgrade`: auto — the always-on
neutral set (`universal` + `claude-code` + `github`) plus any other registry
dir already present in the repo. When passed, **replaces** the auto-detected
set for that run, except `universal` (`.agents/skills/`) which is always
retained because dropping it defeat [...]
| `skill-families:<list>` | Comma-separated **opt-in** families to symlink
(`security`, `pr-management`, `issue`). Default on `adopt`: prompt. Default on
`upgrade`: read the families list from `<committed-lock>` / `<local-lock>`,
**auto-include any opt-in family the framework has introduced since the lock
was written** (recorded back into the lock), and **ensure every framework skill
in the effective family set has a valid symlink** — create or repair missing /
broken symlinks, not just [...]
| `--purge-overrides` | *(unadopt only)* Also `git rm -r`
`.apache-magpie-overrides/`. Default: preserve. |
| `dry-run` | Show what the skill would do without writing anything. |
diff --git a/skills/setup/adopt.md b/skills/setup/adopt.md
index f515201..5caaeba 100644
--- a/skills/setup/adopt.md
+++ b/skills/setup/adopt.md
@@ -149,12 +149,14 @@ How it differs from a remote adoption:
- **Symlinks are committed, not gitignored.** Each
`magpie-<skill>` symlink targets `../../skills/<skill>/` — an
in-repo path that always resolves on a fresh clone — so the
- links are committed. They are written under **both**
- `.claude/skills/` (Claude Code) and `.github/skills/` (GitHub's
- skill loader), so the framework's own skills are discoverable
- by either harness; `.gitignore` un-ignores `magpie-*` in both
- dirs. Every contributor gets the skills active with no setup
- step.
+ links are committed. They are written under **every active
+ target dir** ([`agents.md`](agents.md)) — `.agents/skills/`
+ (the universal path shared by Codex, Cursor, Gemini CLI,
+ Copilot, …), `.claude/skills/` (Claude Code), and
+ `.github/skills/` (GitHub's skill loader) — so the framework's
+ own skills are discoverable by any harness; `.gitignore`
+ un-ignores `magpie-*` in each. Every contributor gets the
+ skills active with no setup step, whatever agent they use.
- **All skills, no family prompt.** Self-adoption links *every*
skill under `skills/`, so the opt-in family prompt of
[Step 5](#step-5--pick-the-skill-families) is skipped.
@@ -192,25 +194,37 @@ How it differs from a remote adoption:
```
5. **`.gitignore`.** Ensure each skills dir glob is ignored with
- the `magpie-*` set un-ignored, in **both** locations
- (idempotent — add any missing line):
+ the `magpie-*` set un-ignored, in **every active target
+ location** ([`agents.md`](agents.md) — the always-on neutral
+ targets `.agents/skills/`, `.claude/skills/`, `.github/skills/`
+ plus any other registry dir already present). Idempotent — add
+ any missing line. For the default set:
```text
+ .agents/skills/*
+ !/.agents/skills/magpie-*
.claude/skills/*
!/.claude/skills/magpie-*
.github/skills/*
!/.github/skills/magpie-*
```
+ (Self-adoption symlinks are *committed*, not gitignored — see
+ the next step — so the `!…/magpie-*` negation here un-ignores
+ the whole set, not just `magpie-setup`.)
+
6. **Create the symlinks.** For each enumerated skill `<n>`,
create the relative symlink `magpie-<n>` → `../../skills/<n>`
- under **both** `<repo-root>/.claude/skills/` and
- `<repo-root>/.github/skills/`. Idempotent: re-point a
- pre-existing `magpie-<n>` symlink only if it targets something
- else; never overwrite a non-symlink (surface the conflict and
- stop). Show the full list and confirm before writing.
+ under **every active target dir** from
+ [`agents.md`](agents.md) (`.agents/skills/`, `.claude/skills/`,
+ `.github/skills/`, plus any present holdout). Idempotent:
+ re-point a pre-existing `magpie-<n>` symlink only if it targets
+ something else; never overwrite a non-symlink (surface the
+ conflict and stop). Show the full list and confirm before
+ writing.
7. **Verify + stage.** Confirm every `magpie-<n>` symlink (in
- both dirs) resolves to a directory containing `SKILL.md`, then
+ every target dir) resolves to a directory containing
+ `SKILL.md`, then
suggest the user `git add` the symlinks, `.apache-magpie.lock`,
and `.gitignore`.
@@ -508,13 +522,26 @@ idempotent — re-add them if they're missing.
/.claude/settings.local.json
```
-**Symlink-pattern entries — vary by adopter
-[skills-dir convention](conventions.md)**:
+**Symlink-pattern entries — one block per active target
+([`agents.md`](agents.md)).** Every framework skill is symlinked
+under the `magpie-` prefix (see
+[`SKILL.md` Golden rule 6](SKILL.md#golden-rules)), so a single
+`magpie-*` glob covers them all in each target dir — no per-family
+lines.
+
+**Universal target — always emitted** (the `.agents/skills/`
+path shared by Codex, Cursor, Gemini CLI, Copilot, OpenCode, …):
+
+ ```text
+ /.agents/skills/magpie-*
+ !/.agents/skills/magpie-setup
+ ```
+
+**Any present holdout** (`.windsurf/skills/`, `.goose/skills/`,
+…) gets the same flat two-line block keyed on its own dir.
- Every framework skill is symlinked under the `magpie-`
- prefix (see [`SKILL.md` Golden rule 6](SKILL.md#golden-rules)),
- so a single `magpie-*` glob covers them all — no per-family
- lines.
+**Claude Code + GitHub pair — varies by adopter
+[skills-dir convention](conventions.md):**
- **Pattern A (flat)** — only the `.claude/skills/...` lines:
@@ -601,12 +628,25 @@ adoption path where the committed lock only records the
opt-in pick. Compute the family glob fresh from the snapshot
contents on disk — do not hard-code skill names.
-Per-pattern symlink wiring (see
+Per-target symlink wiring (targets from
+[`agents.md`](agents.md); the Claude+GitHub layout from
[`conventions.md`](conventions.md)):
Every symlink is named `magpie-<n>` (the `magpie-` prefix
namespaces framework skills); its target keeps the snapshot's
-clean source name `skills/<n>/`.
+clean source name `skills/<n>/`. Wire the **same set of skills**
+into **every active target dir**.
+
+- **Universal target (`.agents/skills/`)** — one flat symlink
+ per skill at `.agents/skills/magpie-<n>` → snapshot. Gitignored.
+ This single placement is what makes the framework discoverable
+ to Codex, Cursor, Gemini CLI, Copilot, OpenCode, and the rest
+ of the shared-path cluster. Any present holdout
+ (`.windsurf/skills/`, `.goose/skills/`, …) is wired the same
+ flat way.
+
+- **Claude Code + GitHub pair** — wired per the detected
+ convention pattern below:
- **Pattern A (flat)** — one symlink per skill at
`.claude/skills/magpie-<n>` → snapshot. Gitignored.
diff --git a/skills/setup/agents.md b/skills/setup/agents.md
new file mode 100644
index 0000000..b2a6358
--- /dev/null
+++ b/skills/setup/agents.md
@@ -0,0 +1,179 @@
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/legal/release-policy.html -->
+
+# agents — the agent-target registry (where framework-skill symlinks land)
+
+Framework skills are **vendor-neutral content**: every supported
+agent reads the *same* `SKILL.md` (the open Agent Skills format —
+plain Markdown + a small YAML frontmatter). The skill body is
+byte-identical no matter which agent loads it; there is **no
+per-agent compile, adapter, or content transform**. The only thing
+that genuinely differs between agents is **where on disk each one
+looks for skills**. This file is the registry of those locations —
+the single source of truth `adopt`, `upgrade`, `verify`,
+`unadopt`, and `worktree-init` consult to decide *which directories*
+to wire, refresh, health-check, and tear down.
+
+It is the magpie analogue of a package manager's per-agent path
+table: keep all vendor-specific knowledge here as *"where files
+go"*, never as *"what files contain"*.
+
+## The registry
+
+| Target id | Project skills dir | Kind | Reads it |
+|---|---|---|---|
+| `universal` | `.agents/skills/` | universal | Codex, Cursor, Gemini CLI,
GitHub Copilot, OpenCode, Cline, Zed, Warp, Amp, and the rest of the cluster
that converged on the shared path |
+| `claude-code` | `.claude/skills/` | native | Claude Code (layout variants
A/B/C/D — see [`conventions.md`](conventions.md)) |
+| `github` | `.github/skills/` | native | GitHub's skill loader (paired with
`claude-code` under conventions Pattern B / D) |
+| `windsurf` | `.windsurf/skills/` | native | Windsurf |
+| `goose` | `.goose/skills/` | native | Goose |
+
+The table is **extensible**: a new agent that wants framework
+skills is one new row (`id`, project dir, kind), nothing else —
+the same way a path-registry-driven installer adds an agent. Do
+not invent per-agent *content*; if an agent needs a different
+directory, add a row, never a forked skill.
+
+## The universal directory — `.agents/skills/`
+
+The load-bearing move for neutrality is that a large cluster of
+agents (Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode,
+Cline, Zed, Warp, …) all read **`.agents/skills/`** as their
+project-scope skills path. Wiring `magpie-<skill>` once into
+`.agents/skills/` covers that whole cluster in a **single
+placement**:
+
+```text
+.agents/skills/magpie-pr-management-triage → one symlink
+ ├─ Codex picks it up
+ ├─ Cursor picks it up
+ ├─ Gemini CLI picks it up
+ └─ Copilot … picks it up
+```
+
+Only agents with a **bespoke** folder (`claude-code` →
+`.claude/skills/`, `github` → `.github/skills/`, `windsurf`,
+`goose`, …) need their own symlink in addition. This pushes the
+adopter repo toward de facto convergence on one neutral location
+while still bridging the holdouts.
+
+(Global / per-user skill paths diverge across agents — e.g.
+`~/.cursor/skills/`, `~/.codex/skills/`, `~/.gemini/skills/`. The
+framework's adoption is **project-scope** — it writes inside the
+adopter repo — so it only ever cares about the project columns
+above. Global installs are the operator's concern, out of scope
+for `setup`.)
+
+## Active-target selection — which dirs `adopt` wires
+
+On every `adopt` / `upgrade` / `worktree-init`, the **active
+target set** is computed as the union of:
+
+1. **The always-on neutral targets** — `universal`
+ (`.agents/skills/`) **plus** the `claude-code` + `github`
+ pair, wired per the adopter's detected
+ [skills-dir convention](conventions.md) (A/B/C/D). These three
+ are wired unconditionally; they are cheap relative symlinks
+ into the gitignored snapshot, harmless to an agent that never
+ reads them, and dropping them is not a supported configuration.
+2. **Any other registry target already present in the repo** —
+ if `.windsurf/skills/` or `.goose/skills/` (etc.) already
+ exists as a real directory, it is added to the active set so
+ that agent sees the framework skills too.
+3. **Explicit opt-in** via the `agents:<list>` flag (see
+ [`SKILL.md` Inputs](SKILL.md#inputs)) — a comma-separated list
+ of registry ids. When passed it **replaces** the auto-detected
+ set (1)+(2) for that run; `universal` is always retained even
+ if omitted, because dropping it defeats neutrality.
+
+The flow **never** removes or rewrites an adopter's own
+non-`magpie-` skill content in any target dir. It only adds /
+repairs `magpie-*` symlinks.
+
+## How the framework's rules generalise across targets
+
+Every existing adoption rule is "per active target", not
+"for `.claude` and `.github`":
+
+- **`magpie-` prefix** ([`SKILL.md` Golden rule 6](SKILL.md#golden-rules))
+ — unchanged. Every framework skill is `magpie-<skill>` in
+ *every* active target dir, so it never collides with an
+ adopter's own skills regardless of agent.
+- **`.gitignore`** — one block per active target dir:
+ `/<dir>/magpie-*` plus `!/<dir>/magpie-setup`. The negation
+ keeps the one committed bootstrap (`magpie-setup`) tracked; the
+ glob ignores the rest (their targets are the gitignored
+ snapshot, so they would dangle on a fresh clone). See
+ [`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only).
+- **Symlink wiring** — flat (one `magpie-<n>` → snapshot per
+ skill), exactly like conventions Pattern A, for `universal`,
+ `windsurf`, `goose`, and any other native dir. The
+ `claude-code` + `github` pair is the **one** special case: its
+ inter-directory relationship (double-symlink B, single-dir
+ symlink D, flat A) is described in [`conventions.md`](conventions.md)
+ and detected per repo. See
+ [`adopt.md` Step 8](adopt.md#step-8--wire-up-the-framework-skill-symlinks).
+- **Local self-adoption** (framework checkout) — committed
+ symlinks into `../../skills/<skill>/` in *every* active target
+ dir, not just two. See
+ [`adopt.md` → Local self-adoption](adopt.md#local-self-adoption-methodlocal).
+- **`unadopt` / `worktree-init`** — every active target dir is
+ torn down / propagated uniformly. Removing only `.claude` +
+ `.github` would orphan the `.agents/skills/magpie-*` links.
+
+## SKILL.md format portability
+
+The same `SKILL.md` is valid in every target with no
+per-agent edit:
+
+| Frontmatter field | Cross-agent behaviour |
+|---|---|
+| `name`, `description` | Universal — discovery works everywhere. |
+| `when_to_use` | Claude-family routing hint; other agents may ignore it →
discovery still works off `description`, only routing precision degrades. |
+| `argument-hint`, `capability` | magpie / Claude extensions; non-supporting
agents silently ignore them. |
+| `license` | Inert metadata. |
+
+Unknown frontmatter is ignored by each agent (graceful
+degradation), so there is **no compile step and no per-agent
+file**. The gitignored snapshot stays the single source of truth;
+every target dir is a symlink into it.
+
+## The Claude-Code-only layer (not wired for other targets)
+
+Some of what `adopt` installs is **genuinely Claude-Code-specific
+and is wired only when the `claude-code` target is active**:
+
+- `.claude/settings.json` — the sandbox (`network.allowedDomains`
+ allowlist, `filesystem.denyRead`), the MCP-tool permission
+ allowlist, and the hooks. Schema:
+ `claude-code-settings.json`.
+- `.claude/settings.local.json` — per-machine sandbox-allowlist
+ entries.
+- The `setup-isolated-setup-*` skill family — sandbox / pinned-
+ tools / hooks installer.
+
+Other agents adopt the **skills** (the neutral content) **without**
+this layer.
+
+> **Security caveat — this layer is a control, not cosmetics.**
+> For a security framework the sandbox is a *confidentiality
+> control* (it blocks exfiltration of non-public vulnerability
+> data and reading `~/`). Running a security-class skill on an
+> agent that lacks an equivalent control is a **policy decision**,
+> not graceful degradation. Adopting the skills onto a non-Claude
+> agent is supported; *executing confidential workflows there*
+> requires the project to either declare that agent unsupported
+> for those workflows or provide an equivalent control. `adopt`
+> itself only places files — it does not grant that approval.
+
+## Relationship to `conventions.md`
+
+[`conventions.md`](conventions.md) describes the **`.claude/` ↔
+`.github/` layout** (patterns A/B/C/D) — i.e. the internal shape
+of the `claude-code` + `github` rows of this registry, which are
+coupled because some adopters mirror one into the other. This file
+is the **wider** picture: the full set of agent targets, of which
+that pair is one coupled entry and `universal` (`.agents/skills/`)
+is another. When `adopt` wires symlinks it consults **both**: this
+file for *which targets*, `conventions.md` for *how the
+`.claude/`/`.github/` pair is laid out* in this particular repo.
diff --git a/skills/setup/conventions.md b/skills/setup/conventions.md
index 896e397..7173dd4 100644
--- a/skills/setup/conventions.md
+++ b/skills/setup/conventions.md
@@ -3,6 +3,16 @@
# conventions — auto-detect the adopter's skills-dir layout
+> **Scope.** This file covers the **`.claude/` ↔ `.github/` pair**
+> — the one coupled entry of the agent-target registry in
+> [`agents.md`](agents.md), where some adopters mirror one
+> directory into the other. The *wider* question of **which**
+> agent targets `adopt` wires (the universal `.agents/skills/`
+> path, plus holdout natives like Windsurf / Goose) lives in
+> [`agents.md`](agents.md). The universal and holdout targets are
+> always wired **flat** (like Pattern A below); only this
+> `.claude/`/`.github/` pair has the A/B/C/D layout variants.
+
Different ASF projects already organise their `.claude/skills/`
differently. Before `setup adopt` creates symlinks
into the snapshot, it detects which pattern is in place and
diff --git a/skills/setup/unadopt.md b/skills/setup/unadopt.md
index ba9ef92..fd2b454 100644
--- a/skills/setup/unadopt.md
+++ b/skills/setup/unadopt.md
@@ -6,10 +6,19 @@
The reverse of [`adopt.md`](adopt.md). Removes the framework
artefacts the adopt flow installed — gitignored snapshot,
committed lock, gitignored local lock, framework-skill
-symlinks, `.gitignore` entries, post-checkout hook, the
-adoption sections in `README.md` / `AGENTS.md` /
-`CONTRIBUTING.md`, and the committed `setup` skill
-itself.
+symlinks **in every active target dir** ([`agents.md`](agents.md)
+— `.agents/skills/`, `.claude/skills/`, `.github/skills/`, plus
+any present holdout), the matching `.gitignore` blocks,
+post-checkout hook, the adoption sections in `README.md` /
+`AGENTS.md` / `CONTRIBUTING.md`, and the committed `setup`
+skill itself.
+
+> **Critical — tear down *all* target dirs.** Removing only the
+> `.claude/skills/` + `.github/skills/` pair would **orphan** the
+> `.agents/skills/magpie-*` links (and any holdout's) — dangling
+> symlinks into a snapshot that no longer exists, plus stale
+> `.gitignore` blocks. The removal must cover every active target
+> dir per [`agents.md`](agents.md).
By default the adopter-authored `.apache-magpie-overrides/`
directory is **preserved** — it contains hand-written
@@ -88,7 +97,7 @@ every artefact).
| Local lock | `<local-lock>` | exists |
| Committed lock | `<committed-lock>` | exists |
| `.gitignore` entries | `<repo-root>/.gitignore` | which of the entries from
[`adopt.md` Step 7](adopt.md) are present |
-| Framework-skill symlinks | `<adopter-skills-dir>/` — both layers under
Pattern B; canonical side only under Pattern D (D.1: `.github/skills/`; D.2:
`.claude/skills/`); single layer under Pattern A | each symlink whose target
resolves into `<snapshot-dir>/skills/` |
+| Framework-skill symlinks | **Every active target dir**
([`agents.md`](agents.md)): `.agents/skills/` (universal — always present), any
present holdout (`.windsurf/skills/`, `.goose/skills/`), plus the
`.claude/`/`.github/` pair — both layers under Pattern B; canonical side only
under Pattern D (D.1: `.github/skills/`; D.2: `.claude/skills/`); single layer
under Pattern A | each `magpie-*` symlink whose target resolves into
`<snapshot-dir>/skills/`, in **each** target dir |
| Post-checkout hook | `<repo-root>/.git/hooks/post-checkout` | exists +
invokes `~/.claude/scripts/sandbox-add-project-root.sh` |
| Doc section: `README.md` | `<repo-root>/README.md` | contains the `##
Agent-assisted contribution (apache-steward)` heading |
| Doc section: `AGENTS.md` | `<repo-root>/AGENTS.md` | contains the `##
apache-steward framework` heading |
@@ -115,10 +124,15 @@ The following will be REMOVED:
Gitignored (no commit needed):
.apache-magpie/ (snapshot, ~N MB)
.apache-magpie.local.lock
- <adopter-skills-dir>/<symlink-1> → .apache-magpie/skills/<skill-1>/
- <adopter-skills-dir>/<symlink-2> → ...
- .github/skills/<symlink-1> (Pattern B only — second physical
layer)
+ .agents/skills/magpie-<skill-1> → .apache-magpie/skills/<skill-1>/
(universal target)
+ .agents/skills/magpie-<skill-2> → ...
+ <holdout>/skills/magpie-<skill-1> → ... (e.g. .windsurf/skills/,
.goose/skills/ — only if present)
+ <adopter-skills-dir>/magpie-<skill-1> → .apache-magpie/skills/<skill-1>/
(claude-code/github pair)
+ <adopter-skills-dir>/magpie-<skill-2> → ...
+ .github/skills/magpie-<skill-1> (Pattern B only — second physical
layer of the pair)
.git/hooks/post-checkout (if it contains the steward recipe)
+ # Target dirs (per agents.md): always .agents/skills/ (universal),
+ # any present holdout, plus the claude-code/github pair below.
# Pattern A: <adopter-skills-dir> = .claude/skills/
# Pattern B: <adopter-skills-dir> spans .claude/skills/ AND
.github/skills/
# Pattern D: <adopter-skills-dir> = canonical side only
@@ -163,8 +177,9 @@ uncommitted edits, prepend a **warning** above the table:
Commit, stash, or copy them out before continuing.
```
-If any framework-skill symlink under `<adopter-skills-dir>/`
-resolves to a path **outside** `<snapshot-dir>/` — i.e. an
+If any `magpie-*` symlink in **any active target dir**
+(`.agents/skills/`, a holdout, or the `.claude/`/`.github/`
+pair) resolves to a path **outside** `<snapshot-dir>/` — i.e. an
adopter committed a real skill at the same name post-
adoption, or a symlink points elsewhere — list it under a
separate **Preserved (not framework-owned)** subsection. The
@@ -191,9 +206,17 @@ artefacts that *depend* on others come out first, so a
half-completed unadopt never leaves a dangling symlink
pointing at a deleted snapshot.
-1. **Framework-skill symlinks.** For each entry in the
- inventory, `rm` the symlink. Per-pattern:
-
+1. **Framework-skill symlinks — in every active target dir.**
+ For each entry in the inventory, `rm` the `magpie-*` symlink.
+ Cover **every active target dir** ([`agents.md`](agents.md)),
+ not just the `.claude/`/`.github/` pair — skipping
+ `.agents/skills/` or a holdout would orphan its `magpie-*`
+ links once the snapshot is removed in step 3.
+
+ - **Universal target (`.agents/skills/`)** — one flat layer;
+ remove each `.agents/skills/magpie-<n>`. Any present holdout
+ (`.windsurf/skills/`, `.goose/skills/`, …) is torn down the
+ same flat way.
- **Pattern A** — one layer; just remove
`.claude/skills/magpie-<n>`.
- **Pattern B** — two layers; remove both
@@ -203,7 +226,10 @@ pointing at a deleted snapshot.
The directory symlink itself (`.claude/skills` or
`.github/skills`) is **adopter-owned** and **not
removed by unadopt** — it predates framework adoption
- and serves the adopter's own native skills too.
+ and serves the adopter's own native skills too. The same
+ adopter-owned rule applies to the target dirs themselves
+ (`.agents/skills/`, a holdout): only the `magpie-*` entries
+ come out, never the directory.
Never touch a non-symlink at the same path.
2. **Post-checkout hook.** Remove only if its content matches
@@ -257,9 +283,10 @@ After the deletions, verify the post-state:
- `<snapshot-dir>/` does not exist.
- `<committed-lock>` and `<local-lock>` do not exist.
-- No symlinks under `<adopter-skills-dir>/` resolve into
- `<snapshot-dir>/` (the path is gone, so dangling symlinks
- would surface here).
+- No `magpie-*` symlinks in **any active target dir**
+ (`.agents/skills/`, any holdout, or the `.claude/`/`.github/`
+ pair) resolve into `<snapshot-dir>/` (the path is gone, so
+ any orphaned dangling symlink would surface here).
- `.gitignore` no longer contains the steward entries.
- The doc sections are gone from the affected files.
- `<adopter-skills-dir>/magpie-setup/` does not exist.
@@ -277,7 +304,7 @@ A summary of what was removed + what remains:
```text
✓ Snapshot removed: .apache-magpie/
✓ Locks removed: .apache-magpie.lock, .apache-magpie.local.lock
-✓ Symlinks removed: <count> (per-pattern — A: under .claude/skills/; B:
under both .claude/skills/ AND .github/skills/; D: under the canonical side
only)
+✓ Symlinks removed: <count> across every active target dir —
.agents/skills/ (universal) + any present holdout + the claude-code/github pair
(A: under .claude/skills/; B: under both .claude/skills/ AND .github/skills/;
D: under the canonical side only)
✓ Post-checkout hook: removed (or: preserved — contained extra adopter
logic)
✓ Doc sections removed: README.md[, AGENTS.md][, CONTRIBUTING.md]
✓ .gitignore cleaned: <N> entries removed
diff --git a/skills/setup/upgrade.md b/skills/setup/upgrade.md
index 517e6be..d95b3e3 100644
--- a/skills/setup/upgrade.md
+++ b/skills/setup/upgrade.md
@@ -111,7 +111,7 @@ For each kind of drift, present:
`git-branch` and `git-tag` methods, list the commit log
(`git log --oneline <local-commit>..<committed-commit>`)
via the GitHub API or by re-cloning to a temp dir.
-- **Files touched in the framework's `.claude/skills/`** —
+- **Files touched in the framework's skill set** —
grouped by skill family. Call out any change to a skill
the adopter has an override for (the override will need
reconciliation in Step 5).
@@ -258,6 +258,23 @@ pattern-matching.
## Step 6 — Refresh framework-skill symlinks
+This step refreshes symlinks for **every active target dir**
+([`agents.md`](agents.md)), not just the `.claude/`/`.github/`
+pair. Compute the **active target set** the same way `adopt`
+does: the always-on neutral targets `.agents/skills/`
+(`universal` — the path shared by Codex, Cursor, Gemini CLI,
+Copilot, OpenCode, …), `.claude/skills/` (`claude-code`), and
+`.github/skills/` (`github`), **plus any registry holdout
+already present in the repo** (`.windsurf/skills/`,
+`.goose/skills/`, …). When the framework has added a new
+always-on target since the last run, it joins the active set and
+gets its symlinks created on this upgrade — the same way the
+effective family set below picks up newly-introduced families.
+The `.agents/skills/` target and every holdout are wired **flat**
+(one `magpie-<n>` → snapshot per skill, like conventions
+Pattern A); only the `.claude/`/`.github/` pair follows the
+A/B/C/D layout from [`conventions.md`](conventions.md).
+
Read the opt-in skill families from `<committed-lock>`
(falling back to `<local-lock>` if the committed lock is
silent on families). Compose the **effective family set**
@@ -293,40 +310,58 @@ a new `setup-*` or `list-*` skill in a release, and
contracts on a rename / removal without code changes here.
Before creating symlinks for a newly-introduced opt-in
-family, reconcile the adopter's `.gitignore` so the new
-family's snapshot symlinks are gitignored. Append the
-`.gitignore` lines from
+family — or for a newly-present active target dir — reconcile
+the adopter's `.gitignore` so the new snapshot symlinks are
+gitignored. Append the `.gitignore` lines from
[`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only)
-for the new family's prefix, matching the adopter's
-[skills-dir convention](conventions.md):
-
-- Pattern A — `/.claude/skills/<prefix>-*` only.
-- Pattern B — both `/.claude/skills/<prefix>-*` and
- `/.github/skills/<prefix>-*` (two physical symlinks per
- skill).
-- Pattern D — only the *canonical-side* `<canonical>/<prefix>-*`
- ignore line. D.1 → `/.github/skills/<prefix>-*`; D.2 →
- `/.claude/skills/<prefix>-*`. The symlinked side's
- directory symlink does not need its own ignore line — git
- does not descend into it.
+for **each active target dir** ([`agents.md`](agents.md)). Every
+framework skill is symlinked under the `magpie-` prefix, so a
+single `magpie-*` glob (plus the `!…/magpie-setup` negation that
+keeps the committed bootstrap tracked) covers them all per
+target — no per-family lines:
+
+- **Universal target (`.agents/skills/`)** — always present:
+
+ ```text
+ /.agents/skills/magpie-*
+ !/.agents/skills/magpie-setup
+ ```
+
+- **Any present holdout** (`.windsurf/skills/`,
+ `.goose/skills/`, …) — the same flat two-line block keyed on
+ its own dir.
+
+- **Claude Code + GitHub pair**, per the adopter's
+ [skills-dir convention](conventions.md):
+ - Pattern A — `/.claude/skills/magpie-*` (plus
+ `!/.claude/skills/magpie-setup`) only.
+ - Pattern B — both `/.claude/skills/magpie-*` and
+ `/.github/skills/magpie-*` (two physical symlinks per
+ skill), each with its `!…/magpie-setup` negation.
+ - Pattern D — only the *canonical-side* `<canonical>/magpie-*`
+ ignore line. D.1 → `/.github/skills/magpie-*`; D.2 →
+ `/.claude/skills/magpie-*`. The symlinked side's
+ directory symlink does not need its own ignore line — git
+ does not descend into it.
The append is idempotent — skip lines that already exist.
The same idempotence covers adopters whose `.gitignore`
already had the entries (e.g. from a manually-edited block
or a previous adopt run).
-The post-upgrade state must be: *every framework skill in
-the new snapshot that belongs to the effective family set
-has a valid symlink in `<adopter-skills-dir>`*, and *no
-symlink points at a framework skill that no longer exists
-in the snapshot*.
+The post-upgrade state must be: *in every active target dir,
+every framework skill in the new snapshot that belongs to the
+effective family set has a valid symlink*, and *no symlink (in
+any target dir) points at a framework skill that no longer
+exists in the snapshot*.
-Run two passes:
+Run two passes **per active target dir** ([`agents.md`](agents.md)):
1. **Ensure every family-member skill is linked.** For each
framework skill in the new snapshot that belongs to the
- effective family set, check
- `<adopter-skills-dir>/magpie-<skill>`:
+ effective family set, check `<target>/magpie-<skill>` in each
+ active target dir (`.agents/skills/`, `.claude/skills/`,
+ `.github/skills/`, plus any present holdout):
- If the symlink exists and points at the matching
snapshot path, leave it alone.
- If it's missing, create it.
@@ -335,34 +370,41 @@ Run two passes:
Do this unconditionally — do not skip skills whose
symlinks "should" already be there. A contributor who
- ran `git clean -fdx`, blew away `<adopter-skills-dir>` by
+ ran `git clean -fdx`, blew away a target dir by
accident, or merged a branch that removed the symlinks
- gets the full set restored without per-symlink re-
- prompting. The aggregated list of created / repaired
- links is reported in the upgrade summary (Step 8 output
- block, under the `+` and `↻` rows).
-
-2. **Reconcile stale symlinks.** Walk
- `<adopter-skills-dir>` looking for symlinks that point
- at framework skills no longer in the new snapshot
- (rename, removal). For each:
+ gets the full set restored in **every** target without
+ per-symlink re-prompting. The aggregated list of created /
+ repaired links is reported in the upgrade summary (Step 8
+ output block, under the `+` and `↻` rows). A newly-present
+ target dir (a holdout that just appeared, or a new always-on
+ target the framework added) gets its full set created here.
+
+2. **Reconcile stale symlinks.** Walk **each active target
+ dir** looking for symlinks that point at framework skills no
+ longer in the new snapshot (rename, removal). For each:
- If renamed (the framework documented a rename in its
release notes), offer to re-symlink to the new name.
- If removed, offer to remove the stale symlink.
-Per-pattern symlink layers to refresh:
-
-- **Pattern A (flat)** — refresh the single layer at
- `.claude/skills/magpie-<n>`.
-- **Pattern B (double-symlinked)** — refresh both layers
- (inner at `.github/skills/magpie-<n>`, outer at
- `.claude/skills/magpie-<n>` → inner).
-- **Pattern D (single directory symlink)** — refresh only
- the *canonical-side* layer at
- `<canonical-side>/magpie-<n>` (D.1 → `.github/skills/magpie-<n>`;
- D.2 → `.claude/skills/magpie-<n>`). The symlinked-side path
- resolves through the directory symlink and needs no
- per-skill plumbing.
+Per-target symlink layers to refresh:
+
+- **Universal target (`.agents/skills/`)** — refresh the single
+ flat layer at `.agents/skills/magpie-<n>`. Any present holdout
+ (`.windsurf/skills/`, `.goose/skills/`, …) is refreshed the
+ same flat way.
+- **Claude Code + GitHub pair**, per the detected
+ [convention](conventions.md):
+ - **Pattern A (flat)** — refresh the single layer at
+ `.claude/skills/magpie-<n>`.
+ - **Pattern B (double-symlinked)** — refresh both layers
+ (inner at `.github/skills/magpie-<n>`, outer at
+ `.claude/skills/magpie-<n>` → inner).
+ - **Pattern D (single directory symlink)** — refresh only
+ the *canonical-side* layer at
+ `<canonical-side>/magpie-<n>` (D.1 → `.github/skills/magpie-<n>`;
+ D.2 → `.claude/skills/magpie-<n>`). The symlinked-side path
+ resolves through the directory symlink and needs no
+ per-skill plumbing.
## Step 6b — Sync locally-installed hooks and configuration
@@ -641,9 +683,10 @@ Symlinks (main checkout):
- <list of removed stale symlinks>
.gitignore reconcile:
- ✓ all opt-in family prefixes already gitignored OR
- + <list of /.claude/skills/<prefix>-* and /.github/skills/<prefix>-*
- lines appended for newly-introduced opt-in families>
+ ✓ all active-target magpie-* globs already gitignored OR
+ + <list of /.agents/skills/magpie-*, /.claude/skills/magpie-*,
+ /.github/skills/magpie-* (+ any holdout) lines appended for
+ newly-introduced families or newly-present target dirs>
Hooks + local config:
✓ <list of files in sync>
diff --git a/skills/setup/verify.md b/skills/setup/verify.md
index 35764da..60192a2 100644
--- a/skills/setup/verify.md
+++ b/skills/setup/verify.md
@@ -12,8 +12,11 @@ default — surfaces gaps and remediation commands.
## Inputs
- `--auto-fix-symlinks` — *exception to read-only*. If the
- snapshot is present but symlinks are missing or dangling,
- recreate them. Used by the post-checkout hook
+ snapshot is present but symlinks are missing or dangling
+ in **any active target dir** ([`agents.md`](agents.md) —
+ `.agents/skills/`, `.claude/skills/`, `.github/skills/`, plus
+ any present holdout), recreate them across all of them. Used
+ by the post-checkout hook
([`adopt.md` Step 10](adopt.md)) on a fresh worktree
where the gitignored symlinks didn't follow the
checkout.
@@ -46,21 +49,24 @@ adoption state.
1. **Marker lock.** `.apache-magpie.lock` parses and records
`method: local`. ✓ when present; ✗ with a pointer at
`/magpie-setup` otherwise.
-2. **Symlinks resolve into `skills/`.** In **both**
- `.claude/skills/` and `.github/skills/`, every `magpie-<n>` is
- a symlink whose target (`../../skills/<n>/`) resolves to a
- directory containing `SKILL.md`. ✗ list any dangling or
- non-symlink entry; remediation: re-run `/magpie-setup`
+2. **Symlinks resolve into `skills/`.** In **every active target
+ dir** ([`agents.md`](agents.md) — `.agents/skills/`,
+ `.claude/skills/`, `.github/skills/`, plus any present
+ holdout), every `magpie-<n>` is a symlink whose target
+ (`../../skills/<n>/`) resolves to a directory containing
+ `SKILL.md`. ✗ list any dangling or non-symlink entry, naming
+ the target dir; remediation: re-run `/magpie-setup`
(idempotent).
3. **Coverage.** Every `skills/<n>/` with a `SKILL.md` has a
- matching `magpie-<n>` symlink in **both** dirs (unless a
- `skill-families:` filter was deliberately applied). ⚠ list any
- source skill with no link; remediation: `/magpie-setup`.
-4. **`.gitignore`.** `.claude/skills/*` and `.github/skills/*` are
- ignored, with `!/.claude/skills/magpie-*` and
- `!/.github/skills/magpie-*` un-ignoring the committed symlinks.
- ✗ if either un-ignore line is missing (those symlinks would not
- be tracked).
+ matching `magpie-<n>` symlink in **every active target dir**
+ (unless a `skill-families:` filter was deliberately applied).
+ ⚠ list any source skill with no link, per target; remediation:
+ `/magpie-setup`.
+4. **`.gitignore`.** Each active target dir's `<dir>/*` is
+ ignored, with `!/<dir>/magpie-*` un-ignoring the committed
+ symlinks — `.agents/skills/`, `.claude/skills/`,
+ `.github/skills/`, and any present holdout. ✗ if any un-ignore
+ line is missing (those symlinks would not be tracked).
5. **No remote leftovers.** No `.apache-magpie/` snapshot dir and
no `.apache-magpie.local.lock` — local self-adoption uses
neither. ⚠ surface either if found (a stale remote adoption was
@@ -150,21 +156,26 @@ Check that the entries from
must never be committed since the content is machine-specific
absolute paths)
-Recommended (the family patterns the adopter's
-[skills-dir convention](conventions.md) requires):
-
-- **Pattern A** — framework-skill symlink patterns
- (`security-*`, `pr-management-*`, `issue-*`,
- `setup-isolated-setup-*`, `setup-shared-config-sync`,
- `list-*`) under `.claude/skills/` only.
-- **Pattern B** — same patterns under **both**
- `.claude/skills/` and `.github/skills/` (one ignore line
- per physical symlink).
-- **Pattern D** — same patterns under the **canonical side
- only** (`.github/skills/` for D.1; `.claude/skills/` for
- D.2). The symlinked side does not need its own ignore
- lines because git does not descend into a directory
- symlink.
+Recommended (the `magpie-*` glob per **active target dir** —
+[`agents.md`](agents.md) — plus the `.claude/`/`.github/`
+[skills-dir convention](conventions.md)):
+
+- **Universal target (`.agents/skills/`)** — always present:
+ `/.agents/skills/magpie-*` with `!/.agents/skills/magpie-setup`.
+- **Any present holdout** (`.windsurf/skills/`,
+ `.goose/skills/`, …) — the same flat two-line block keyed on
+ its own dir.
+- **Claude Code + GitHub pair**, per convention:
+ - **Pattern A** — `/.claude/skills/magpie-*` (with
+ `!/.claude/skills/magpie-setup`) only.
+ - **Pattern B** — the same glob under **both**
+ `.claude/skills/` and `.github/skills/` (one ignore line
+ per physical symlink).
+ - **Pattern D** — the same glob under the **canonical side
+ only** (`.github/skills/` for D.1; `.claude/skills/` for
+ D.2). The symlinked side does not need its own ignore
+ lines because git does not descend into a directory
+ symlink.
- ✗ if `/.apache-magpie/` is not gitignored — the snapshot
is at risk of being accidentally committed.
@@ -180,16 +191,23 @@ Recommended (the family patterns the adopter's
### 5. Symlinks point at live framework skills
-For each symlink under `<adopter-skills-dir>` that resolves
+Run this check across **every active target dir**
+([`agents.md`](agents.md) — `.agents/skills/`, `.claude/skills/`,
+`.github/skills/`, plus any present holdout), not just the
+`.claude/`/`.github/` pair.
+
+For each symlink under any active target dir that resolves
into `.apache-magpie/skills/<name>/`:
- ✓ if the target exists.
-- ✗ if dangling (target deleted or snapshot missing).
- Remediation: `/magpie-setup adopt` (idempotent re-run)
- or this same skill with `--auto-fix-symlinks`.
+- ✗ if dangling (target deleted or snapshot missing), naming
+ the target dir. Remediation: `/magpie-setup adopt` (idempotent
+ re-run) or this same skill with `--auto-fix-symlinks`.
For each framework skill in the snapshot **not** symlinked
-in the adopter, classify it:
+in a given active target dir, classify it (a skill missing
+from `.agents/skills/` is as much a gap as one missing from
+`.claude/skills/`):
- **Always-on family** (every `setup-*` *except*
`setup` itself, and every `list-*` — per
@@ -208,9 +226,9 @@ in the adopter, classify it:
family; the warning prompts a decision.
The `--auto-fix-symlinks` path repairs the first two
-classes in place without prompting; the ⚠ class needs an
-explicit `/magpie-setup adopt` re-run with the family
-added to the pick.
+classes in place — in **every active target dir** — without
+prompting; the ⚠ class needs an explicit `/magpie-setup adopt`
+re-run with the family added to the pick.
### 6. `.apache-magpie-overrides/` exists + has the README
diff --git a/skills/setup/worktree-init.md b/skills/setup/worktree-init.md
index a527e91..cd33c3d 100644
--- a/skills/setup/worktree-init.md
+++ b/skills/setup/worktree-init.md
@@ -70,13 +70,16 @@ Then verify the chain end-to-end:
- `ls <worktree>/.apache-magpie/skills/` lists the
same skills as `ls <main>/.apache-magpie/skills/`.
-## Step 1b — Wire up the worktree's `<adopter-skills-dir>` symlinks
+## Step 1b — Wire up the worktree's per-target symlinks
The snapshot symlink in Step 1 only makes the framework's
-*source* available to this worktree. The `<adopter-skills-dir>`
-symlinks (the gitignored per-skill entries the agent harness
-actually resolves) are **per-worktree** — each working copy
-needs its own. A worktree branched from before adoption
+*source* available to this worktree. The per-skill symlinks (the
+gitignored entries the agent harness actually resolves) live in
+**every active target dir** ([`agents.md`](agents.md) —
+`.agents/skills/` (universal), `.claude/skills/`,
+`.github/skills/`, plus any present holdout) and are
+**per-worktree** — each working copy needs its own in every
+target. A worktree branched from before adoption
landed, or branched from a state where the symlinks were
cleaned, has none on disk.
@@ -91,18 +94,23 @@ Compose the **effective family set** for this worktree:
[`SKILL.md` Golden rule 8](SKILL.md#golden-rules). These
are added unconditionally, never read from the lock.
-For each framework skill in the effective family set:
+For each framework skill in the effective family set, and for
+**each active target dir**:
-- If `<worktree>/<adopter-skills-dir>/magpie-<skill>` is missing —
+- If `<worktree>/<target>/magpie-<skill>` is missing —
create it (gitignored).
- If it exists and points at the correct snapshot path —
leave it alone.
- If it exists but is broken or points at the wrong path —
repair it.
-Reuse the convention detection from
-[`conventions.md`](conventions.md). The pattern drives how
-many layers the worktree's `<adopter-skills-dir>` needs:
+The `.agents/skills/` (universal) target and any present
+holdout (`.windsurf/skills/`, `.goose/skills/`, …) are wired
+**flat** — one `magpie-<n>` → snapshot per skill, exactly like
+conventions Pattern A. For the `.claude/`/`.github/` pair, reuse
+the convention detection from
+[`conventions.md`](conventions.md); the pattern drives how
+many layers that pair needs:
- **Pattern A (flat)** — one layer at
`.claude/skills/magpie-<n>`.
@@ -115,20 +123,25 @@ many layers the worktree's `<adopter-skills-dir>` needs:
automatically through the directory symlink, so there is
no per-skill plumbing to add or repair on that side.
-The worktree's `.claude/skills` / `.github/skills` directory
-symlink itself (for Pattern D) is **not** a framework
-artefact — it is checked into the repo as part of the
-adopter's layout, so every worktree inherits it via the
-ordinary `git worktree add` flow. `worktree-init` does not
-touch it.
-
-Pick any framework skill symlink that should now exist (e.g.
-`<worktree>/.claude/skills/magpie-security-issue-sync/SKILL.md`) and
-confirm `readlink -f` resolves it into
+The worktree's target directories themselves — `.agents/skills/`,
+any holdout, and the `.claude/skills` / `.github/skills`
+directory symlink (for Pattern D) — are **not** framework
+artefacts; they are checked into the repo as part of the
+adopter's layout, so every worktree inherits them via the
+ordinary `git worktree add` flow. `worktree-init` only wires the
+`magpie-*` entries inside them; it does not touch the
+directories.
+
+Pick a framework skill symlink that should now exist in **each**
+active target dir (e.g.
+`<worktree>/.agents/skills/magpie-security-issue-sync/SKILL.md`
+and `<worktree>/.claude/skills/magpie-security-issue-sync/SKILL.md`)
+and confirm `readlink -f` resolves each into
`<main>/.apache-magpie/...` rather than dangling — same
sanity check as Step 1's bottom bullet, just now end-to-end
from agent-harness path through the worktree's symlink
-through the snapshot symlink to the framework source.
+through the snapshot symlink to the framework source, in every
+target.
## Step 1c — Add the worktree to its own project-local sandbox allowlists
@@ -182,9 +195,10 @@ Print a short summary:
- The main checkout's resolved path.
- The framework version the main is pinned at (read from
`<main>/.apache-magpie.lock`).
-- The effective family set wired in Step 1b, split into
- *opt-in* and *always-on*, with per-skill ✓ / + / ↻
- counts.
+- The effective family set wired in Step 1b across every
+ active target dir (`.agents/skills/`, the `.claude/`/`.github/`
+ pair, any present holdout), split into *opt-in* and
+ *always-on*, with per-skill ✓ / + / ↻ counts.
- A reminder: `upgrade` from the main, not from the worktree.
## Inputs
@@ -218,5 +232,5 @@ different overrides. Symlinking it would conflate branches.
| Symptom | Likely cause | Fix |
|---|---|---|
| Step 0 step 3 stops with "main checkout not adopted" | The main has never
run `adopt`. | `cd <main> && /magpie-setup`, then re-run `worktree-init` here. |
-| `worktree-init` runs but skills still fail to resolve | The
`<adopter-skills-dir>/magpie-<skill>` symlinks are missing from this worktree's
commit (the worktree was branched from before `adopt` ran on main). | Re-run
`worktree-init` from main's `adopt` flow afterwards, or `git merge` / `git
rebase` the branch carrying the symlink commits. |
+| `worktree-init` runs but skills still fail to resolve | The per-target
`magpie-<skill>` symlinks (in `.agents/skills/`, the `.claude/`/`.github/`
pair, or a holdout) are missing from this worktree's commit (the worktree was
branched from before `adopt` ran on main). | Re-run `worktree-init` from main's
`adopt` flow afterwards, or `git merge` / `git rebase` the branch carrying the
symlink commits. |
| `<snapshot-dir>` is a regular directory and `--force` is not passed | A
previous worktree snapshot is still on disk. | Re-run the skill, accept the
move-aside prompt, then optionally inspect `.apache-magpie.bak.<timestamp>` for
any non-snapshot content before deleting. |