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.git


The following commit(s) were added to refs/heads/main by this push:
     new 942de38a350 Make .agents/skills the canonical agent-skills home 
(#68143)
942de38a350 is described below

commit 942de38a35017a8ada6b828d5b7d7ab5b4bc251f
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun Jun 7 10:07:34 2026 +0200

    Make .agents/skills the canonical agent-skills home (#68143)
    
    Adopt the apache-steward #460 "canonical .agents/skills, relay other
    agents into it" model and move every agent skill — the repo's own and
    the magpie framework ones — to a single canonical home so they are
    discoverable by the whole shared-path agent cluster (Codex, Cursor,
    Gemini CLI, Copilot, OpenCode, …) alongside Claude Code and GitHub:
    
    - `.agents/skills/<skill>` is the canonical entry; `.github/skills/`
      and `.claude/skills/` carry per-skill relay symlinks pointing into it.
    - Relocate the repo's own skills (`aip-user-stories`,
      `airflow-translations`, `prepare-providers-documentation`) and the
      committed `magpie-setup` bootstrap copy from `.github/skills/` to
      `.agents/skills/`, with relays from `.github`/`.claude`.
    - Refresh the committed `magpie-setup` bootstrap from
      apache/airflow-steward main (the #460 canonical-.agents model;
      `conventions.md` merged into `agents.md`).
    
    Re-point the prek hooks and tooling that anchored on `.github/skills/...`
    at the new `.agents/` location — full/short license headers,
    blacken-docs, codespell, inclusive-language, markdownlint, lychee, the
    translation-namespace sync trigger, and the hardcoded SKILL.md path in
    `sync_translation_namespaces.py`. prek skips symlinks, so only the real
    files (now under `.agents/`) are processed. Add `.gitignore` negations
    so the `.claude/skills/` relays stay tracked, and update the
    provider-release doc path reference.
---
 .../skills/aip-user-stories/SKILL.md               |   0
 .../references/playbook-template.md                |   0
 .../skills/airflow-translations/SKILL.md           |   0
 .../skills/airflow-translations/locales/ar.md      |   0
 .../skills/airflow-translations/locales/ca.md      |   0
 .../skills/airflow-translations/locales/de.md      |   0
 .../skills/airflow-translations/locales/el.md      |   0
 .../skills/airflow-translations/locales/es.md      |   0
 .../skills/airflow-translations/locales/fr.md      |   0
 .../skills/airflow-translations/locales/he.md      |   0
 .../skills/airflow-translations/locales/hi.md      |   0
 .../skills/airflow-translations/locales/hu.md      |   0
 .../skills/airflow-translations/locales/it.md      |   0
 .../skills/airflow-translations/locales/ja.md      |   0
 .../skills/airflow-translations/locales/ko.md      |   0
 .../skills/airflow-translations/locales/nl.md      |   0
 .../skills/airflow-translations/locales/pl.md      |   0
 .../skills/airflow-translations/locales/pt.md      |   0
 .../skills/airflow-translations/locales/th.md      |   0
 .../skills/airflow-translations/locales/tr.md      |   0
 .../skills/airflow-translations/locales/zh-CN.md   |   0
 .../skills/airflow-translations/locales/zh-TW.md   |   0
 {.github => .agents}/skills/magpie-setup/SKILL.md  |  83 +++---
 {.github => .agents}/skills/magpie-setup/adopt.md  | 286 ++++++++++-----------
 .agents/skills/magpie-setup/agents.md              | 198 ++++++++++++++
 .../skills/magpie-setup/overrides.md               |   0
 .../skills/magpie-setup/unadopt.md                 | 119 +++++----
 .../skills/magpie-setup/upgrade.md                 | 195 +++++++-------
 {.github => .agents}/skills/magpie-setup/verify.md | 113 ++++----
 .../skills/magpie-setup/worktree-init.md           |  90 +++----
 .../prepare-providers-documentation/SKILL.md       |   0
 .claude/skills/aip-user-stories                    |   2 +-
 .claude/skills/airflow-translations                |   1 +
 .claude/skills/magpie-setup                        |   2 +-
 .claude/skills/prepare-providers-documentation     |   1 +
 .github/skills/aip-user-stories                    |   1 +
 .github/skills/airflow-translations                |   1 +
 .github/skills/magpie-setup                        |   1 +
 .github/skills/magpie-setup/conventions.md         | 278 --------------------
 .github/skills/prepare-providers-documentation     |   1 +
 .gitignore                                         |   6 +-
 .pre-commit-config.yaml                            |  18 +-
 dev/README_RELEASE_PROVIDERS.md                    |   4 +-
 scripts/ci/prek/sync_translation_namespaces.py     |   2 +-
 44 files changed, 697 insertions(+), 705 deletions(-)

diff --git a/.github/skills/aip-user-stories/SKILL.md 
b/.agents/skills/aip-user-stories/SKILL.md
similarity index 100%
rename from .github/skills/aip-user-stories/SKILL.md
rename to .agents/skills/aip-user-stories/SKILL.md
diff --git a/.github/skills/aip-user-stories/references/playbook-template.md 
b/.agents/skills/aip-user-stories/references/playbook-template.md
similarity index 100%
rename from .github/skills/aip-user-stories/references/playbook-template.md
rename to .agents/skills/aip-user-stories/references/playbook-template.md
diff --git a/.github/skills/airflow-translations/SKILL.md 
b/.agents/skills/airflow-translations/SKILL.md
similarity index 100%
rename from .github/skills/airflow-translations/SKILL.md
rename to .agents/skills/airflow-translations/SKILL.md
diff --git a/.github/skills/airflow-translations/locales/ar.md 
b/.agents/skills/airflow-translations/locales/ar.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/ar.md
rename to .agents/skills/airflow-translations/locales/ar.md
diff --git a/.github/skills/airflow-translations/locales/ca.md 
b/.agents/skills/airflow-translations/locales/ca.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/ca.md
rename to .agents/skills/airflow-translations/locales/ca.md
diff --git a/.github/skills/airflow-translations/locales/de.md 
b/.agents/skills/airflow-translations/locales/de.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/de.md
rename to .agents/skills/airflow-translations/locales/de.md
diff --git a/.github/skills/airflow-translations/locales/el.md 
b/.agents/skills/airflow-translations/locales/el.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/el.md
rename to .agents/skills/airflow-translations/locales/el.md
diff --git a/.github/skills/airflow-translations/locales/es.md 
b/.agents/skills/airflow-translations/locales/es.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/es.md
rename to .agents/skills/airflow-translations/locales/es.md
diff --git a/.github/skills/airflow-translations/locales/fr.md 
b/.agents/skills/airflow-translations/locales/fr.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/fr.md
rename to .agents/skills/airflow-translations/locales/fr.md
diff --git a/.github/skills/airflow-translations/locales/he.md 
b/.agents/skills/airflow-translations/locales/he.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/he.md
rename to .agents/skills/airflow-translations/locales/he.md
diff --git a/.github/skills/airflow-translations/locales/hi.md 
b/.agents/skills/airflow-translations/locales/hi.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/hi.md
rename to .agents/skills/airflow-translations/locales/hi.md
diff --git a/.github/skills/airflow-translations/locales/hu.md 
b/.agents/skills/airflow-translations/locales/hu.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/hu.md
rename to .agents/skills/airflow-translations/locales/hu.md
diff --git a/.github/skills/airflow-translations/locales/it.md 
b/.agents/skills/airflow-translations/locales/it.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/it.md
rename to .agents/skills/airflow-translations/locales/it.md
diff --git a/.github/skills/airflow-translations/locales/ja.md 
b/.agents/skills/airflow-translations/locales/ja.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/ja.md
rename to .agents/skills/airflow-translations/locales/ja.md
diff --git a/.github/skills/airflow-translations/locales/ko.md 
b/.agents/skills/airflow-translations/locales/ko.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/ko.md
rename to .agents/skills/airflow-translations/locales/ko.md
diff --git a/.github/skills/airflow-translations/locales/nl.md 
b/.agents/skills/airflow-translations/locales/nl.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/nl.md
rename to .agents/skills/airflow-translations/locales/nl.md
diff --git a/.github/skills/airflow-translations/locales/pl.md 
b/.agents/skills/airflow-translations/locales/pl.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/pl.md
rename to .agents/skills/airflow-translations/locales/pl.md
diff --git a/.github/skills/airflow-translations/locales/pt.md 
b/.agents/skills/airflow-translations/locales/pt.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/pt.md
rename to .agents/skills/airflow-translations/locales/pt.md
diff --git a/.github/skills/airflow-translations/locales/th.md 
b/.agents/skills/airflow-translations/locales/th.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/th.md
rename to .agents/skills/airflow-translations/locales/th.md
diff --git a/.github/skills/airflow-translations/locales/tr.md 
b/.agents/skills/airflow-translations/locales/tr.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/tr.md
rename to .agents/skills/airflow-translations/locales/tr.md
diff --git a/.github/skills/airflow-translations/locales/zh-CN.md 
b/.agents/skills/airflow-translations/locales/zh-CN.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/zh-CN.md
rename to .agents/skills/airflow-translations/locales/zh-CN.md
diff --git a/.github/skills/airflow-translations/locales/zh-TW.md 
b/.agents/skills/airflow-translations/locales/zh-TW.md
similarity index 100%
rename from .github/skills/airflow-translations/locales/zh-TW.md
rename to .agents/skills/airflow-translations/locales/zh-TW.md
diff --git a/.github/skills/magpie-setup/SKILL.md 
b/.agents/skills/magpie-setup/SKILL.md
similarity index 83%
rename from .github/skills/magpie-setup/SKILL.md
rename to .agents/skills/magpie-setup/SKILL.md
index d671d9cdced..15ccb89006d 100644
--- a/.github/skills/magpie-setup/SKILL.md
+++ b/.agents/skills/magpie-setup/SKILL.md
@@ -74,15 +74,22 @@ copy):
   one records what each machine actually fetched. Drift
   between them is surfaced and remediated by
   `/magpie-setup upgrade`.
-- Symlinks from the adopter's skill directory into
-  `<snapshot-dir>/skills/<framework-skill>/` make the
-  framework's skills callable as if they lived in the adopter
-  repo. **Each symlink is named `magpie-<framework-skill>`** —
-  every framework skill is installed under a `magpie-` prefix
-  so it is namespaced and never collides with the adopter's own
-  skills (e.g. the snapshot's `skills/pr-management-triage/`
-  becomes `.claude/skills/magpie-pr-management-triage`, invoked
-  as `/magpie-pr-management-triage`). The symlinks are also
+- Symlinks make the framework's skills callable as if they
+  lived in the adopter repo. **Each symlink is named
+  `magpie-<framework-skill>`** — every framework skill is
+  installed under a `magpie-` prefix so it is namespaced and
+  never collides with the adopter's own skills (e.g. the
+  snapshot's `skills/pr-management-triage/` becomes
+  `magpie-pr-management-triage`, invoked as
+  `/magpie-pr-management-triage`). **`.agents/skills/` is the
+  one canonical home**: its `magpie-*` entries link into
+  `<snapshot-dir>/skills/<framework-skill>/`. Every other agent
+  target (`.claude/skills/`, `.github/skills/`, …) gets a thin
+  per-skill **relay** symlink that points back at the canonical
+  entry (`.claude/skills/magpie-<n>` →
+  `../../.agents/skills/magpie-<n>`) — no matter what layout the
+  adopting project previously used (see
+  [`agents.md`](agents.md)). The symlinks are
   **gitignored** because their targets disappear on a fresh
   clone before `/magpie-setup` runs.
 - Adopter-specific modifications to framework workflows live as
