diff --git a/.agents/skills/magpie-setup/SKILL.md b/.agents/skills/magpie-setup/SKILL.md index cfa5a435b8f4c..4458c03a85f13 100644 --- a/.agents/skills/magpie-setup/SKILL.md +++ b/.agents/skills/magpie-setup/SKILL.md @@ -1,7 +1,7 @@ --- name: magpie-setup description: | - Adopt and maintain the apache-steward framework in a project + Adopt and maintain the apache-magpie framework in a project repo via the snapshot-based adoption mechanism. The only framework skill committed in an adopter's repo; every other skill is a symlink the adopt sub-action wires up. @@ -13,14 +13,14 @@ description: | `/magpie-setup override ` - open or scaffold an agentic override in `.apache-magpie-overrides/` `/magpie-setup unadopt` - reverse the adoption (snapshot, locks, symlinks, hook, doc sections); preserves `.apache-magpie-overrides/` by default (main-checkout only) when_to_use: | - Invoke when the user says "adopt apache-steward", "adopt - apache/airflow-steward", "set up steward in this repo", + Invoke when the user says "adopt apache-magpie", "adopt + apache/magpie", "set up magpie in this repo", "follow .claude/skills/magpie-setup", or follows the framework's README adoption instructions. Also for periodic - maintenance: "upgrade steward", "verify steward setup", - "check steward drift", "the snapshot is stale". + maintenance: "upgrade magpie", "verify magpie setup", + "check magpie drift", "the snapshot is stale". argument-hint: "[adopt|upgrade|worktree-init|verify|override skill-name|unadopt]" -capability: capability:setup +capability: capability:platform license: Apache-2.0 --- @@ -33,14 +33,14 @@ license: Apache-2.0 → `.apache-magpie.lock` (committed — project's pin) → `.apache-magpie.local.lock` (gitignored — per-machine record) → adopter's public source repo (the repo this skill is being run in) - → the apache-steward source we download a snapshot from + → the apache-magpie source we download a snapshot from — one of: signed zip from ASF dist, git tag, git branch. See [`docs/setup/install-recipes.md`](../../docs/setup/install-recipes.md). --> # setup This skill is **the only framework artefact an adopter -project commits**. Every other apache-steward skill (security, +project commits**. Every other apache-magpie skill (security, pr-management, issue) is a gitignored symlink into the gitignored snapshot at `` that this skill manages. @@ -387,7 +387,7 @@ first, then continue. machines. That is [`setup-shared-config-sync`](../setup-shared-config-sync/SKILL.md). - Not for committing framework changes. Framework PRs go - against `apache/airflow-steward` directly — the snapshot is + against `apache/magpie` directly — the snapshot is read-only. ## Failure modes @@ -398,4 +398,4 @@ first, then continue. | Snapshot present but symlinks dangle | Adopter ran `git clone` but not `/magpie-setup` after — symlinks are gitignored but persist in their target's absence on disk | `/magpie-setup verify --auto-fix-symlinks` (or `/magpie-setup adopt`, idempotent) | | Worktree off the adopter repo can't find framework skills | Worktrees off the adopter don't auto-inherit the gitignored snapshot | The `adopt` sub-action installs a `post-checkout` git hook that re-runs the snapshot install on worktree creation; verify the hook is present (`/magpie-setup verify`) | | `git clone` of an upstream PR sees no framework skills | Expected — the snapshot is gitignored, so a fresh clone has no ``. The clone needs `/magpie-setup` once before any framework skill is invocable | `/magpie-setup` | -| Project decided to stop using apache-steward | The reverse of adoption — remove the snapshot, locks, symlinks, hook, doc sections, and the `setup` skill itself. `.apache-magpie-overrides/` is preserved by default | `/magpie-setup unadopt` (add `--purge-overrides` to also drop the overrides directory) | +| Project decided to stop using apache-magpie | The reverse of adoption — remove the snapshot, locks, symlinks, hook, doc sections, and the `setup` skill itself. `.apache-magpie-overrides/` is preserved by default | `/magpie-setup unadopt` (add `--purge-overrides` to also drop the overrides directory) | diff --git a/.agents/skills/magpie-setup/adopt.md b/.agents/skills/magpie-setup/adopt.md index f5d19402217bd..e5f140973223f 100644 --- a/.agents/skills/magpie-setup/adopt.md +++ b/.agents/skills/magpie-setup/adopt.md @@ -1,9 +1,9 @@ -# adopt — first-time install of apache-steward into an adopter repo +# adopt — first-time install of apache-magpie into an adopter repo -The default sub-action when the user says "adopt apache-steward". +The default sub-action when the user says "adopt apache-magpie". There are two adoption shapes the skill recognises and routes between automatically: @@ -72,7 +72,7 @@ between automatically: Detect it **structurally** — do *not* rely on the `origin` URL, which on a contributor's fork points at - `/airflow-steward`, not `apache/`. The repo is the + `/magpie`, not `apache/`. The repo is the framework checkout when `skills/setup/SKILL.md` exists at the repo root with `name: magpie-setup` in its frontmatter **and** `skills/list-skills/` is present. @@ -317,7 +317,7 @@ logic for a *new* framework version. 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 - PR to `apache/airflow-steward` first, or (c) defers the + PR to `apache/magpie` first, or (c) defers the bootstrap-skill refresh — in (c) the rest of this run continues against the in-flight (older) version with a warning. @@ -463,7 +463,7 @@ structured-question tool, use a *multi-select* prompt for the three opt-in families (`security`, `pr-management`, `issue`) — the families are not mutually exclusive. Pre-select the **union** of (a) families the user named in -their initial "adopt" request (e.g. *"adopt apache-steward +their initial "adopt" request (e.g. *"adopt apache-magpie for PR triage"* → `pr-management`) and (b) `` from Step 4b. Mention in the prompt body why each family is pre-ticked (named by the @@ -503,6 +503,8 @@ idempotent — re-add them if they're missing. /.apache-magpie/ /.apache-magpie.local.lock /.claude/settings.local.json +/.claude/hooks/agent-guard.py +/.claude/hooks/guards.d/ __pycache__/ *.pyc ``` @@ -514,6 +516,24 @@ scripts emit when run from the adopter checkout (e.g. out of the tree. Most adopters already carry these from a stock Python `.gitignore`; the adopt flow adds them if missing. +The `/.claude/hooks/agent-guard.py` and `/.claude/hooks/guards.d/` +lines keep the deterministic `PreToolUse` guard **gitignored** — +it is framework code synced from the snapshot +([Step 12 pass 1](#step-12--post-install-sync--worktree-propagation--sandbox-allowlist--sanity-check)), +not an adopter artefact, so it is regenerated on `/magpie-setup` +rather than committed (the same rule that keeps the snapshot and +the framework-skill symlinks out of git — the only committed +framework artefact is `magpie-setup`). The committed +`.claude/settings.json` still references it; the **wiring** is +committed, the **script** is not. Because the script is gitignored, +**no** worktree inherits it via git — every worktree is seeded from +the main checkout by the post-checkout hook +([Step 10](#step-10--worktree-aware-post-checkout-hook-fresh-only)) +or by [`worktree-init` Step 1d](worktree-init.md#step-1d--seed-the-worktrees-agent-guard-pretooluse-hook). +An adopter who keeps their own non-framework guards under +`.claude/hooks/guards.d/` and wants them tracked should commit them +with `git add -f` (the directory is ignored by default). + **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 @@ -629,22 +649,22 @@ Create `/.apache-magpie-overrides/` (directory) with a small `README.md` inside: ```markdown -# apache-steward overrides +# apache-magpie overrides Agent-readable instructions that override specific steps or -behaviours of apache-steward framework skills, scoped to +behaviours of apache-magpie framework skills, scoped to this adopter repo. Each override file is named after the framework skill it modifies (e.g. `pr-management-triage.md` overrides the `pr-management-triage` skill). The framework skills consult this directory at run-time before executing default behaviour. See -[`docs/setup/agentic-overrides.md`](https://github.com/apache/airflow-steward/blob/main/docs/setup/agentic-overrides.md) +[`docs/setup/agentic-overrides.md`](https://github.com/apache/magpie/blob/main/docs/setup/agentic-overrides.md) in the framework for the full contract. **Hard rule**: never modify the snapshot under `/.apache-magpie/`. Local mods go here. -Framework changes go via PR to `apache/airflow-steward`. +Framework changes go via PR to `apache/magpie`. ``` This directory is **committed** (overrides ship with the @@ -676,7 +696,7 @@ configuration* → *`user.md` resolution order*](../../AGENTS.md#usermd-resoluti Use this project-agnostic template: ```markdown -# Per-user configuration for apache-steward +# Per-user configuration for apache-magpie This file is committed in the adopter repo and holds preferences that vary per developer (GitHub handle, local clone paths, optional @@ -686,10 +706,11 @@ setup; the skills skip any block that is missing or marked `TODO`. ## `role_flags` -- `pmc_member: TODO` — set to `true` if you are a PMC member of the - adopting project. Used by `security-cve-allocate` to decide whether - you can submit the CVE allocation form directly or need to relay - the request to a PMC member. +- `governance_member: TODO` — set to `true` if you are a member of the + adopting project's governing body (a PMC member at the ASF; whatever + the project's `governance.cve_allocation_gate` names elsewhere). Used + by `security-cve-allocate` to decide whether you can submit the CVE + allocation form directly or need to relay the request to a member. ## `environment` @@ -781,11 +802,11 @@ When the agent harness offers a structured-question tool, ask the remaining unknowns in **one batch** rather than serially. The canonical batch is: -1. **`role_flags.pmc_member`** — *single-select, default `No`*. - "Are you a PMC member of ``?" Used by - `security-cve-allocate` to decide whether the user can submit - the CVE allocation form directly or needs to relay through a - PMC member. +1. **`role_flags.governance_member`** — *single-select, default `No`*. + "Are you a member of ``'s governing body (e.g. a PMC + member at the ASF)?" Used by `security-cve-allocate` to decide + whether the user can submit the CVE allocation form directly or + needs to relay through a member. 2. **Auto-detected env paths confirmation** — *single-select, default "Use as detected"*. Only ask this if both `upstream_clone` and `upstream_fork_remote` were auto-detected @@ -901,43 +922,91 @@ are handled — both are read-only and scoped. ## Step 10 — Worktree-aware post-checkout hook (FRESH only) -Install `/.git/hooks/post-checkout` that chains into -the sandbox-allowlist helper installed by -`setup-isolated-setup-install`, so the new worktree's working -directory is added to the worktree's own -`.claude/settings.local.json`'s `sandbox.filesystem.allowRead` / -`allowWrite` (defensive against -[issue #197](https://github.com/apache/airflow-steward/issues/197) -— see -[`setup-isolated-setup-install/SKILL.md` → Step P](../setup-isolated-setup-install/SKILL.md#step-p--project-root-coverage-in-the-sandbox-allowlists)). +Install `/.git/hooks/post-checkout` — a best-effort +per-worktree reconciler that fires on `git checkout` and on +`git worktree add`. It carries **two** responsibilities, each +guarded independently so neither can gate the git operation: + +1. **Sandbox allowlist.** Chain into the sandbox-allowlist helper + installed by `setup-isolated-setup-install`, so the new + worktree's working directory is added to the worktree's own + `.claude/settings.local.json`'s `sandbox.filesystem.allowRead` / + `allowWrite` (defensive against + [issue #197](https://github.com/apache/magpie/issues/197) + — see + [`setup-isolated-setup-install/SKILL.md` → Step P](../setup-isolated-setup-install/SKILL.md#step-p--project-root-coverage-in-the-sandbox-allowlists)). + +2. **agent-guard seeding.** The committed `.claude/settings.json` + wires the deterministic `PreToolUse` guard + ([`tools/agent-guard`](../../tools/agent-guard/README.md)) at + `$CLAUDE_PROJECT_DIR/.claude/hooks/agent-guard.py` — a + **per-worktree** path. The script + its `guards.d/` are + adopter-installed local files synced into the **main** checkout + by [Step 12 pass 1](#step-12--post-install-sync--worktree-propagation--sandbox-allowlist--sanity-check) + and **gitignored** ([Step 7](#step-7--gitignore-entries-fresh-only)). + Because they are gitignored, **no** worktree inherits them via + `git worktree add` — every freshly-created worktree starts + without the script and would run with the guard **silently + inactive**. The hook seeds them from the main checkout's + already-synced copy when — and only when — this worktree has + none, so the guard is live in every worktree from its first + checkout. It never overwrites a copy the worktree already + carries (which may hold worktree-local guards). The hook is a small shell script. Surface the exact content to the user before writing: ```bash #!/usr/bin/env bash -# 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 /magpie-setup-isolated-setup-install; -# no-op when the helper is absent. +# apache-magpie post-checkout hook (installed by /magpie-setup adopt). +# Best-effort per-worktree reconciliation on checkout / `git worktree add`. +# Every action is individually guarded and the hook always exits 0 — it +# never gates the surrounding git operation. set -u + +# (a) Sandbox allowlist (issue #197): add the current worktree's working +# dir to the worktree's own .claude/settings.local.json. No-op when the +# helper from /magpie-setup-isolated-setup-install is absent. if [ -x "$HOME/.claude/scripts/sandbox-add-project-root.sh" ]; then "$HOME/.claude/scripts/sandbox-add-project-root.sh" || true fi + +# (b) agent-guard PreToolUse guard: .claude/settings.json resolves it at +# $CLAUDE_PROJECT_DIR/.claude/hooks/agent-guard.py (per-worktree). Seed +# this worktree from the main checkout's already-synced copy when it has +# none — never overwrite a copy the worktree already carries. +wt="$(git rev-parse --show-toplevel 2>/dev/null)" || wt="" +main="$(dirname "$(cd "$(git rev-parse --git-common-dir 2>/dev/null)" 2>/dev/null && pwd)")" +if [ -n "$wt" ] && [ "$main" != "$wt" ] \ + && [ -f "$main/.claude/hooks/agent-guard.py" ] \ + && [ ! -f "$wt/.claude/hooks/agent-guard.py" ]; then + mkdir -p "$wt/.claude/hooks/guards.d" + cp "$main/.claude/hooks/agent-guard.py" "$wt/.claude/hooks/agent-guard.py" || true + [ -d "$main/.claude/hooks/guards.d" ] && + cp "$main/.claude/hooks/guards.d/"*.py "$wt/.claude/hooks/guards.d/" 2>/dev/null || true +fi + exit 0 ``` -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. +The `|| true` guards (and the trailing `exit 0`) keep 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 `/magpie-setup-isolated-setup-install`, -the helper-script line is a no-op (the `-x` test fails). When +the helper-script line (a) 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. - -**Why no framework-skill symlink reconciliation here.** Earlier +the next `post-checkout` fires the helper automatically. Likewise +(b) is a no-op when the main checkout has no agent-guard yet (the +`-f "$main/.claude/hooks/agent-guard.py"` test fails) or when the +worktree already carries its own copy. + +**Why agent-guard seeding is shell-safe but symlink +reconciliation is not.** Seeding agent-guard is a plain +`cp` of files that already exist in the main checkout — a pure +shell operation with no dependency on the agent harness. Recreating +the gitignored framework-skill symlinks is **not**: earlier template versions of this hook also called `/magpie-setup verify --auto-fix-symlinks` to recreate gitignored symlinks after a checkout. That line printed a spurious @@ -972,10 +1041,10 @@ framework before they hit a "skill not found" error: the skill families they actually installed: ```markdown - ## Agent-assisted contribution (apache-steward) + ## Agent-assisted contribution (apache-magpie) This repo adopts the - [`apache/airflow-steward`](https://github.com/apache/airflow-steward) + [`apache/magpie`](https://github.com/apache/magpie) framework via a snapshot mechanism. The framework provides maintainer-facing skills (e.g. `pr-management-triage`, `pr-management-code-review`, `pr-management-stats`, @@ -1007,7 +1076,7 @@ framework before they hit a "skill not found" error: in [`.apache-magpie-overrides/`](.apache-magpie-overrides/) (committed) — never edit the snapshot directly. Framework changes go via PR to - [`apache/airflow-steward`](https://github.com/apache/airflow-steward). + [`apache/magpie`](https://github.com/apache/magpie). ``` Trim the skill-family list to what was actually picked in @@ -1026,10 +1095,10 @@ framework before they hit a "skill not found" error: Suggested template: ```markdown - ## apache-steward framework + ## apache-magpie framework This repo adopts the - [`apache/airflow-steward`](https://github.com/apache/airflow-steward) + [`apache/magpie`](https://github.com/apache/magpie) framework via the snapshot mechanism. The framework provides the `pr-management-*` skills; they are gitignored symlinks into the `.apache-magpie/` snapshot directory. @@ -1041,14 +1110,14 @@ framework before they hit a "skill not found" error: [`.apache-magpie.lock`](.apache-magpie.lock). The contributor-facing summary of the adoption + setup flow lives in the - [Agent-assisted contribution section of `README.md`](README.md#agent-assisted-contribution-apache-steward). + [Agent-assisted contribution section of `README.md`](README.md#agent-assisted-contribution-apache-magpie). Adopter-specific modifications to framework-skill workflows live in [`.apache-magpie-overrides/`](.apache-magpie-overrides/) — never edit the snapshot directly. Framework changes go via PR to - [`apache/airflow-steward`](https://github.com/apache/airflow-steward). + [`apache/magpie`](https://github.com/apache/magpie). ``` Do not create `AGENTS.md` if it does not already exist — @@ -1170,7 +1239,7 @@ Four passes, in this order: 3. **Add the adopter's project root to each worktree's project-local sandbox allowlists.** Defensive against - [issue #197](https://github.com/apache/airflow-steward/issues/197) — + [issue #197](https://github.com/apache/magpie/issues/197) — `sandbox.filesystem.allowRead: ["."]` does not in practice cover CWD, so reads under a freshly-cloned adopter repo fail under the sandbox until an explicit absolute path is @@ -1246,7 +1315,8 @@ A summary of what was written: ✓ Locks: .apache-magpie.lock (committed) + .apache-magpie.local.lock (gitignored) ✓ Symlinks: ✓ Overrides scaffold: .apache-magpie-overrides/ (committed) -✓ post-checkout hook installed +✓ post-checkout hook installed (seeds sandbox allowlist + agent-guard per worktree) +✓ agent-guard PreToolUse hook synced (.claude/hooks/agent-guard.py + guards.d/ — gitignored) ✓ /README.md updated with adoption note Committed (you'll see in `git status`): @@ -1261,6 +1331,9 @@ Committed (you'll see in `git status`): Gitignored (do NOT commit): .apache-magpie/ .apache-magpie.local.lock + .claude/settings.local.json # per-machine, per-worktree sandbox allowlist (issue #197) + .claude/hooks/agent-guard.py # framework code synced from the snapshot; seeded into each worktree + .claude/hooks/guards.d/ # bundled + skill-owned guards; re-collected on /magpie-setup __pycache__/ + *.pyc # byte-compiled artefacts from skill scripts; added to .gitignore if missing .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-* diff --git a/.agents/skills/magpie-setup/overrides.md b/.agents/skills/magpie-setup/overrides.md index 5e1ede6bf229f..33800a8432616 100644 --- a/.agents/skills/magpie-setup/overrides.md +++ b/.agents/skills/magpie-setup/overrides.md @@ -74,7 +74,7 @@ Use the canonical scaffold below. - -# unadopt — remove the apache-steward framework from an adopter repo +# unadopt — remove the apache-magpie framework from an adopter repo The reverse of [`adopt.md`](adopt.md). Removes the framework artefacts the adopt flow installed — gitignored snapshot, @@ -9,9 +9,12 @@ committed lock, gitignored local lock, framework-skill 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. +post-checkout hook, the gitignored agent-guard hook +(`.claude/hooks/agent-guard.py` + `guards.d/`), the adoption +sections in `README.md` / `AGENTS.md` / `CONTRIBUTING.md`, and the +committed `setup` skill itself. (The committed +`.claude/settings.json` `hooks.PreToolUse` wiring is adopter-owned +and agent-edit-denied — surfaced for manual removal, never edited.) > **Critical — tear down *all* target dirs.** Removing only the > `.claude/skills/` + `.github/skills/` pair would **orphan** the @@ -31,7 +34,7 @@ explicit confirmation before any write. ## When to use -- The project decided to stop using apache-steward. +- The project decided to stop using apache-magpie. - The repo was adopted experimentally and the experiment is over. - The adoption was scoped to a fork / branch and that branch @@ -70,7 +73,7 @@ relevant override file rather than unadopting. > undo just this worktree's symlink without touching the > main, `rm /.apache-magpie` manually."* -3. Confirm we are **not** in `apache/airflow-steward` itself +3. Confirm we are **not** in `apache/magpie` itself (`git remote get-url origin`); refuse if it resolves to the framework — the framework is not "adopted into" itself. 4. Confirm `` (`.apache-magpie.lock`) is @@ -101,9 +104,10 @@ every artefact). | Committed lock | `` | exists | | `.gitignore` entries | `/.gitignore` | which of the entries from [`adopt.md` Step 7](adopt.md) are present | | 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 `/skills/`, relays resolving into `.agents/skills/magpie-*` — in **each** target dir | -| Post-checkout hook | `/.git/hooks/post-checkout` | exists + invokes `~/.claude/scripts/sandbox-add-project-root.sh` | -| Doc section: `README.md` | `/README.md` | contains the `## Agent-assisted contribution (apache-steward)` heading | -| Doc section: `AGENTS.md` | `/AGENTS.md` | contains the `## apache-steward framework` heading | +| Post-checkout hook | `/.git/hooks/post-checkout` | exists + invokes `~/.claude/scripts/sandbox-add-project-root.sh` and/or seeds `.claude/hooks/agent-guard.py` | +| agent-guard hook | `/.claude/hooks/agent-guard.py` + `/.claude/hooks/guards.d/` | exist (gitignored framework code). The committed `.claude/settings.json` `hooks.PreToolUse` wiring is **adopter-owned** — surface it for the user to remove by hand (settings.json is agent-edit-denied); do not edit it. | +| Doc section: `README.md` | `/README.md` | contains the `## Agent-assisted contribution (apache-magpie)` heading | +| Doc section: `AGENTS.md` | `/AGENTS.md` | contains the `## apache-magpie framework` heading | | Doc section: `CONTRIBUTING.md` | `/CONTRIBUTING.md` | contains the adoption section (fallback layout) | | Overrides directory | `/.apache-magpie-overrides/` | exists; count framework-scaffold files vs adopter-authored | | `setup` skill itself | canonical `.agents/skills/magpie-setup/` + the `.claude`/`.github` relay symlinks to it | exists (this is the only committed framework skill) | @@ -132,7 +136,9 @@ The following will be REMOVED: .claude/skills/magpie- → ../../.agents/skills/magpie- (relay) .github/skills/magpie- → ../../.agents/skills/magpie- (relay) /skills/magpie- → ../../.agents/skills/magpie- (relay; e.g. .windsurf/skills/, .goose/skills/ — only if present) - .git/hooks/post-checkout (if it contains the steward recipe) + .git/hooks/post-checkout (if it contains the magpie recipe) + .claude/hooks/agent-guard.py (gitignored framework code) + .claude/hooks/guards.d/ (gitignored; bundled + skill-owned guards) # Target dirs (per agents.md): canonical .agents/skills/, the # .claude/skills/ + .github/skills/ relay pair, plus any present # holdout — each carries one magpie- entry per linked skill. @@ -140,8 +146,8 @@ The following will be REMOVED: 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) + README.md (the `## Agent-assisted contribution (apache-magpie)` section) + AGENTS.md (the `## apache-magpie framework` section, if present) .agents/skills/magpie-setup/ (this skill itself — self-destructive; canonical copy) .claude/skills/magpie-setup (relay symlink) .github/skills/magpie-setup (relay symlink) @@ -155,12 +161,12 @@ The following will be PRESERVED: Surface the `~/.config/apache-magpie/user.md` line only if that file is actually present on disk. If it is absent (or the operator drove `user.md` resolution via -`$APACHE_STEWARD_USER_CONFIG` / the legacy per-project location), +`$APACHE_MAGPIE_USER_CONFIG` / the legacy per-project location), omit the line. The framework never touches the per-user file regardless of `--purge-overrides` — it is shared across every adopter project on the operator's machine and unadopting from *this* project does not imply they have stopped using -apache-steward elsewhere. +apache-magpie elsewhere. If `--purge-overrides` was passed, move `.apache-magpie-overrides/` into the *removed* section and @@ -230,21 +236,34 @@ pointing at a deleted snapshot. Never touch a non-symlink at the same path. 2. **Post-checkout hook.** Remove only if its content matches - the steward recipe verbatim (i.e. the hook the adopt flow - wrote — a single - `~/.claude/scripts/sandbox-add-project-root.sh` invocation - guarded by the `-x` test; see + the magpie recipe verbatim (i.e. the hook the adopt flow + wrote — the two-part body that chains + `~/.claude/scripts/sandbox-add-project-root.sh` (guarded by + the `-x` test) **and** seeds `.claude/hooks/agent-guard.py` + from the main checkout; see [`adopt.md` Step 10](adopt.md#step-10--worktree-aware-post-checkout-hook-fresh-only) for the exact text). If the hook contains additional adopter logic, surface that, leave the hook in place, and tell the - user which line to delete by hand. Hooks that still contain + user which lines to delete by hand. Hooks that still contain the obsolete `/magpie-setup verify --auto-fix-symlinks` line (a Claude Code slash command that does not work from a shell hook — removed in a later framework release) should be replaced with the current Step 10 template. -3. **Snapshot directory.** `rm -rf /`. -4. **Local lock.** `rm `. -5. **`.gitignore` entries.** Read `/.gitignore`, +3. **agent-guard hook files.** `rm -f + /.claude/hooks/agent-guard.py` and `rm -rf + /.claude/hooks/guards.d/` — gitignored framework + code, no `git rm` needed. If the adopter force-added their own + guards under `guards.d/` (tracked via `git add -f`), surface + them and `git rm` only those by name; do not delete an + adopter-authored tracked guard silently. Leave the + `.claude/hooks/` directory itself if it holds non-framework + hooks. The committed `.claude/settings.json` `hooks.PreToolUse` + wiring is **adopter-owned and agent-edit-denied** — surface the + exact entry for the user to delete by hand; do not edit + `settings.json`. +4. **Snapshot directory.** `rm -rf /`. +5. **Local lock.** `rm `. +6. **`.gitignore` entries.** Read `/.gitignore`, remove exactly the lines from [`adopt.md` Step 7](adopt.md) that are present, and leave any adopter-added entries (e.g. unrelated rules near the @@ -253,21 +272,21 @@ pointing at a deleted snapshot. and `*.pyc` in place — they are stock Python entries that most repos carry independently of the framework, so removing them would break the adopter's own Python ignores. Only drop - them if they sit unambiguously inside the steward-managed + them if they sit unambiguously inside the magpie-managed block (under the same comment header the adopt flow wrote) and the repo has no other Python sources. -6. **Doc sections.** For each of `README.md`, `AGENTS.md`, +7. **Doc sections.** For each of `README.md`, `AGENTS.md`, `CONTRIBUTING.md` that contains an adoption section, remove the section. The boundaries are the section heading (e.g. `## Agent-assisted contribution (apache- - steward)`) and the next `##`-level heading (or EOF). + magpie)`) and the next `##`-level heading (or EOF). Surface the proposed diff (`git diff` form) to the user before writing; one batched confirmation for the whole doc set, not per file. -7. **Committed lock.** `git rm `. -8. **Overrides directory** *(only if `--purge-overrides`)*. +8. **Committed lock.** `git rm `. +9. **Overrides directory** *(only if `--purge-overrides`)*. `git rm -r .apache-magpie-overrides/`. -9. **`setup` skill itself.** `git rm -r` the canonical copy +10. **`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 @@ -292,7 +311,11 @@ After the deletions, verify the post-state: pair, or any holdout) — neither dangling canonical links into the removed `/` nor relays into the now-empty `.agents/skills/`. -- `.gitignore` no longer contains the steward entries. +- `.gitignore` no longer contains the magpie entries. +- `.claude/hooks/agent-guard.py` and `.claude/hooks/guards.d/` + do not exist (save any adopter-authored guards the user chose + to keep); the `.claude/settings.json` `hooks.PreToolUse` entry + was surfaced for manual removal. - The doc sections are gone from the affected files. - `.agents/skills/magpie-setup/` and its `.claude`/`.github` relays do not exist. @@ -320,7 +343,7 @@ 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) .agents/skills/, .claude/skills/, .github/skills/ (target dirs — adopter-owned; only the magpie-* entries were removed) - + Staged for commit (you'll see in `git status`): D .apache-magpie.lock @@ -330,7 +353,7 @@ Staged for commit (you'll see in `git status`): 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. +framework repo at https://github.com/apache/magpie. ``` Suggest the user open the diff (`git diff --cached`) before diff --git a/.agents/skills/magpie-setup/upgrade.md b/.agents/skills/magpie-setup/upgrade.md index 205f52a595985..fbb7586c93610 100644 --- a/.agents/skills/magpie-setup/upgrade.md +++ b/.agents/skills/magpie-setup/upgrade.md @@ -57,30 +57,61 @@ Both paths run the same flow. route as a recover-snapshot install per the committed lock, not as an upgrade. Continue at Step 3. -## Step 0a — Pre-Magpie leftovers safety check +## Step 0a — Migrate `apache-steward`-era naming The framework was once named **apache-steward** before it was -renamed to **Apache Magpie**. The framework **no longer ships an -automated pre-Magpie migration** — a repo still on the old layout -must be migrated by hand. +renamed to **Apache Magpie**. Every upgrade run **performs this +migration automatically** so no adopter is left half-renamed. +**This step is the only place the `steward` name should still +appear anywhere in the framework — and only as the *source* side +of a rename.** -If you detect **any** legacy artefact here — +First detect whether any legacy artefact is present — `.apache-steward.lock`, `.apache-steward/`, -`.apache-steward-overrides/`, a committed -`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: - -1. Remove the legacy in-repo artefacts (`.apache-steward*`, any - un-prefixed framework symlinks, a committed `setup-steward` - skill). -2. Re-adopt from scratch with `/magpie-setup` so the current - `.apache-magpie*` layout and `magpie-`-prefixed symlinks are - written fresh. -3. Move `~/.config/apache-steward/` (per-user) to - `~/.config/apache-magpie/` and update any sandbox-allowlist - entry that referenced the old path. +`.apache-steward-overrides/`, a committed `setup-steward/` skill +directory, a framework symlink **without** the `magpie-` prefix, +a `~/.config/apache-steward/` user-config dir, a +`[tool.steward.checks]` block in a member `pyproject.toml`, a +`STEWARD_*` / `APACHE_STEWARD_*` reference, or an +`apache-steward` / `airflow-steward` path in `.claude/settings*.json`. +If **none** is present, the repo is already on the Magpie layout — +skip to Step 1. + +Otherwise, perform every migration below that applies, then resume +the normal upgrade against the clean Magpie layout. + +**Performed automatically by this skill:** + +1. **User config dir.** If `~/.config/apache-steward/` exists and + `~/.config/apache-magpie/` does not, move it: + `mv ~/.config/apache-steward ~/.config/apache-magpie`. If **both** + exist, do **not** clobber — stop and ask the maintainer to merge + them by hand. +2. **Sandbox-allowlist path references.** In `.claude/settings.json` + and `.claude/settings.local.json`, rewrite any `apache-steward` + path to `apache-magpie` and any `airflow-steward` checkout path to + the current repo path. +3. **Per-member opt-out key.** Rewrite any `[tool.steward.checks]` + block in a workspace member's `pyproject.toml` to + `[tool.magpie.checks]`. +4. **Snapshot layout.** Remove the legacy gitignored artefacts + (`.apache-steward*`, any un-prefixed framework symlinks, a + committed `setup-steward` skill) and re-adopt with `/magpie-setup` + so the `.apache-magpie*` layout and `magpie-`-prefixed symlinks + are written fresh. (The snapshot is a build artefact, so + re-adoption — not hand-editing — is the safe path here.) + +**Cannot be reached from inside the repo — prompt the maintainer:** + +5. **Environment variables.** Any `STEWARD_*` override + (`STEWARD_GUARD_OFF`, `STEWARD_ALLOW_*`, `STEWARD_GUARD_DIRS`, + `STEWARD_READY_LABEL`) and `APACHE_STEWARD_USER_CONFIG` are now + `MAGPIE_*` / `APACHE_MAGPIE_USER_CONFIG`. Tell the maintainer to + update their shell profile, CI secrets, and any wrapper scripts. +6. **Issue / PR body markers.** Comment markers written as + `` are now ``. + The tooling reads only the new marker, so any open tracker item + still carrying the old marker must have it rewritten by hand. Then resume this upgrade against the clean Magpie layout. @@ -179,7 +210,7 @@ bootstrap logic. It implements the diff and stop. Do **not** silently overwrite local work. The user either (a) confirms the modifications can be discarded, (b) decides to upstream them as a PR - against `apache/airflow-steward` first, or (c) defers + against `apache/magpie` first, or (c) defers the bootstrap-skill update to a later upgrade run. 3. **If there are no local modifications** (or the user confirmed in 2), copy the snapshot's `setup` @@ -215,7 +246,7 @@ bootstrap logic. It implements The adopter shouldn't modify the bootstrap copy locally — the framework's hard rule is *"local mods go in `.apache-magpie-overrides/`, framework changes go via PR -to `apache/airflow-steward`"*. But if they did, step (2) +to `apache/magpie`"*. But if they did, step (2) catches it before the overwrite would erase their work. ## Step 5 — Reconcile overrides @@ -383,7 +414,13 @@ The framework ships hooks and config files an adopter rather than pulls in via symlink. Examples: - `/.git/hooks/post-checkout` (the worktree-aware - hook installed during adoption). + hook installed during adoption). Its expected content is the + [`adopt.md` Step 10](adopt.md#step-10--worktree-aware-post-checkout-hook-fresh-only) + template — which now both chains the sandbox-allowlist helper + **and** seeds a new worktree's agent-guard from the main + checkout. An adopter on an older hook (sandbox-only, or the + long-removed `--auto-fix-symlinks` line) is re-installed to the + current template by this drift sync. - `/.claude/hooks/agent-guard.py` and the `/.claude/hooks/guards.d/` directory (the deterministic `PreToolUse` guard dispatcher and its guards — see @@ -488,7 +525,7 @@ makes the skipped worktrees easy to come back to. sandbox-allowlist helper once with `--all-worktrees` to ensure each worktree's project root is in that worktree's own `.claude/settings.local.json` (defensive against -[issue #197](https://github.com/apache/airflow-steward/issues/197); +[issue #197](https://github.com/apache/magpie/issues/197); see [`setup-isolated-setup-install/SKILL.md` → Step P](../setup-isolated-setup-install/SKILL.md#step-p--project-root-coverage-in-the-sandbox-allowlists)): @@ -533,7 +570,7 @@ times been seeded from one specific adopter's data (originally the project the framework grew out of) and not always generalised back. This step surfaces the residue so it can be filed as an issue against -`apache/airflow-steward` and fixed upstream. +`apache/magpie` and fixed upstream. For each file under `/projects/_template/`, scan for adopter-specific signals: @@ -565,7 +602,7 @@ read-only per [`SKILL.md` Golden rule 1](SKILL.md#golden-rules)). The recap is purely advisory; the operator decides whether to open a tracking issue / PR against -`apache/airflow-steward`. +`apache/magpie`. The check is intentionally heuristic — false positives are acceptable because the cost is one line in the summary, not @@ -693,7 +730,7 @@ Framework templates (projects/_template/): ✓ all templates look generic OR ⚠ <_template/foo.md> (e.g. H1 title hardcoded to a specific project name) ⚠ <_template/bar.md> (e.g. `committers_team` set to a concrete org/team without placeholder) - → file an issue against apache/airflow-steward to upstream a fix + → file an issue against apache/magpie to upstream a fix Recommended follow-ups: - Run /magpie-setup-isolated-setup-update if the secure-setup blast diff --git a/.agents/skills/magpie-setup/verify.md b/.agents/skills/magpie-setup/verify.md index 1f39588369644..111d9a76147d8 100644 --- a/.agents/skills/magpie-setup/verify.md +++ b/.agents/skills/magpie-setup/verify.md @@ -1,7 +1,7 @@ -# verify — health check of the steward integration + drift detection +# verify — health check of the magpie integration + drift detection Confirms the framework is wired in correctly so the rest of the framework's skills resolve from the right paths, and @@ -156,7 +156,7 @@ Check that the entries from settings — written to by [`sandbox-add-project-root.sh`](../../tools/agent-isolation/sandbox-add-project-root.sh) as the per-worktree sandbox-allowlist defense for - [issue #197](https://github.com/apache/airflow-steward/issues/197); + [issue #197](https://github.com/apache/magpie/issues/197); must never be committed since the content is machine-specific absolute paths) - `__pycache__/` and `*.pyc` (byte-compiled artefacts emitted when @@ -275,7 +275,7 @@ snapshot's `.apache-magpie/skills/setup/`. adopter modified the bootstrap skill directly; an anti-pattern per the framework's hard rule). The framework-side fix is to upstream the modifications as - a PR against `apache/airflow-steward`; the local fix + a PR against `apache/magpie`; the local fix is to revert the modifications and use `.apache-magpie-overrides/` instead. @@ -284,12 +284,18 @@ snapshot's `.apache-magpie/skills/setup/`. Two sub-checks on `/.git/hooks/post-checkout`: 1. **Presence + executable.** File exists, is executable, - and contains the - `/magpie-setup verify --auto-fix-symlinks` recipe. - - ⚠ if missing — strictly optional, but worktrees off - this repo will need a manual - `/magpie-setup verify --auto-fix-symlinks` after - checkout. Print the install recipe. + and carries the current hook body — the sandbox-allowlist + helper chain **and** the agent-guard seeding block (see + [`adopt.md` Step 10](adopt.md#step-10--worktree-aware-post-checkout-hook-fresh-only)). + It must **not** contain the long-removed + `/magpie-setup verify --auto-fix-symlinks` line (a slash + command is not shell-callable; it printed a spurious error on + every checkout). + - ⚠ if missing — strictly optional, but worktrees off this + repo will then not get their sandbox allowlist or + agent-guard seeded automatically on `git worktree add` + (they fall back to `/magpie-setup worktree-init`). Print + the install recipe. 2. **Content drift vs the framework's expected.** Diff the installed hook against the framework's expected hook @@ -334,10 +340,24 @@ Three sub-checks for the deterministic guard [`adopt.md` Step 12](adopt.md#step-12--post-install-sync--worktree-propagation--sandbox-allowlist--sanity-check)) for the maintainer to apply (settings.json is agent-edit-denied). +The script + `guards.d` are **gitignored** framework code +([`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only)), +synced from the snapshot rather than committed — so a *missing* +script is the expected state of a fresh checkout, not a defect, and +the fix is always a re-sync (never `git add`). When this check runs +**inside a worktree**, the script + `guards.d` are per-worktree +files (the `settings.json` wiring resolves +`$CLAUDE_PROJECT_DIR/.claude/hooks/agent-guard.py` against the +worktree root). The remediation for a *missing* script in a worktree +is not the main-checkout sync but +[`worktree-init.md` Step 1d](worktree-init.md#step-1d--seed-the-worktrees-agent-guard-pretooluse-hook) +(or the post-checkout hook on the next `git worktree add`), which +seeds it from the main checkout's already-synced copy. + ### 8b. Sandbox-allowlist coverage of the current worktree Defensive cross-check for -[issue #197](https://github.com/apache/airflow-steward/issues/197): +[issue #197](https://github.com/apache/magpie/issues/197): `sandbox.filesystem.allowRead: ["."]` does not in practice cover CWD under the harness, so `/magpie-setup` (adopt, upgrade, worktree-init) chains into @@ -544,7 +564,7 @@ adopter opted into via (The `mcp__apache-projects__*` read tools back the roster / affiliation lookups — also used by `contributor-nomination`, - the maintainer-side PMC/committer assessment skill. Both MCP + the maintainer-side /committer assessment skill. Both MCP servers are installed from the latest `main` of `apache/comdev`; see [`tools/apache-projects/tool.md`](../../tools/apache-projects/tool.md) and [`tools/ponymail/tool.md`](../../tools/ponymail/tool.md).) @@ -662,15 +682,15 @@ Two files to check (per - **`/README.md`** — should have a contributor-facing section (typically `## Agent-assisted contribution - (apache-steward)`) that mentions the snapshot mechanism, the + (apache-magpie)`) that mentions the snapshot mechanism, the `/magpie-setup` invocation for fresh clones, the `.apache-magpie.lock` pin, and `.apache-magpie-overrides/`. - Grep for `apache-steward` and `/magpie-setup` together as a + Grep for `apache-magpie` and `/magpie-setup` together as a proxy. ⚠ if either token is absent. - **`/AGENTS.md`** — if the file exists, it should - have an `## apache-steward framework` section that + have an `## apache-magpie framework` section that cross-references the README section. Grep for - `apache-steward` and a link to the README anchor. ⚠ if the + `apache-magpie` and a link to the README anchor. ⚠ if the file exists but lacks the section; not applicable if the file does not exist (do not create one just to satisfy the check). diff --git a/.agents/skills/magpie-setup/worktree-init.md b/.agents/skills/magpie-setup/worktree-init.md index 07fcf28bb9cdc..2cee737af6421 100644 --- a/.agents/skills/magpie-setup/worktree-init.md +++ b/.agents/skills/magpie-setup/worktree-init.md @@ -132,7 +132,7 @@ target. ## Step 1c — Add the worktree to its own project-local sandbox allowlists Defensive against -[issue #197](https://github.com/apache/airflow-steward/issues/197) — +[issue #197](https://github.com/apache/magpie/issues/197) — `sandbox.filesystem.allowRead: ["."]` does not in practice cover the worktree's working dir, so reads under this worktree fail under the sandbox until an explicit absolute path is added. See @@ -173,6 +173,53 @@ one-line recap row for the Step 2 summary: `worktree-init` does **not** fail when the helper is absent; secure-agent isolation is independent of framework adoption. +## Step 1d — Seed the worktree's agent-guard PreToolUse hook + +The committed `.claude/settings.json` wires the deterministic +guard ([`tools/agent-guard`](../../tools/agent-guard/README.md)) +at `$CLAUDE_PROJECT_DIR/.claude/hooks/agent-guard.py` — a +**per-worktree** path. The script + its `guards.d/` are +adopter-installed local files synced into the **main** checkout by +[`adopt.md` Step 12 pass 1](adopt.md#step-12--post-install-sync--worktree-propagation--sandbox-allowlist--sanity-check) +/ [`upgrade.md` Step 6b](upgrade.md#step-6b--sync-locally-installed-hooks-and-configuration) +and **gitignored** ([`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only)). +Because they are gitignored, no worktree inherits them via git — +every worktree starts without the script and would run with the +guard **silently inactive** until seeded. + +This is the agent-driven counterpart of the +[post-checkout hook's agent-guard seeding](adopt.md#step-10--worktree-aware-post-checkout-hook-fresh-only): +the git hook covers `git worktree add`, this step covers worktrees +that pre-date the hook or where its best-effort copy did not run. + +Seed from the main checkout's already-synced copy — a plain file +copy, the same `
` resolved in Step 0: + +```bash +# Only when the main has a guard and this worktree has none — never +# overwrite a copy the worktree already carries (worktree-local guards). +if [ -f "
/.claude/hooks/agent-guard.py" ] && + [ ! -f "/.claude/hooks/agent-guard.py" ]; then + mkdir -p "/.claude/hooks/guards.d" + cp "
/.claude/hooks/agent-guard.py" "/.claude/hooks/agent-guard.py" + cp "
/.claude/hooks/guards.d/"*.py "/.claude/hooks/guards.d/" 2>/dev/null || true +fi +``` + +Idempotent: a no-op when the worktree already has the script, and +a no-op when the main has no agent-guard yet (an adopter who has +not run the Step 12 / Step 6b sync). Surface a one-line recap row +for Step 2: + +- ✓ already present, OR +- + seeded from `
` (script + N guards), OR +- ⚠ main has no agent-guard yet — run `/magpie-setup` (or + `/magpie-setup upgrade`) from the main checkout to sync it. + +`worktree-init` does **not** fail when the main carries no +agent-guard; the guard is an opt-in adopter-side file, and the +worktree's framework-skill symlinks are usable without it. + ## Step 2 — Recap Print a short summary: @@ -185,6 +232,9 @@ Print a short summary: active target dir (`.agents/skills/`, the `.claude/`/`.github/` pair, any present holdout), split into *opt-in* and *always-on*, with per-skill ✓ / + / ↻ counts. +- The sandbox-allowlist recap row from Step 1c. +- The agent-guard recap row from Step 1d (✓ already present / + + seeded / ⚠ main has no agent-guard yet). - A reminder: `upgrade` from the main, not from the worktree. ## Inputs