@@ -98,11 +105,14 @@ copy):
 repo that cannot be adopted via the snapshot mechanism is the
 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
+with `method:local`: each canonical `magpie-<skill>` in
+`.agents/skills/` is a **committed** symlink into the in-repo
+`../../skills/<skill>/` source, and every other active agent
+target ([`agents.md`](agents.md)) — `.claude/skills/` (Claude
+Code), `.github/skills/` (GitHub's skill loader), and any present
+holdout — gets a committed **relay** symlink
+(`magpie-<skill>` → `../../.agents/skills/magpie-<skill>`) — 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 +180,7 @@ 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, and the 
**canonical-plus-relay** model: `.agents/skills/` is the one canonical home 
(links into the snapshot/source); every other target (`claude-code`, `github`, 
holdout natives like Windsurf / Goose) gets a per-skill relay symlink into 
`.agents/skills/`. Defines active-target selection, SKILL.md format 
portability, and the Claude-Code-only layer (sandbox/hooks). [...]
 | [`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. |
 
@@ -222,32 +232,44 @@ Three things gitignored in the adopter repo:
 - `<snapshot-dir>` (the entire framework snapshot — gigabytes
   potentially).
 - `<local-lock>` (per-machine state).
-- The symlinks `setup adopt` creates in the adopter's
-  skills directory (they target the gitignored snapshot, so
-  they would dangle in a fresh clone).
-
-**Committed**: this skill (`setup`), the
+- The `magpie-*` symlinks `setup adopt` creates in every active
+  target dir — the canonical ones in `.agents/skills/` (they
+  target the gitignored snapshot) and the relays in
+  `.claude/skills/` / `.github/skills/` / holdouts (they target
+  the canonical entries) — both would dangle in a fresh clone.
+  The one exception un-ignored in each dir is `magpie-setup`.
+
+**Committed**: this skill (`setup`, as the canonical
+`.agents/skills/magpie-setup/` plus its relays), the
 `<committed-lock>`, the `.apache-magpie-overrides/`
 directory, the `.gitignore` entries themselves, any
 project-doc updates the `adopt` sub-action makes.
 
-**Golden rule 5 — follow the adopter's existing skills-dir
-convention.** Different ASF projects already organise their
-`.claude/skills/` differently (see
-[`conventions.md`](conventions.md)). The `adopt` sub-action
-detects which pattern is in place and matches it.
+**Golden rule 5 — `.agents/skills/` is canonical; everything
+else just relays into it.** Regardless of how an adopting
+project previously organised its `.claude/skills/` or
+`.github/skills/`, `adopt` always wires the framework the same
+way: the canonical `magpie-*` links live in `.agents/skills/`,
+and every other active target (`.claude`, `.github`, holdouts)
+gets per-skill relay symlinks pointing back at the canonical
+entries (`.claude/skills/magpie-<n>` →
+`../../.agents/skills/magpie-<n>`). The adopter's own native
+(non-`magpie-`) skills in those dirs are left untouched. See
+[`agents.md`](agents.md).
 
 **Golden rule 6 — copy this skill, symlink the rest; all under
 the `magpie-` prefix.** This skill (source `skills/setup/`) is
 the **only** framework skill that gets **copied** into an
-adopter repo — committed as `.claude/skills/magpie-setup/`.
-All other framework skills are **symlinked** into the
-gitignored snapshot, each named `magpie-<framework-skill>`
+adopter repo — committed as the canonical
+`.agents/skills/magpie-setup/`, with committed relay symlinks to
+it from `.claude/skills/magpie-setup` and
+`.github/skills/magpie-setup`. All other framework skills are
+**symlinked** (canonical link into the gitignored snapshot, plus
+relays), each named `magpie-<framework-skill>`
 (e.g. `magpie-security-issue-import` → 
`<snapshot-dir>/skills/security-issue-import/`).
 The `magpie-` prefix namespaces every framework skill so it
 never collides with an adopter's own skills. Mixing copy and
 symlink — copying a security skill, for instance — creates a
-copying a security skill, for instance — creates a
 maintenance hazard: copies drift from the framework's source-
 of-truth, and the drift-detection mechanism (which assumes
 the framework version is the one in `<snapshot-dir>`)
@@ -301,7 +323,7 @@ modified files of this skill before continuing** the rest of
 the current run. Concretely: after the copy lands on disk,
 re-load `SKILL.md` and the sub-action file you are
 currently executing (and any helper file you have already
-opened, such as `conventions.md` or `overrides.md`), then
+opened, such as `agents.md` or `overrides.md`), then
 resume from the step after the overwrite. The reload runs as
 the **first thing** that happens after the overwrite, before
 any further reconciliation, symlink work, or doc updates.
@@ -358,6 +380,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 it is the canonica [...]
 | `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/.github/skills/magpie-setup/adopt.md 
b/.agents/skills/magpie-setup/adopt.md
similarity index 86%
rename from .github/skills/magpie-setup/adopt.md
rename to .agents/skills/magpie-setup/adopt.md
index b7b508b2316..ea7e8af5559 100644
--- a/.github/skills/magpie-setup/adopt.md
+++ b/.agents/skills/magpie-setup/adopt.md
@@ -88,44 +88,22 @@ between automatically:
      is refused.
    - **Adopter repo** (the structural markers are absent) →
      continue with the normal remote-snapshot flow below.
-4. Detect the adopter's existing skills-dir convention by
-   following [`conventions.md`](conventions.md). Pin the
-   result as `<adopter-skills-dir>` for the rest of this
-   flow.
-
-   If detection returns *"ambiguous → propose Pattern D
-   consolidation"* (both `.claude/skills/` and
-   `.github/skills/` exist as regular directories with
-   independent, non-aliased content), run the
-   **Pre-Pattern-D consolidation** flow described under
-   [section D of 
`conventions.md`](conventions.md#d-single-directory-symlink--one-of-claudeskills--githubskills-is-a-symlink-to-the-other)
-   before continuing:
-
-   - List the skills in each directory with their content
-     fingerprint (real dir vs symlink, target if symlink,
-     SKILL.md presence).
-   - Flag any name collisions where the two sides have
-     different content for the same name.
-   - Use a structured prompt (`AskUserQuestion` when the
-     harness offers one) with three options: **D.1**
-     (consolidate under `.github/skills/`), **D.2**
-     (consolidate under `.claude/skills/`), or **decline**
-     (fall back to Pattern A treating `.claude/skills/` as
-     canonical and leaving `.github/skills/` alone).
-   - On D.1 / D.2 confirmation: move every skill from the
-     side that will become the symlink into the side that
-     will become the real directory (resolving any flagged
-     name collisions first — never auto-rename adopter
-     content), then replace the now-empty side with a
-     relative symlink to the other side, then re-run
-     detection to confirm the pattern is now D.
-   - If the user declines or unresolved name collisions
-     block consolidation, fall back to Pattern A and pin
-     `<adopter-skills-dir>` = `.claude/skills/` as usual.
-
-   The consolidation is a one-time, deliberate layout
-   change; the adopt flow surfaces every step before
-   writing.
+4. Compute the **active target set** per
+   [`agents.md`](agents.md): the always-on neutral targets
+   (`universal` = `.agents/skills/`, the canonical home; plus
+   the `claude-code` + `github` relay pair), any other registry
+   dir already present in the repo, and any `agents:<list>`
+   opt-in. **`.agents/skills/` is always canonical** — its
+   `magpie-*` entries are the links into the snapshot; every
+   other active target gets per-skill relay symlinks into it.
+
+   There is **no skills-dir convention to detect**: regardless
+   of how the adopter previously organised `.claude/skills/` or
+   `.github/skills/`, the framework always wires the `magpie-*`
+   set the same way (canonical in `.agents/skills/`, relayed
+   elsewhere) and leaves the adopter's own native (non-`magpie-`)
+   skills in place. Pin `.agents/skills/` as the canonical dir
+   for the rest of this flow.
 
 ## Local self-adoption (`method:local`)
 
@@ -149,12 +127,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,27 +172,44 @@ 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-*
    ```
 
-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.
-7. **Verify + stage.** Confirm every `magpie-<n>` symlink (in
-   both dirs) resolves to a directory containing `SKILL.md`, then
-   suggest the user `git add` the symlinks, `.apache-magpie.lock`,
-   and `.gitignore`.
+   (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** (canonical first, then relays). For
+   each enumerated skill `<n>`:
+   - **Canonical** — create `.agents/skills/magpie-<n>` →
+     `../../skills/<n>` (the in-repo source).
+   - **Relays** — for every *other* active target dir from
+     [`agents.md`](agents.md) (`.claude/skills/`, `.github/skills/`,
+     plus any present holdout), create `magpie-<n>` →
+     `../../.agents/skills/magpie-<n>` (pointing back at the
+     canonical entry).
+
+   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 canonical `magpie-<n>`
+   symlink resolves to a directory containing `SKILL.md`, and
+   every relay resolves through `.agents/skills/magpie-<n>` to the
+   same, then suggest the user `git add` the symlinks,
+   `.apache-magpie.lock`, and `.gitignore`.
 
 Self-adoption skips the adopter-only steps entirely: no snapshot
 fetch (Step 3), no committed-`setup` reconcile (Step 3b), no
@@ -299,24 +296,24 @@ snapshot's version before the rest of this run executes —
 otherwise we finish adoption against the *old* bootstrap
 logic for a *new* framework version.
 
-1. Diff `<adopter-skills-dir>/magpie-setup/` against
+1. Diff the canonical committed copy
+   `.agents/skills/magpie-setup/` against
    `.apache-magpie/skills/setup/`.
 2. If they match — skip the rest of this step.
 3. If they differ and the adopter has **no** local
    modifications beyond what the snapshot ships — overwrite
-   the committed copy from the snapshot:
+   the canonical committed copy from the snapshot:
 
    ```bash
-   # Flat layout:
-   rm -rf <adopter-skills-dir>/magpie-setup
+   rm -rf .agents/skills/magpie-setup
    cp -r .apache-magpie/skills/setup \
-         <adopter-skills-dir>/magpie-setup
-
-   # Double-symlinked layout: copy into .github/skills/ —
-   # the .claude/skills/magpie-setup symlink already
-   # points at it.
+         .agents/skills/magpie-setup
    ```
 
+   The relay symlinks (`.claude/skills/magpie-setup`,
+   `.github/skills/magpie-setup`) point at
+   `../../.agents/skills/magpie-setup` and need no change.
+
 4. If the adopter **does** have local modifications,
    surface the diff and stop. The user either (a) confirms
    the local mods can be discarded, (b) upstreams them as a
@@ -325,10 +322,10 @@ logic for a *new* framework version.
    continues against the in-flight (older) version with a
    warning.
 5. **Reload in-flight.** Immediately after the copy lands,
-   re-read `<adopter-skills-dir>/magpie-setup/SKILL.md`
-   and `<adopter-skills-dir>/magpie-setup/adopt.md` (the
+   re-read `.agents/skills/magpie-setup/SKILL.md`
+   and `.agents/skills/magpie-setup/adopt.md` (the
    current sub-action file), plus any helper file already
-   open in this run (`conventions.md`, `overrides.md`),
+   open in this run (`agents.md`, `overrides.md`),
    before continuing to Step 4. The remaining steps run
    against the just-loaded content.
 
@@ -508,51 +505,30 @@ idempotent — re-add them if they're missing.
 /.claude/settings.local.json
 ```
 
-**Symlink-pattern entries — vary by adopter
-[skills-dir convention](conventions.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 — no per-family
-  lines.
-
-- **Pattern A (flat)** — only the `.claude/skills/...` lines:
-
-  ```text
-  /.claude/skills/magpie-*
-  !/.claude/skills/magpie-setup
-  ```
-
-- **Pattern B (double-symlinked)** — both `.claude/skills/...`
-  AND `.github/skills/...` lines, because each framework skill
-  has two physical symlinks (outer at `.claude/skills/magpie-<n>`,
-  inner at `.github/skills/magpie-<n>`):
+**Symlink entries — one uniform block per active target
+([`agents.md`](agents.md)), no per-layout variation.** 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. The canonical target (`.agents/skills/`) and every relay
+target (`.claude/skills/`, `.github/skills/`, any present holdout)
+get the **same** two-line block, keyed on the target's own dir:
 
-  ```text
-  /.claude/skills/magpie-*
-  !/.claude/skills/magpie-setup
-  /.github/skills/magpie-*
-  !/.github/skills/magpie-setup
-  ```
-
-- **Pattern D (single directory symlink)** — only the
-  *canonical-side* `.../skills/...` line. With D.1
-  (canonical = `.github/skills/`):
-
-  ```text
-  /.github/skills/magpie-*
-  !/.github/skills/magpie-setup
-  ```
-
-  With D.2 (canonical = `.claude/skills/`), use
-  `/.claude/skills/magpie-*` (plus `!/.claude/skills/magpie-setup`)
-  instead. Pattern D does not
-  need ignore lines on the *symlinked* side because that side
-  is itself a single tracked symlink — git does not descend
-  into it, so the symlinked-side paths match no tracked file.
+```text
+/.agents/skills/magpie-*
+!/.agents/skills/magpie-setup
+/.claude/skills/magpie-*
+!/.claude/skills/magpie-setup
+/.github/skills/magpie-*
+!/.github/skills/magpie-setup
+```
 
-- **Pattern C (none yet)** — same as the pattern the user
-  picks during adopt (defaults to A).
+Add the analogous two lines for any present holdout
+(`.windsurf/skills/`, `.goose/skills/`, …). The relay symlinks are
+gitignored exactly like the canonical ones: a relay points at
+`../../.agents/skills/magpie-<n>`, which itself targets the
+gitignored snapshot, so it dangles on a fresh clone before
+`/magpie-setup` runs.
 
 The `magpie-*` glob covers both the opt-in families and the
 always-on families (`magpie-setup-*` and the `magpie-list-*`
@@ -577,11 +553,11 @@ adopt flow checks for the line and adds it if missing.
 
 ## Step 8 — Wire up the framework-skill symlinks
 
-The skill walks `<snapshot-dir>/skills/` and creates
-a gitignored symlink for every framework skill the adopter
-should have callable, at `<adopter-skills-dir>/magpie-<skill>` →
-relative path into
-`<snapshot-dir>/skills/<skill>/`.
+The skill walks `<snapshot-dir>/skills/` and, for every
+framework skill the adopter should have callable, creates a
+gitignored **canonical** symlink at `.agents/skills/magpie-<skill>`
+→ relative path into `<snapshot-dir>/skills/<skill>/`, plus a
+**relay** symlink in every other active target dir.
 
 The set of skills to link is the **union** of:
 
@@ -601,28 +577,28 @@ 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
-[`conventions.md`](conventions.md)):
+Symlink wiring (targets from [`agents.md`](agents.md)) — the
+**canonical-plus-relay** model, applied identically no matter
+what layout the adopter's `.claude/` / `.github/` were in before:
 
 Every symlink is named `magpie-<n>` (the `magpie-` prefix
-namespaces framework skills); its target keeps the snapshot's
-clean source name `skills/<n>/`.
-
-- **Pattern A (flat)** — one symlink per skill at
-  `.claude/skills/magpie-<n>` → snapshot. Gitignored.
-- **Pattern B (double-symlinked)** — two symlinks per skill:
-  the inner one in `.github/skills/magpie-<n>` → snapshot, the
-  outer `.claude/skills/magpie-<n>` →
-  `../../.github/skills/magpie-<n>/`. Both gitignored.
-- **Pattern D (single directory symlink)** — one symlink per
-  skill at the *canonical-side* `<canonical>/skills/magpie-<n>`
-  → snapshot. **Skip the symlinked side entirely** — one of
-  `.claude/skills` / `.github/skills` is itself a directory
-  symlink into the other, so the symlinked-side path is
-  automatically resolved. With D.1 the canonical side is
-  `.github/skills/`; with D.2 it is `.claude/skills/`.
-  Gitignored.
-- **Pattern C (none yet)** — same as A.
+namespaces framework skills). Wire the **same set of skills**
+into **every active target dir**, canonical first:
+
+- **Canonical target (`.agents/skills/`)** — one symlink per
+  skill at `.agents/skills/magpie-<n>` → relative path into the
+  snapshot (`../../.apache-magpie/skills/<n>/`). Gitignored. This
+  is the single placement that makes the framework discoverable to
+  Codex, Cursor, Gemini CLI, Copilot, OpenCode, and the rest of
+  the shared-path cluster, and the one source every relay points
+  at.
+
+- **Relay targets (`.claude/skills/`, `.github/skills/`, any
+  present holdout)** — one symlink per skill at
+  `<target>/skills/magpie-<n>` → `../../.agents/skills/magpie-<n>`
+  (pointing back at the canonical entry, **not** the snapshot).
+  Gitignored. The adopter's own native (non-`magpie-`) skills in
+  these dirs are left untouched.
 
 **Never overwrite an existing committed skill** of the same
 name. Surface conflicts and stop. The bootstrap `setup` skill
@@ -934,7 +910,7 @@ the user before writing:
 # apache-steward post-checkout hook (installed by /magpie-setup adopt).
 # Add the current worktree's working dir to the worktree's own
 # .claude/settings.local.json sandbox allowlists (per issue #197).
-# Chains into the helper if installed by /setup-isolated-setup-install;
+# Chains into the helper if installed by /magpie-setup-isolated-setup-install;
 # no-op when the helper is absent.
 set -u
 if [ -x "$HOME/.claude/scripts/sandbox-add-project-root.sh" ]; then
@@ -947,7 +923,7 @@ The `|| true` guard keeps the hook from failing the 
surrounding
 git operation (`git checkout`, `git worktree add`) — the hook is
 best-effort reconciliation, not a gate.
 
-If the operator has not yet run `/setup-isolated-setup-install`,
+If the operator has not yet run `/magpie-setup-isolated-setup-install`,
 the helper-script line is a no-op (the `-x` test fails). When
 they later install the secure setup, no hook re-write is needed:
 the next `post-checkout` fires the helper automatically.
@@ -1004,7 +980,7 @@ framework before they hit a "skill not found" error:
    [`.apache-magpie.lock`](.apache-magpie.lock). The only
    framework artefact committed to this repo is the
    `setup` skill at
-   [`.github/skills/magpie-setup/`](.github/skills/magpie-setup/);
+   [`.agents/skills/magpie-setup/`](.agents/skills/magpie-setup/);
    everything else is a gitignored symlink the setup skill
    wires up.
 
@@ -1013,7 +989,7 @@ framework before they hit a "skill not found" error:
 
        /magpie-setup
 
-   (or follow [`.claude/skills/magpie-setup/`](.claude/skills/magpie-setup/))
+   (or follow [`.agents/skills/magpie-setup/`](.agents/skills/magpie-setup/))
    to fetch the snapshot per the committed lock, scaffold the
    gitignored symlinks, and install the post-checkout hook
    that re-creates them on each worktree checkout.
@@ -1027,10 +1003,8 @@ framework before they hit a "skill not found" error:
 
    Trim the skill-family list to what was actually picked in
    Step 5 (only mention `security-*` if the adopter installed
-   that family, etc.). Adjust the skill paths to the adopter's
-   convention (flat / double-symlinked / single-directory-symlink
-   — see [`conventions.md`](conventions.md)). Skip this sub-step
-   entirely if `README.md` does not exist.
+   that family, etc.). Skip this sub-step entirely if
+   `README.md` does not exist.
 
 2. **`AGENTS.md` (agent-facing detail, ONLY if the file
    already exists).** Agent harnesses load this file
@@ -1053,7 +1027,7 @@ framework before they hit a "skill not found" error:
 
    A fresh clone needs the snapshot populated before any
    framework skill is invocable. Run `/magpie-setup` (or
-   follow [`.claude/skills/magpie-setup/`](.claude/skills/magpie-setup/))
+   follow [`.agents/skills/magpie-setup/`](.agents/skills/magpie-setup/))
    to fetch it per the committed
    [`.apache-magpie.lock`](.apache-magpie.lock). The
    contributor-facing summary of the adoption + setup flow
@@ -1110,7 +1084,7 @@ Four passes, in this order:
 2. **Propagate to every worktree (run `worktree-init`
    unconditionally).** The main is now adopted; any
    pre-existing linked worktree of this repo still lacks
-   the snapshot symlink and the `<adopter-skills-dir>`
+   the snapshot symlink and the per-target framework-skill
    symlinks. `worktree-init` is **always run on every
    worktree** at the end of adopt, even when none exist
    yet, even when the worktree appears wired, because
@@ -1133,8 +1107,8 @@ Four passes, in this order:
      the family set from `<main>/.apache-magpie.lock` plus
      the always-on families per
      [`SKILL.md` Golden rule 8](SKILL.md#golden-rules), and
-     reconciles both the snapshot symlink and the
-     `<adopter-skills-dir>` symlinks (see
+     reconciles both the snapshot symlink and the canonical +
+     relay framework-skill symlinks (see
      [`worktree-init.md` Step 1 + Step 1b](worktree-init.md)).
    - Collect each invocation's recap into a per-worktree
      row in the adopt summary's `Worktrees:` section.
@@ -1185,7 +1159,7 @@ Four passes, in this order:
 
    - **Helper absent** (`~/.claude/scripts/sandbox-add-project-root.sh`
      does not exist) → surface as ⚠ in the adopt summary with a
-     pointer at `/setup-isolated-setup-install`. Do not block
+     pointer at `/magpie-setup-isolated-setup-install`. Do not block
      adopt — many adopters set up secure-agent isolation later,
      and the framework-skill symlinks are usable without it (the
      adopter just runs Bash outside the sandbox until they wire
@@ -1231,16 +1205,18 @@ Committed (you'll see in `git status`):
   .gitignore
   .apache-magpie.lock
   .apache-magpie-overrides/README.md
-  <adopter-skills-dir>/magpie-setup/   (this skill itself)
+  .agents/skills/magpie-setup/         (this skill itself — canonical copy)
+  .claude/skills/magpie-setup          (relay symlink → 
../../.agents/skills/magpie-setup)
+  .github/skills/magpie-setup          (relay symlink → 
../../.agents/skills/magpie-setup)
   README.md (or CONTRIBUTING.md)
 
 Gitignored (do NOT commit):
   .apache-magpie/
   .apache-magpie.local.lock
-  <adopter-skills-dir>/magpie-*   (except magpie-setup, committed above)  # 
every framework skill: opt-in + always-on families
-  # Pattern A:  <adopter-skills-dir> = .claude/skills/
-  # Pattern B:  <adopter-skills-dir> = both .claude/skills/ AND .github/skills/
-  # Pattern D:  <adopter-skills-dir> = .github/skills/ only
+  .agents/skills/magpie-*   (except magpie-setup, committed above)  # 
canonical links into the snapshot: opt-in + always-on families
+  .claude/skills/magpie-*   (except magpie-setup, committed above)  # relays → 
../../.agents/skills/magpie-*
+  .github/skills/magpie-*   (except magpie-setup, committed above)  # relays → 
../../.agents/skills/magpie-*
+  # plus the same two lines for any present holdout (.windsurf/skills/, 
.goose/skills/, …)
 ```
 
 Then suggest the user `git add` the committed files and open
diff --git a/.agents/skills/magpie-setup/agents.md 
b/.agents/skills/magpie-setup/agents.md
new file mode 100644
index 00000000000..f8210e6d6b7
--- /dev/null
+++ b/.agents/skills/magpie-setup/agents.md
@@ -0,0 +1,198 @@
+<!-- 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 **(canonical)** | 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 (relay) | Claude Code |
+| `github` | `.github/skills/` | native (relay) | GitHub's skill loader |
+| `windsurf` | `.windsurf/skills/` | native (relay) | Windsurf |
+| `goose` | `.goose/skills/` | native (relay) | 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 canonical directory — `.agents/skills/`
+
+`.agents/skills/` is the **one canonical home** for every framework
+skill. Its `magpie-<skill>` entries are the links that resolve to
+the actual skill source — the gitignored snapshot
+(`.apache-magpie/skills/<skill>/`) for a normal adopter, or the
+in-repo `../../skills/<skill>/` source for the framework's own
+[local self-adoption](adopt.md#local-self-adoption-methodlocal).
+
+This is the load-bearing move for neutrality on two fronts:
+
+1. **One placement covers the whole shared-path cluster.** A large
+   set of agents (Codex, Cursor, Gemini CLI, GitHub Copilot,
+   OpenCode, Cline, Zed, Warp, …) all read `.agents/skills/` as
+   their project-scope skills path, so a single
+   `.agents/skills/magpie-<skill>` link is seen by all of them:
+
+   ```text
+   .agents/skills/magpie-pr-management-triage   →  the canonical link
+      ├─ Codex      picks it up
+      ├─ Cursor     picks it up
+      ├─ Gemini CLI picks it up
+      └─ Copilot …  picks it up
+   ```
+
+2. **Every other target is a thin relay into it.** Agents with a
+   bespoke folder (`claude-code` → `.claude/skills/`, `github` →
+   `.github/skills/`, `windsurf`, `goose`, …) do **not** link into
+   the snapshot independently. Each one gets a per-skill relay
+   symlink that points back at the canonical entry:
+
+   ```text
+   .claude/skills/magpie-<skill>   →  ../../.agents/skills/magpie-<skill>
+   .github/skills/magpie-<skill>   →  ../../.agents/skills/magpie-<skill>
+   ```
+
+   The snapshot path appears exactly **once** — in
+   `.agents/skills/`. Re-pointing the framework at a new snapshot,
+   or repairing a broken link, is a single-source operation; the
+   relays follow automatically. Adopters keep their own native
+   (non-`magpie-`) skills in `.claude/skills/` / `.github/skills/`
+   untouched — only the `magpie-*` entries are relayed.
+
+(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/`, canonical) **plus** the `claude-code` +
+   `github` relay pair. These three are wired unconditionally; the
+   relays are cheap relative symlinks, 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 (as a relay).
+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 it is the canonical home every relay points
+   at — dropping it would leave the relays dangling.
+
+The flow **never** removes or rewrites an adopter's own
+non-`magpie-` skill content in any target dir. It only adds /
+repairs `magpie-*` symlinks. Whatever layout an adopter's
+`.claude/` / `.github/` directories were in before, the framework
+always wires the `magpie-*` set the same way: canonical in
+`.agents/skills/`, relayed everywhere else.
+
+## How the framework's rules generalise across targets
+
+Every adoption rule is "canonical link, then relays", not
+"per-target independent link":
+
+- **`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 **uniform** block per active target dir,
+  with no per-layout variation: `/<dir>/magpie-*` ignored plus
+  `!/<dir>/magpie-setup` un-ignored. The negation keeps the one
+  committed bootstrap (`magpie-setup`) tracked; the glob ignores
+  the rest (the canonical links target the gitignored snapshot, so
+  the relays that follow them dangle on a fresh clone). See
+  [`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only).
+- **Symlink wiring** — the canonical `magpie-<n>` →
+  snapshot/source link is created once in `.agents/skills/`; every
+  other active target (`claude-code`, `github`, `windsurf`,
+  `goose`, …) gets a per-skill relay `magpie-<n>` →
+  `../../.agents/skills/magpie-<n>`. See
+  [`adopt.md` Step 8](adopt.md#step-8--wire-up-the-framework-skill-symlinks).
+- **Committed bootstrap** ([`SKILL.md` Golden rule 6](SKILL.md#golden-rules))
+  — the one committed framework artefact, `magpie-setup`, lives at
+  the **canonical** `.agents/skills/magpie-setup/` (a committed
+  copy for adopters; a committed symlink under self-adoption).
+  `.claude`/`.github` carry a committed relay symlink to it.
+- **Local self-adoption** (framework checkout) — canonical
+  committed symlinks into `../../skills/<skill>/` in
+  `.agents/skills/`, plus committed relays into
+  `../../.agents/skills/magpie-<skill>` in every other active
+  target. 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 canonical `.agents/skills/magpie-*`
+  links; removing only `.agents` would leave every relay dangling.
+
+## 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;
+`.agents/skills/` links into it, and every other target dir
+resolves into it through the `.agents/skills/` relay.
+
+## 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.
diff --git a/.github/skills/magpie-setup/overrides.md 
b/.agents/skills/magpie-setup/overrides.md
similarity index 100%
rename from .github/skills/magpie-setup/overrides.md
rename to .agents/skills/magpie-setup/overrides.md
diff --git a/.github/skills/magpie-setup/unadopt.md 
b/.agents/skills/magpie-setup/unadopt.md
similarity index 72%
rename from .github/skills/magpie-setup/unadopt.md
rename to .agents/skills/magpie-setup/unadopt.md
index ba9ef9255a7..a09f33efa89 100644
--- a/.github/skills/magpie-setup/unadopt.md
+++ b/.agents/skills/magpie-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
@@ -70,9 +79,12 @@ relevant override file rather than unadopting.
    lock, the adopter ran the install recipe but never
    completed `/magpie-setup adopt`; treat that as not-yet-
    adopted and stop with the same message.)
-5. Detect the adopter's skills-dir convention per
-   [`conventions.md`](conventions.md). Pin the result as
-   `<adopter-skills-dir>` for the rest of this flow.
+5. Compute the **active target set** per
+   [`agents.md`](agents.md): the canonical `.agents/skills/`, the
+   `.claude/skills/` + `.github/skills/` relay pair, and any
+   holdout dir present. There is no skills-dir convention to
+   detect — every target carries `magpie-*` symlinks in the same
+   canonical-plus-relay shape.
 
 ## Step 1 — Inventory what was installed
 
@@ -88,13 +100,13 @@ 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)): the canonical `.agents/skills/` (always present), 
the `.claude/skills/` + `.github/skills/` relay pair, and any present holdout 
(`.windsurf/skills/`, `.goose/skills/`) | each `magpie-*` symlink — canonical 
entries resolving into `<snapshot-dir>/skills/`, relays resolving into 
`.agents/skills/magpie-*` — 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 |
 | Doc section: `CONTRIBUTING.md` | `<repo-root>/CONTRIBUTING.md` | contains 
the adoption section (fallback layout) |
 | Overrides directory | `<repo-root>/.apache-magpie-overrides/` | exists; 
count framework-scaffold files vs adopter-authored |
-| `setup` skill itself | `<adopter-skills-dir>/magpie-setup/` | exists (this 
is the only committed framework skill) |
+| `setup` skill itself | canonical `.agents/skills/magpie-setup/` + the 
`.claude`/`.github` relay symlinks to it | exists (this is the only committed 
framework skill) |
 
 For the overrides directory: distinguish the
 **framework-scaffold** files (`README.md`, `user.md` from
@@ -115,21 +127,24 @@ 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>/   
(canonical)
+    .agents/skills/magpie-<skill-2>      → ...
+    .claude/skills/magpie-<skill-1>      → 
../../.agents/skills/magpie-<skill-1>   (relay)
+    .github/skills/magpie-<skill-1>      → 
../../.agents/skills/magpie-<skill-1>   (relay)
+    <holdout>/skills/magpie-<skill-1>    → 
../../.agents/skills/magpie-<skill-1>   (relay; e.g. .windsurf/skills/, 
.goose/skills/ — only if present)
     .git/hooks/post-checkout              (if it contains the steward recipe)
-    # 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
-    #             (D.1: .github/skills/;  D.2: .claude/skills/)
+    # Target dirs (per agents.md): canonical .agents/skills/, the
+    #   .claude/skills/ + .github/skills/ relay pair, plus any present
+    #   holdout — each carries one magpie-<n> entry per linked skill.
 
   Committed (will show in `git status`):
     .apache-magpie.lock                  (the project's pin)
     .gitignore                            (the entries listed in adopt.md Step 
7)
     README.md                             (the `## Agent-assisted contribution 
(apache-steward)` section)
     AGENTS.md                             (the `## apache-steward framework` 
section, if present)
-    <adopter-skills-dir>/magpie-setup/   (this skill itself — self-destructive)
+    .agents/skills/magpie-setup/         (this skill itself — 
self-destructive; canonical copy)
+    .claude/skills/magpie-setup          (relay symlink)
+    .github/skills/magpie-setup          (relay symlink)
 
 The following will be PRESERVED:
 
@@ -163,12 +178,15 @@ 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
-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
-unadopt flow never deletes content it does not own.
+If any `magpie-*` symlink in **any active target dir**
+(canonical `.agents/skills/`, a holdout, or the `.claude/`/`.github/`
+relay pair) resolves to a path **outside** the adoption — i.e. a
+canonical entry that does not resolve into `<snapshot-dir>/`, a
+relay that does not resolve through `.agents/skills/`, or an
+adopter who committed a real skill at the same name post-adoption
+— list it under a separate **Preserved (not framework-owned)**
+subsection. The unadopt flow never deletes content it does not
+own.
 
 ## Step 3 — Confirm
 
@@ -191,19 +209,24 @@ 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:
-
-   - **Pattern A** — one layer; just remove
-     `.claude/skills/magpie-<n>`.
-   - **Pattern B** — two layers; remove both
-     `.claude/skills/magpie-<n>` and `.github/skills/magpie-<n>`.
-   - **Pattern D** — one layer at the canonical side
-     (D.1: `.github/skills/magpie-<n>`; D.2: `.claude/skills/magpie-<n>`).
-     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.
+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.
+
+   - **Canonical target (`.agents/skills/`)** — remove each
+     `.agents/skills/magpie-<n>` (the link into the snapshot).
+   - **Relay targets (`.claude/skills/`, `.github/skills/`, any
+     present holdout)** — remove each `<target>/skills/magpie-<n>`
+     (the relay into `.agents/skills/`).
+
+   The target dirs themselves (`.agents/skills/`, `.claude/skills/`,
+   `.github/skills/`, any holdout) are **adopter-owned** and **not
+   removed by unadopt** — they may predate framework adoption and
+   serve the adopter's own native skills too. 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
@@ -238,9 +261,10 @@ pointing at a deleted snapshot.
 7. **Committed lock.** `git rm <committed-lock>`.
 8. **Overrides directory** *(only if `--purge-overrides`)*.
    `git rm -r .apache-magpie-overrides/`.
-9. **`setup` skill itself.**
-   `git rm -r <adopter-skills-dir>/magpie-setup/`. After
-   this step the running skill has deleted its own committed
+9. **`setup` skill itself.** `git rm -r` the canonical copy
+   `.agents/skills/magpie-setup/` and its relay symlinks
+   `.claude/skills/magpie-setup` and `.github/skills/magpie-setup`.
+   After this step the running skill has deleted its own committed
    source. Future invocations of `/magpie-setup` will
    resolve to nothing — the adopter has to re-run the
    install recipe in
@@ -257,12 +281,15 @@ 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 remain in **any active target dir**
+  (canonical `.agents/skills/`, the `.claude/`/`.github/` relay
+  pair, or any holdout) — neither dangling canonical links into
+  the removed `<snapshot-dir>/` nor relays into the now-empty
+  `.agents/skills/`.
 - `.gitignore` no longer contains the steward entries.
 - The doc sections are gone from the affected files.
-- `<adopter-skills-dir>/magpie-setup/` does not exist.
+- `.agents/skills/magpie-setup/` and its `.claude`/`.github`
+  relays do not exist.
 - If `--purge-overrides`: `.apache-magpie-overrides/` does
   not exist.
 - If *not* `--purge-overrides`:
@@ -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 — canonical 
.agents/skills/ + the .claude/skills/ + .github/skills/ relay pair + any 
present holdout
 ✓ Post-checkout hook:      removed (or: preserved — contained extra adopter 
logic)
 ✓ Doc sections removed:    README.md[, AGENTS.md][, CONTRIBUTING.md]
 ✓ .gitignore cleaned:      <N> entries removed
@@ -286,7 +313,7 @@ A summary of what was removed + what remains:
 Preserved:
   .apache-magpie-overrides/   (M files; pass `--purge-overrides` to remove)
   ~/.config/apache-magpie/user.md   (per-user; shared with other adopters on 
this machine — remove manually if this was your last adoption)
-  .claude/skills (or .github/skills)   (Pattern D directory symlink — 
adopter-owned, predates framework adoption)
+  .agents/skills/, .claude/skills/, .github/skills/   (target dirs — 
adopter-owned; only the magpie-* entries were removed)
   <list of any non-steward-owned content the plan flagged>
 
 Staged for commit (you'll see in `git status`):
@@ -294,7 +321,7 @@ Staged for commit (you'll see in `git status`):
   M  .gitignore
   M  README.md
   M  AGENTS.md             (if section was present)
-  D  <adopter-skills-dir>/magpie-setup/...
+  D  .agents/skills/magpie-setup/...   (+ .claude/.github relay symlinks)
 
 To re-adopt later: follow docs/setup/install-recipes.md in the
 framework repo at https://github.com/apache/airflow-steward.
diff --git a/.github/skills/magpie-setup/upgrade.md 
b/.agents/skills/magpie-setup/upgrade.md
similarity index 82%
rename from .github/skills/magpie-setup/upgrade.md
rename to .agents/skills/magpie-setup/upgrade.md
index bc104e10af4..b760a7e84b1 100644
--- a/.github/skills/magpie-setup/upgrade.md
+++ b/.agents/skills/magpie-setup/upgrade.md
@@ -67,7 +67,7 @@ must be migrated by hand.
 If you detect **any** legacy artefact here —
 `.apache-steward.lock`, `.apache-steward/`,
 `.apache-steward-overrides/`, a committed
-`<adopter-skills-dir>/setup-steward/`, or a framework symlink
+`setup-steward/` skill directory, or a framework symlink
 **without** the `magpie-` prefix — do **not** continue the normal
 upgrade against the half-migrated state. Stop and surface the
 manual remediation:
@@ -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).
@@ -171,9 +171,8 @@ project just pinned to, not against the pre-upgrade
 bootstrap logic. It implements
 [`SKILL.md` Golden rule 9](SKILL.md#golden-rules).
 
-1. Compute the diff between the adopter-side
-   `<adopter-skills-dir>/magpie-setup/` (committed copy)
-   and the snapshot's
+1. Compute the diff between the canonical committed copy
+   `.agents/skills/magpie-setup/` and the snapshot's
    `.apache-magpie/skills/setup/`.
 2. **If the adopter has local modifications** to their
    committed copy beyond what the snapshot ships — surface
@@ -187,37 +186,22 @@ bootstrap logic. It implements
    over the committed copy:
 
    ```bash
-   # For the flat layout (Pattern A):
-   rm -rf .claude/skills/magpie-setup
+   # Overwrite the canonical committed copy:
+   rm -rf .agents/skills/magpie-setup
    cp -r .apache-magpie/skills/setup \
-         .claude/skills/magpie-setup
-
-   # For the double-symlinked layout (Pattern B):
-   rm -rf .github/skills/magpie-setup
-   cp -r .apache-magpie/skills/setup \
-         .github/skills/magpie-setup
-   # The .claude/skills/magpie-setup per-skill symlink does
-   # not need touching — it points at .github/skills/magpie-setup
-   # which is now the new content.
-
-   # For the single directory-symlink layout (Pattern D),
-   # write to the *canonical* side only. With D.1
-   # (canonical = .github/skills/):
-   rm -rf .github/skills/magpie-setup
-   cp -r .apache-magpie/skills/setup \
-         .github/skills/magpie-setup
-   # With D.2 (canonical = .claude/skills/), write to
-   # .claude/skills/magpie-setup instead. Either way: the
-   # symlinked side resolves to the refreshed content
-   # automatically — nothing to touch there.
+         .agents/skills/magpie-setup
+   # The relay symlinks (.claude/skills/magpie-setup,
+   # .github/skills/magpie-setup) point at
+   # ../../.agents/skills/magpie-setup and resolve to the
+   # refreshed content automatically — nothing to touch there.
    ```
 
 4. **Reload in-flight.** Immediately after the copy lands —
    before doing anything else in this run — re-read the
-   updated `<adopter-skills-dir>/magpie-setup/SKILL.md`,
+   updated `.agents/skills/magpie-setup/SKILL.md`,
    the just-overwritten `upgrade.md` (this file), and any
    helper file you have already opened in this run
-   (`conventions.md`, `overrides.md`, `verify.md`). Resume
+   (`agents.md`, `overrides.md`, `verify.md`). Resume
    the upgrade from the step *after* this one, executing
    the reloaded content — not the version of this file
    that was in memory when the upgrade started.
@@ -258,6 +242,25 @@ 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.
+Wiring is the **canonical-plus-relay** model
+([`agents.md`](agents.md)), applied identically regardless of any
+pre-existing `.claude/`/`.github/` layout: `.agents/skills/` holds
+the canonical `magpie-<n>` → snapshot links; every other active
+target gets relay `magpie-<n>` → `../../.agents/skills/magpie-<n>`
+links.
+
 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,76 +296,85 @@ 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:
+
+One **uniform** two-line block per active target dir (canonical
+and relays alike), no per-layout variation:
+
+```text
+/.agents/skills/magpie-*
+!/.agents/skills/magpie-setup
+/.claude/skills/magpie-*
+!/.claude/skills/magpie-setup
+/.github/skills/magpie-*
+!/.github/skills/magpie-setup
+```
+
+Add the analogous two lines for any present holdout
+(`.windsurf/skills/`, `.goose/skills/`, …).
 
 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>`:
-   - If the symlink exists and points at the matching
-     snapshot path, leave it alone.
+   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 expected target
+     for that dir (canonical → snapshot; relay →
+     `../../.agents/skills/magpie-<skill>`), leave it alone.
    - If it's missing, create it.
    - If it exists but is broken (target gone, points at the
      wrong path), repair it.
 
    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:
+Per-target 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.
+- **Canonical target (`.agents/skills/`)** — refresh the
+  canonical layer at `.agents/skills/magpie-<n>` →
+  `../../.apache-magpie/skills/<n>/`.
+- **Relay targets (`.claude/skills/`, `.github/skills/`, any
+  present holdout)** — refresh the relay layer at
+  `<target>/skills/magpie-<n>` → `../../.agents/skills/magpie-<n>`.
+  Repair a relay if it is missing, broken, or still points at the
+  old snapshot path directly (a pre-canonical-model layout) rather
+  than at the canonical `.agents/skills/` entry.
 
 ## Step 6b — Sync locally-installed hooks and configuration
 
@@ -404,12 +416,12 @@ case (e.g. a contributor accidentally edited
 ## Step 6c — Propagate to every worktree (run `worktree-init` unconditionally)
 
 The main checkout drives the upgrade, but each worktree
-carries its own gitignored `<adopter-skills-dir>` symlinks.
-Those symlinks need refreshing too — otherwise a developer
-sitting in a worktree sees the new snapshot via the shared
-`<snapshot-dir>` symlink (per
-[`worktree-init.md`](worktree-init.md)) but their
-`<adopter-skills-dir>` may still point at *missing* skills
+carries its own gitignored canonical + relay framework-skill
+symlinks. Those symlinks need refreshing too — otherwise a
+developer sitting in a worktree sees the new snapshot via the
+shared `<snapshot-dir>` symlink (per
+[`worktree-init.md`](worktree-init.md)) but their per-target
+`magpie-*` symlinks may still point at *missing* skills
 (a family the upgrade added) or *renamed* ones (a framework
 rename).
 
@@ -435,8 +447,8 @@ Procedure:
    committed lock the worktree shares via git) plus the
    always-on families per
    [`SKILL.md` Golden rule 8](SKILL.md#golden-rules), and
-   reconciles both the snapshot symlink and the
-   `<adopter-skills-dir>` symlinks (see
+   reconciles both the snapshot symlink and the canonical +
+   relay framework-skill symlinks (see
    [`worktree-init.md` Step 1 + Step 1b](worktree-init.md)).
 3. Collect each invocation's recap into a per-worktree row
    for the upgrade summary's `Worktrees:` section
@@ -492,7 +504,7 @@ committed project-scope file). Idempotent — already-present
 paths are skipped. If
 `~/.claude/scripts/sandbox-add-project-root.sh` is absent,
 surface as ⚠ in the upgrade summary with a pointer at
-`/setup-isolated-setup-install` and continue (do not block
+`/magpie-setup-isolated-setup-install` and continue (do not block
 upgrade — secure-agent setup is independent of framework
 upgrade). The recap row in Step 8's output goes under a new
 `Sandbox allowlist:` section.
@@ -635,15 +647,16 @@ Symlinks (main checkout):
   Always-on families:  setup-*, list-*       (per Golden rule 8)
   ✓ <list of unchanged symlinks>
   + <list of newly-created symlinks (skill present in the
-     effective family set but missing from <adopter-skills-dir>)>
+     effective family set but missing from an active target dir)>
   ↻ <list of repaired symlinks (existed but broken / pointing
      at the wrong path)>
   - <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>
@@ -659,7 +672,7 @@ Worktrees (worktree-init was run on each, idempotently):
 Sandbox allowlist (sandbox-add-project-root.sh --all-worktrees):
   ✓ already covers this project + N worktrees   OR
   + <list of <worktree>/.claude/settings.local.json files updated>   OR
-  ⚠ helper not installed — run /setup-isolated-setup-install
+  ⚠ helper not installed — run /magpie-setup-isolated-setup-install
 
 Overrides:
   ✓ <list of overrides whose target is unchanged>
@@ -673,7 +686,7 @@ Framework templates (projects/_template/):
   → file an issue against apache/airflow-steward to upstream a fix
 
 Recommended follow-ups:
-  - Run /setup-isolated-setup-update if the secure-setup blast
+  - Run /magpie-setup-isolated-setup-update if the secure-setup blast
     radius (settings.json, agent-isolation/, pinned-versions.toml)
     appears in the diff.
   - Open .apache-magpie-overrides/<name>.md for any ⚠ entry above.
diff --git a/.github/skills/magpie-setup/verify.md 
b/.agents/skills/magpie-setup/verify.md
similarity index 88%
rename from .github/skills/magpie-setup/verify.md
rename to .agents/skills/magpie-setup/verify.md
index 8db9c41ec17..1c634d63c10 100644
--- a/.github/skills/magpie-setup/verify.md
+++ b/.agents/skills/magpie-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,28 @@ 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`
-   (idempotent).
+2. **Symlinks resolve (canonical → source, relays → canonical).**
+   In the canonical dir `.agents/skills/`, every `magpie-<n>` is a
+   symlink whose target (`../../skills/<n>/`) resolves to a
+   directory containing `SKILL.md`. In every **relay** target dir
+   ([`agents.md`](agents.md) — `.claude/skills/`, `.github/skills/`,
+   plus any present holdout), every `magpie-<n>` is a symlink whose
+   target (`../../.agents/skills/magpie-<n>`) resolves through the
+   canonical entry to the same `SKILL.md`. ✗ list any dangling or
+   non-symlink entry — or any relay that points straight at the
+   snapshot/source instead of at `.agents/skills/` — 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).
+   canonical `magpie-<n>` symlink in `.agents/skills/` and a
+   matching relay in **every other 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 +160,19 @@ 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 (a **uniform** `magpie-*` glob block per **active
+target dir** — [`agents.md`](agents.md) — with no per-layout
+variation):
+
+- **Canonical target (`.agents/skills/`)** — always present:
+  `/.agents/skills/magpie-*` with `!/.agents/skills/magpie-setup`.
+- **Relay targets (`.claude/skills/`, `.github/skills/`)** — the
+  same two-line block keyed on each dir
+  (`/.claude/skills/magpie-*` with `!/.claude/skills/magpie-setup`,
+  and likewise for `.github/skills/`).
+- **Any present holdout** (`.windsurf/skills/`,
+  `.goose/skills/`, …) — the same two-line block keyed on its own
+  dir.
 
 - ✗ if `/.apache-magpie/` is not gitignored — the snapshot
   is at risk of being accidentally committed.
@@ -180,16 +188,27 @@ Recommended (the family patterns the adopter's
 
 ### 5. Symlinks point at live framework skills
 
-For each symlink under `<adopter-skills-dir>` that resolves
-into `.apache-magpie/skills/<name>/`:
+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 `magpie-*` symlink under any active target dir —
+canonical ones resolving (via `.agents/skills/`) into
+`.apache-magpie/skills/<name>/`, relays resolving through
+`../../.agents/skills/magpie-<name>` to the same:
 
-- ✓ 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 it resolves to a live skill.
+- ✗ if dangling (target deleted or snapshot missing), or a relay
+  pointing straight at the snapshot instead of at the canonical
+  `.agents/skills/` entry, 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 +227,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
 
@@ -226,8 +245,8 @@ with the `README.md` scaffold from
 
 ### 7. The `setup` skill itself is up to date
 
-Compare the adopter-side committed `setup` skill
-(at `<adopter-skills-dir>/magpie-setup/`) against the
+Compare the canonical committed `setup` skill
+(at `.agents/skills/magpie-setup/`) against the
 snapshot's `.apache-magpie/skills/setup/`.
 
 - ✓ if same content.
@@ -311,13 +330,13 @@ For the current worktree (resolved via
   pass.
 - ⚠ if missing from either array **and** the helper script is
   absent — the operator has not run
-  `/setup-isolated-setup-install` yet. Suggest that skill.
+  `/magpie-setup-isolated-setup-install` yet. Suggest that skill.
   Not ✗ because secure-agent isolation is independent of
   framework adoption, and an adopter who runs without the
   sandbox enabled has nothing to lose by the missing entry.
 - ⚠ if `<worktree>/.claude/settings.local.json` is absent
   entirely — same remediation (re-run the helper or
-  `/setup-isolated-setup-install`). The file is auto-created
+  `/magpie-setup-isolated-setup-install`). The file is auto-created
   by the helper on first run.
 - ✗ if `<worktree>/.claude/settings.local.json` exists AND
   is **not** gitignored (cross-check via `git check-ignore`).
@@ -550,7 +569,7 @@ which holds a POSIX `fcntl.flock` advisory exclusive lock on
 the target file, re-parses under the lock, mutates
 `.permissions.allow[]` in place, writes to a sibling temp
 file, and `os.replace`s into place — so concurrent
-`/setup-isolated-setup-install` (which also writes to the same
+`/magpie-setup-isolated-setup-install` (which also writes to the same
 file's `sandbox.filesystem.*` arrays) does not silently
 clobber the diff. When the target file lives at a path the
 agent's sandbox marks as `denyWithinAllow` (the per-machine
@@ -559,7 +578,7 @@ operator to authorise the sandbox bypass for that single 
write
 — it does not silently skip the file. ⚠ if either file is
 absent (most adopters will have at least
 `settings.local.json` after the first
-`/setup-isolated-setup-install` pass; absence is a soft signal
+`/magpie-setup-isolated-setup-install` pass; absence is a soft signal
 not a hard fault).
 
 **Why we propose, never auto-apply.** The allow-list is
diff --git a/.github/skills/magpie-setup/worktree-init.md 
b/.agents/skills/magpie-setup/worktree-init.md
similarity index 74%
rename from .github/skills/magpie-setup/worktree-init.md
rename to .agents/skills/magpie-setup/worktree-init.md
index c4a2240a7a8..07fcf28bb9c 100644
--- a/.github/skills/magpie-setup/worktree-init.md
+++ b/.agents/skills/magpie-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,44 +94,40 @@ 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:
-
-- If `<worktree>/<adopter-skills-dir>/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:
-
-- **Pattern A (flat)** — one layer at
-  `.claude/skills/magpie-<n>`.
-- **Pattern B (double-symlinked)** — two layers (inner at
-  `.github/skills/magpie-<n>`, outer at `.claude/skills/magpie-<n>` →
-  inner). Both gitignored.
-- **Pattern D (single directory symlink)** — one layer at
-  the canonical side (D.1: `.github/skills/magpie-<n>`;
-  D.2: `.claude/skills/magpie-<n>`). The symlinked side resolves
-  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
+Wiring follows the **canonical-plus-relay** model
+([`agents.md`](agents.md)), with no per-layout variation. For each
+framework skill in the effective family set:
+
+- **Canonical (`.agents/skills/`)** — ensure
+  `<worktree>/.agents/skills/magpie-<skill>` →
+  `../../.apache-magpie/skills/<skill>/` (the worktree's snapshot
+  symlink from Step 1). Create if missing, repair if broken or
+  pointing at the wrong path, leave alone if correct.
+- **Relays (`.claude/skills/`, `.github/skills/`, any present
+  holdout)** — ensure `<worktree>/<target>/skills/magpie-<skill>`
+  → `../../.agents/skills/magpie-<skill>`. Create / repair / leave
+  alone the same way.
+
+All these entries are gitignored and per-worktree.
+
+The worktree's target directories themselves — `.agents/skills/`,
+`.claude/skills/`, `.github/skills/`, any holdout — 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
 
@@ -169,7 +168,7 @@ one-line recap row for the Step 2 summary:
 
 - ✓ already covered, OR
 - + added `<worktree-path>`, OR
-- ⚠ helper not installed — `/setup-isolated-setup-install` to wire it up.
+- ⚠ helper not installed — `/magpie-setup-isolated-setup-install` to wire it 
up.
 
 `worktree-init` does **not** fail when the helper is absent;
 secure-agent isolation is independent of framework adoption.
@@ -182,9 +181,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 +218,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. |
diff --git a/.github/skills/prepare-providers-documentation/SKILL.md 
b/.agents/skills/prepare-providers-documentation/SKILL.md
similarity index 100%
rename from .github/skills/prepare-providers-documentation/SKILL.md
rename to .agents/skills/prepare-providers-documentation/SKILL.md
diff --git a/.claude/skills/aip-user-stories b/.claude/skills/aip-user-stories
index 801b62bc706..06986229dc1 120000
--- a/.claude/skills/aip-user-stories
+++ b/.claude/skills/aip-user-stories
@@ -1 +1 @@
-../../.github/skills/aip-user-stories
\ No newline at end of file
+../../.agents/skills/aip-user-stories
\ No newline at end of file
diff --git a/.claude/skills/airflow-translations 
b/.claude/skills/airflow-translations
new file mode 120000
index 00000000000..774fda334f5
--- /dev/null
+++ b/.claude/skills/airflow-translations
@@ -0,0 +1 @@
+../../.agents/skills/airflow-translations
\ No newline at end of file
diff --git a/.claude/skills/magpie-setup b/.claude/skills/magpie-setup
index d7a9cac8b0f..625c6aa3d47 120000
--- a/.claude/skills/magpie-setup
+++ b/.claude/skills/magpie-setup
@@ -1 +1 @@
-../../.github/skills/magpie-setup
\ No newline at end of file
+../../.agents/skills/magpie-setup
\ No newline at end of file
diff --git a/.claude/skills/prepare-providers-documentation 
b/.claude/skills/prepare-providers-documentation
new file mode 120000
index 00000000000..1eadc05fae1
--- /dev/null
+++ b/.claude/skills/prepare-providers-documentation
@@ -0,0 +1 @@
+../../.agents/skills/prepare-providers-documentation
\ No newline at end of file
diff --git a/.github/skills/aip-user-stories b/.github/skills/aip-user-stories
new file mode 120000
index 00000000000..06986229dc1
--- /dev/null
+++ b/.github/skills/aip-user-stories
@@ -0,0 +1 @@
+../../.agents/skills/aip-user-stories
\ No newline at end of file
diff --git a/.github/skills/airflow-translations 
b/.github/skills/airflow-translations
new file mode 120000
index 00000000000..774fda334f5
--- /dev/null
+++ b/.github/skills/airflow-translations
@@ -0,0 +1 @@
+../../.agents/skills/airflow-translations
\ No newline at end of file
diff --git a/.github/skills/magpie-setup b/.github/skills/magpie-setup
new file mode 120000
index 00000000000..625c6aa3d47
--- /dev/null
+++ b/.github/skills/magpie-setup
@@ -0,0 +1 @@
+../../.agents/skills/magpie-setup
\ No newline at end of file
diff --git a/.github/skills/magpie-setup/conventions.md 
b/.github/skills/magpie-setup/conventions.md
deleted file mode 100644
index 896e397c93e..00000000000
--- a/.github/skills/magpie-setup/conventions.md
+++ /dev/null
@@ -1,278 +0,0 @@
-<!-- SPDX-License-Identifier: Apache-2.0
-     https://www.apache.org/legal/release-policy.html -->
-
-# conventions — auto-detect the adopter's skills-dir layout
-
-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
-matches it. The framework's symlinks land at the same depth
-as the adopter's existing skills, not one level off.
-
-## Patterns
-
-### A. Flat — skills live directly in `.claude/skills/`
-
-```text
-<repo-root>/
-└── .claude/
-    └── skills/
-        └── <skill-name>/
-            └── SKILL.md
-```
-
-The simple, default Claude Code layout. Most repos that just
-started using Claude Code use this. **Detection signal**:
-`.claude/skills/<n>/SKILL.md` is a regular file.
-
-For framework symlinks: create them at
-`<repo-root>/.claude/skills/magpie-<n>` → relative path into
-`.apache-magpie/skills/<n>/` (the symlink carries the
-`magpie-` prefix; its target keeps the snapshot's clean
-source name).
-
-**Caveat — `.claude/` already gitignored.** Some adopters (notably
-those that previously used Claude Code with per-user `.claude/`
-settings) have `.claude/` listed in their repo's `.gitignore`.
-This prevents `.claude/skills/magpie-setup/` from being committed
-per [`SKILL.md` Golden rule 6](SKILL.md#golden-rules) (which expects
-`setup` itself to be the only committed framework skill).
-
-Three resolution paths:
-
-- **Override the gitignore** — add `!/.claude/skills/magpie-setup/`
-  after the broader `.claude/` line. Keeps the rest of `.claude/`
-  gitignored; commits only the framework's bootstrap skill.
-- **Switch to Pattern B** — move skills to `.github/skills/` and use
-  the double-symlinked layout. Sidesteps the `.claude/` gitignore
-  entirely. See *B. Double-symlinked* below.
-- **Defer commit** — treat the whole adoption as a no-commit local
-  experiment until the gitignore is revised. Useful for spike-style
-  evaluation.
-
-The adopt flow surfaces the conflict when it detects `.claude/` is
-gitignored AND Pattern A was selected; the user picks one of the
-three above.
-
-### B. Double-symlinked — `.claude/skills/` mirrors `.github/skills/`
-
-```text
-<repo-root>/
-├── .claude/
-│   └── skills/
-│       └── <skill-name>  →  ../../.github/skills/<skill-name>/
-└── .github/
-    └── skills/
-        └── <skill-name>/
-            └── SKILL.md
-```
-
-The pattern apache/airflow uses today (e.g. apache/airflow has this): actual 
skill content
-lives under `.github/skills/`; `.claude/skills/<n>` is a
-relative symlink pointing into `.github/skills/<n>/`. The
-rationale (per the airflow team): `.github/` is the canonical
-infra-glue directory in apache/airflow (e.g. that project), and `.claude/` is a
-view of those skills filtered for Claude Code.
-
-**Detection signal**: at least one entry in `.claude/skills/`
-is a symlink resolving into `.github/skills/`.
-
-For framework symlinks: create *both* layers — the inner
-`.github/skills/magpie-<n>` → relative path into
-`.apache-magpie/skills/<n>/`, and the outer
-`.claude/skills/magpie-<n>` → `../../.github/skills/magpie-<n>/`
-(matching the existing pattern). Both layers gitignored.
-
-### C. None yet — neither directory exists
-
-A new adopter that has never used Claude Code skills before.
-The skill creates the directory layout the adopter prefers
-(default: pattern A, flat — simpler). If the user has a
-preference, they say so during the adopt flow.
-
-### D. Single directory symlink — one of `.claude/skills` / `.github/skills` 
is a symlink to the other
-
-```text
-# D.1 — content under .github/skills/, .claude/skills is the symlink:
-<repo-root>/
-├── .claude/
-│   └── skills  →  ../.github/skills/
-└── .github/
-    └── skills/
-        ├── <native-skill>/
-        │   └── SKILL.md
-        ├── <framework-symlink>  →  
../../.apache-magpie/skills/<framework-skill>/
-        └── ...
-```
-
-```text
-# D.2 — content under .claude/skills/, .github/skills is the symlink:
-<repo-root>/
-├── .claude/
-│   └── skills/
-│       ├── <native-skill>/
-│       │   └── SKILL.md
-│       ├── <framework-symlink>  →  
../../.apache-magpie/skills/<framework-skill>/
-│       └── ...
-└── .github/
-    └── skills  →  ../.claude/skills/
-```
-
-A simplification of Pattern B: instead of one per-skill
-symlink mirroring every entry from one directory to the
-other, **one of the two directories is itself a symlink to
-the other**. Both `.claude/skills/<n>` and
-`.github/skills/<n>` always resolve to the same content for
-every skill — the project's native skills and the framework's
-gitignored symlinks alike — without any per-skill plumbing.
-Adding a new skill (project-native or framework) just means
-adding it once in the canonical directory; the mirror is
-automatic.
-
-**Two orientations** — same shape, opposite direction:
-
-- **D.1** — content lives under `.github/skills/`,
-  `.claude/skills` is the symlink. The natural choice for
-  projects whose canonical skills directory is `.github/`
-  (e.g. apache/airflow, which uses `.github/` as its
-  infra-glue root and `.claude/` as a Claude-Code-facing
-  view).
-- **D.2** — content lives under `.claude/skills/`,
-  `.github/skills` is the symlink. The natural choice for
-  projects whose canonical skills directory is `.claude/`
-  (e.g. a Pattern A project that wants `.github/skills/`
-  available too without duplicating content).
-
-**Detection signal**: exactly one of `.claude/skills` /
-`.github/skills` is a symlink (test with `[ -L <path> ]` /
-`readlink <path>`) and resolves to the other path in the same
-repo. Either orientation counts as Pattern D.
-
-For framework symlinks: create them at **only one layer** —
-the *real* directory side, never the symlinked side. With
-D.1 that means `.github/skills/magpie-<n>` → relative path into
-`.apache-magpie/skills/<n>/`; with D.2 it means
-`.claude/skills/magpie-<n>` → the same. The opposite path is
-automatically the same content via the directory symlink.
-
-Gitignore consequences: only entries on the real-directory
-side are needed (e.g. `/.github/skills/magpie-*` for D.1,
-or `/.claude/skills/magpie-*` for D.2). Git treats the
-symlinked side as a single tracked symlink and does not
-descend into it, so ignore entries on that side would match
-no actual tracked path and are unnecessary.
-
-The directory symlink itself is **adopter-owned** — created
-deliberately by the adopter as part of the project's layout
-choice, and not touched by `/magpie-setup unadopt`. The
-framework treats it the same way it treats the real-directory
-side: as part of the surrounding repo layout.
-
-**Pre-Pattern-D consolidation** — if both `.claude/skills/`
-and `.github/skills/` exist as **regular directories** (not
-yet symlinked to each other) and contain skill content that
-is not already aliased through symlinks, the adopt flow
-**does not silently apply Pattern D**. Each directory's
-contents are an independent set; turning one into a symlink
-to the other would clobber the symlinked side's content. The
-flow surfaces the conflict and offers a consolidation prompt:
-
-1. List the skills present in each directory (real
-   directories, regular files, and any non-Pattern-B
-   symlinks).
-2. Flag name collisions where the same skill name exists in
-   both directories with different content.
-3. Ask the user to pick D.1 or D.2 and confirm the
-   consolidation steps:
-   - Move every skill from the side that will become the
-     symlink into the side that will become the real
-     directory, resolving any flagged name collisions first.
-   - Replace the now-empty side with a relative symlink to
-     the other side.
-4. Only after the consolidation is complete does the adopt
-   flow proceed to wire framework symlinks at the chosen
-   real-directory side.
-
-If the consolidation cannot proceed (unresolved name
-collisions the user has not addressed), the adopt flow stops
-and lets the user resolve in their own commit before
-re-invoking — the framework never auto-renames adopter-owned
-content.
-
-## Detection algorithm
-
-```text
-# Pattern D first — either orientation:
-if .claude/skills is a symlink:
-    if it resolves to .github/skills/ in the same repo:
-        pattern = D.1 (single directory symlink; canonical = .github/skills/)
-    else:
-        # operator pointed `.claude/skills` somewhere else
-        # deliberately; surface, do not guess.
-        pattern = ambiguous → prompt the user
-elif .github/skills is a symlink:
-    if it resolves to .claude/skills/ in the same repo:
-        pattern = D.2 (single directory symlink; canonical = .claude/skills/)
-    else:
-        # same — surface the unexpected target, do not guess.
-        pattern = ambiguous → prompt the user
-
-# Otherwise fall through to A / B / C:
-elif .claude/skills/ exists (regular directory):
-    if any entry in .claude/skills/ is a symlink resolving
-    into .github/skills/:
-        pattern = B (double-symlinked)
-    else:
-        if .github/skills/ also exists as a regular directory
-        with independent content:
-            pattern = ambiguous → propose Pattern D
-                                   consolidation (see *Pre-Pattern-D
-                                   consolidation* under section D
-                                   above), with A as the fallback
-                                   if the user declines
-        else:
-            pattern = A (flat)
-elif .github/skills/ exists:
-    pattern = B (the user has a `.github/skills/` half but
-                  hasn't wired up `.claude/` yet — the adopt
-                  flow will create the .claude/ side as part
-                  of installing framework skills)
-else:
-    pattern = C (none yet — default to A unless user picks
-                  otherwise)
-```
-
-## What the adopt flow does per pattern
-
-| Pattern | `<adopter-skills-dir>` (where framework symlinks land) | Side 
effect |
-|---|---|---|
-| A — flat | `.claude/skills/` | None |
-| B — double-symlinked | `.github/skills/` (the inner layer); 
`.claude/skills/` symlinks to it | If `.github/skills/<n>` for a framework 
skill already exists as a real directory (an old in-repo copy), refuse and let 
the user resolve |
-| C — none yet | `.claude/skills/` | Create the directory |
-| D.1 — single directory symlink, canonical `.github/skills/` | 
`.github/skills/` (the only layer; `.claude/skills` resolves into it via the 
directory symlink) | None — no outer-layer plumbing to create |
-| D.2 — single directory symlink, canonical `.claude/skills/` | 
`.claude/skills/` (the only layer; `.github/skills` resolves into it via the 
directory symlink) | None — no outer-layer plumbing to create |
-
-## Ambiguous cases
-
-- **The repo has both `.claude/skills/` and `.github/skills/`
-  but neither contains symlinks linking the two**. This is a
-  half-migrated state. The adopt flow surfaces it and asks
-  the user which pattern they want; it does not guess.
-- **Pattern B but with absolute symlinks** (rather than
-  relative). The adopt flow will use *relative* symlinks for
-  consistency. If the user wants absolute, they say so;
-  otherwise relative is the default — it survives a repo
-  move.
-- **`.claude/skills` (or `.github/skills`) is a symlink but
-  resolves outside the repo or to a path other than the
-  expected counterpart directory**. The operator pointed it
-  somewhere deliberately (e.g. a sibling worktree). The
-  adopt flow surfaces the resolved target and asks the user;
-  it does not match Pattern D automatically.
-- **Both `.claude/skills/` and `.github/skills/` exist as
-  regular directories with independent (non-aliased)
-  content**. Surfaced as a Pattern D consolidation
-  opportunity per the **Pre-Pattern-D consolidation** flow
-  under section D above. The user picks D.1 or D.2 (or
-  declines, in which case the flow falls back to Pattern A
-  treating `.claude/skills/` as canonical).
diff --git a/.github/skills/prepare-providers-documentation 
b/.github/skills/prepare-providers-documentation
new file mode 120000
index 00000000000..1eadc05fae1
--- /dev/null
+++ b/.github/skills/prepare-providers-documentation
@@ -0,0 +1 @@
+../../.agents/skills/prepare-providers-documentation
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 250beef1e1e..a0ccf9fd3de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -132,11 +132,13 @@ ENV/
 *.iml
 
 # Claude Code — ignore local config/caches/worktrees, but keep the
-# tracked symlinks in .claude/skills/ that point at .github/skills/.
+# tracked symlinks in .claude/skills/ that point at .agents/skills/.
 .claude/*
 !.claude/skills/
 .claude/skills/*
 !.claude/skills/aip-user-stories
+!.claude/skills/airflow-translations
+!.claude/skills/prepare-providers-documentation
 !.claude/skills/magpie-setup
 
 # Kiro
@@ -332,6 +334,8 @@ dev/registry/providers.json
 # (every framework skill is namespaced under the magpie- prefix).
 # The committed magpie-setup bootstrap is kept tracked by the
 # negation lines (the magpie-* glob would otherwise ignore it).
+/.agents/skills/magpie-*
+!/.agents/skills/magpie-setup
 /.claude/skills/magpie-*
 !/.claude/skills/magpie-setup
 /.github/skills/magpie-*
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b5d0226dd41..f115bf85b50 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -168,6 +168,7 @@ repos:
           (?x)
           ^\.github/.*\.md$|
           ^\.claude/|
+          ^\.agents/|
           ^(?:.*/)?AGENTS\.md$|
           ^(?:.*/)?CLAUDE\.md$|
           ^(?:.*/)?SKILL\.md$|
@@ -186,13 +187,14 @@ repos:
           (?x)
           ^\.github/.*\.md$|
           ^\.claude/|
+          ^\.agents/|
           ^(?:.*/)?AGENTS\.md$|
           ^(?:.*/)?CLAUDE\.md$|
           ^(?:.*/)?SKILL\.md$
         exclude:
           (?x)
           ^scripts/ci/license-templates/|
-          ^\.github/skills/magpie-setup/
+          ^\.agents/skills/magpie-setup/
       - id: insert-license
         name: Add license for all other files
         args:
@@ -299,7 +301,7 @@ repos:
          - 'black==26.1.0'
         exclude: >
           (?x)
-          ^\.github/skills/aip-user-stories
+          ^\.agents/skills/aip-user-stories
   - repo: https://github.com/pre-commit/pre-commit-hooks
     rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c  # frozen: v6.0.0
     hooks:
@@ -396,8 +398,8 @@ repos:
           ^.*pnpm-lock\.yaml$|
           .*/dist/.*|
           ^airflow-core/src/airflow/ui/public/i18n/locales/(?!en/).+/|
-          ^\.github/skills/airflow-translations/|
-          ^\.github/skills/magpie-setup/|
+          ^\.agents/skills/airflow-translations/|
+          ^\.agents/skills/magpie-setup/|
           ^scripts/docker/keys/.*\.asc$
         args:
           - --ignore-words=docs/spelling_wordlist.txt
@@ -442,7 +444,7 @@ repos:
           ^\.build/|
           ^generated/|
           ^\.apache-magpie-overrides/|
-          ^\.github/skills/magpie-setup/
+          ^\.agents/skills/magpie-setup/
   - repo: local
     # Note that this is the 2nd "local" repo group in the 
.pre-commit-config.yaml file. This is because
     # we try to minimize the number of passes that must happen to apply some 
of the changes
@@ -680,6 +682,8 @@ repos:
           
^providers/common/ai/src/airflow/providers/common/ai/plugins/www/pnpm-lock\.yaml$|
           ^airflow-core/src/airflow/ui/public/i18n/locales/de/README\.md$|
           ^airflow-core/src/airflow/ui/src/i18n/config\.ts$|
+          ^\.agents/skills/airflow-translations/|
+          ^\.agents/skills/magpie-setup/|
           ^airflow-core/src/airflow/utils/db\.py$|
           ^airflow-core/src/airflow/utils/trigger_rule\.py$|
           ^airflow-core/tests/|
@@ -887,7 +891,7 @@ repos:
         files: >
           (?x)
           ^airflow-core/src/airflow/ui/public/i18n/locales/en/.*\.json$|
-          ^\.github/skills/airflow-translations/SKILL\.md$
+          ^\.agents/skills/airflow-translations/SKILL\.md$
         pass_filenames: false
       - id: update-pyproject-toml
         name: Update Airflow's meta-package pyproject.toml
@@ -959,7 +963,7 @@ repos:
         exclude: |
           (?x)
           ^\.apache-magpie-overrides/|
-          ^\.github/skills/magpie-setup/
+          ^\.agents/skills/magpie-setup/
         additional_dependencies: ['[email protected]']
       - id: lint-json-schema
         name: Lint JSON Schema files
diff --git a/dev/README_RELEASE_PROVIDERS.md b/dev/README_RELEASE_PROVIDERS.md
index 9b222ed7a0f..efe187921bb 100644
--- a/dev/README_RELEASE_PROVIDERS.md
+++ b/dev/README_RELEASE_PROVIDERS.md
@@ -220,14 +220,14 @@ options:
 Other MCP-compatible agentic clients should work as long as the GitHub MCP 
server is wired up and
 the framework loads `SKILL.md` files from the `.claude/skills/` discovery path.
 
-The skill source of truth lives in 
[`.github/skills/prepare-providers-documentation/SKILL.md`](../.github/skills/prepare-providers-documentation/SKILL.md).
+The skill source of truth lives in 
[`.agents/skills/prepare-providers-documentation/SKILL.md`](../.agents/skills/prepare-providers-documentation/SKILL.md).
 Both Claude Code and OpenAI Codex CLI discover project-local skills via a 
symlink at
 `.claude/skills/prepare-providers-documentation`. If your local checkout 
doesn't have that symlink
 (the `.claude/` directory is gitignored), set it up once:
 
 ```shell script
 mkdir -p .claude/skills
-ln -s ../../.github/skills/prepare-providers-documentation 
.claude/skills/prepare-providers-documentation
+ln -s ../../.agents/skills/prepare-providers-documentation 
.claude/skills/prepare-providers-documentation
 ```
 
 Before invoking the skill, set the environment variable ``RELEASE_DATE`` to 
the date of the release,
diff --git a/scripts/ci/prek/sync_translation_namespaces.py 
b/scripts/ci/prek/sync_translation_namespaces.py
index 1dd9df6eb61..cfc309f71ad 100755
--- a/scripts/ci/prek/sync_translation_namespaces.py
+++ b/scripts/ci/prek/sync_translation_namespaces.py
@@ -26,7 +26,7 @@ from common_prek_utils import AIRFLOW_ROOT_PATH, 
insert_documentation
 EN_LOCALE_DIR = (
     AIRFLOW_ROOT_PATH / "airflow-core" / "src" / "airflow" / "ui" / "public" / 
"i18n" / "locales" / "en"
 )
-SKILL_FILE = AIRFLOW_ROOT_PATH / ".github" / "skills" / "airflow-translations" 
/ "SKILL.md"
+SKILL_FILE = AIRFLOW_ROOT_PATH / ".agents" / "skills" / "airflow-translations" 
/ "SKILL.md"
 
 START_MARKER = "<!-- START namespace-files, please keep comment here to allow 
auto update -->"
 END_MARKER = "<!-- END namespace-files, please keep comment here to allow auto 
update -->"

Reply via email to