Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
extend-ignore-re = [
"`[0-9a-f]{7,40}`",
"surface_hash: sha256:[0-9a-f]+",
# Fixture state stores the invoking skill's generated surface digest as JSON.
'"hash": "sha256:[0-9a-f]+"',
"FRAMEWORK_FINGERPRINT = \"sha256:[0-9a-f]+\"",
]

Expand Down
2 changes: 2 additions & 0 deletions docs/pr-management/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ wording, CI-check → doc-URL map, review-criteria source files).

## Install & first runs

New to this family? Read the [one-minute welcome guide](../../plugins/magpie-setup/skills/setup/setup_preflight/guides/pr-management.md).

Install just this family — one plugin, 9 skills. Maintainer-facing PR-queue management.

Once you have [added the marketplace](../setup/marketplace-install.md):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->

# 🐦 Magpie here — your PR-management sidekick

A one-minute tour, then straight back to what you asked for.

## ⚙️ Configure it

Start with `<upstream>`, the project's review criteria and its area labels.
`pr-management-code-review-criteria.md` names the criteria sources;
`pr-management-config.md` carries the labels and queue rules.
`reviewer-roster.md` tells reviewer routing who covers each area.

- **Configure it for yourself** — personal configuration, nothing committed — `/magpie-setup:setup config`
- **Adopted** — for your project: shared configuration committed to the repo — `/magpie-setup:setup adopt` (a maintainers' decision)

On a pinned-snapshot install, use `/magpie-setup config` or `/magpie-setup adopt`.

Optional companion: roborev reviews your own branch before it becomes a PR;
this family helps you review and manage other people's PRs.
Neither requires the other.

## 🔄 The typical flow

1. **Triage the queue** — propose a disposition for each PR — *"triage the open PRs"*
2. **Route reviewers** — use the roster to suggest the right people — *"who should review this PR?"*
3. **Review** — inspect the diff and draft inline feedback — *"review this PR against our criteria"*
4. **Quick-merge** — screen easy candidates and propose the command for you to run — *"which PRs are ready for quick-merge?"*
5. **Mentor** — help a contributor through the next step — *"draft helpful feedback for this contributor"*
6. **Stale sweep** — propose follow-ups for inactive PRs — *"show me the stale PRs"*
7. **Stats** — see where the queue needs attention — *"summarise the PR backlog by area"*

Nothing is written, posted, labelled or closed without your explicit confirmation.
Quick-merge never merges for you: it hands you the command.

## 💬 Just ask

- *"check my branch before I open my first PR"*
- *"triage the PRs waiting for review"*
- *"who should review this PR?"*
- *"review this PR against our criteria"*
- *"review this PR stack in dependency order"*
- *"which PRs are ready for quick-merge?"*
- *"help this contributor understand the requested change"*
- *"what changed in the queue since the last sweep?"*
Comment thread
leninworld marked this conversation as resolved.
- *"summarise the PR backlog by area"*

## 🎨 Make it yours

Change a workflow by describing the change:

- *"from now on, group the backlog by our area labels"*
- *"use our review criteria when drafting feedback"*

Each becomes an override for the PR-management skill it changes.
Useful to other maintainers? *"Upstream this change to Magpie"* prepares a generic PR for you to review before it opens.

<!-- welcome footer: generated from guides/_footer.md -->
27 changes: 21 additions & 6 deletions tools/setup-preflight/tests/test_welcome.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

REPO_ROOT = Path(__file__).resolve().parents[3]
SECURITY_SKILL = "issue-sync"
OTHER_SKILL = "pr-triage"
OTHER_SKILL = "ci-runner-audit"


def stamp(root: Path) -> dict:
Expand Down Expand Up @@ -136,6 +136,14 @@ def test_the_issue_family_guide_comes_on_the_next_run(project: Path, version) ->
assert due(project, "magpie-issue-triage") == [("general", "again")]


def test_the_pr_management_family_guide_comes_on_the_next_run(project: Path, version) -> None:
assert due(project, "pr-triage") == [("general", "first-run")]
welcome.record(project, "general", "seen")
assert due(project, "pr-triage") == [("pr-management", "first-run")]
welcome.record(project, "pr-management", "seen")
assert due(project, "pr-triage") == [("general", "again")]


def test_at_most_one_guide_per_run(project: Path, version) -> None:
for _ in range(8):
found = welcome_findings(project, SECURITY_SKILL)
Expand All @@ -159,7 +167,7 @@ def test_a_hidden_general_guide_does_not_hold_back_the_family_one(project: Path,


def test_a_skill_of_a_family_without_a_guide_gets_only_the_general_one(project: Path, version) -> None:
assert welcome.family_of(OTHER_SKILL) == "pr-management"
assert welcome.family_of(OTHER_SKILL) == "repo-health"
assert due(project, OTHER_SKILL) == [("general", "first-run")]
welcome.record(project, "general", "never")
assert due(project, OTHER_SKILL) == []
Expand Down Expand Up @@ -431,13 +439,17 @@ def test_show_without_color_prints_the_markdown(capsys) -> None:
# --- what ships -----------------------------------------------------------------------


def test_the_rules_section_and_both_guides_ship() -> None:
def test_the_rules_section_and_all_guides_ship() -> None:
assert welcome.SECTION in sections.available()
assert {"general", "security", "utilities", "mentoring", "pairing", "issue"} <= set(welcome.guides())
assert {"general", "security", "utilities", "mentoring", "pairing", "issue", "pr-management"} <= set(
welcome.guides()
)
assert welcome.family_guides() == [g for g in welcome.guides() if g != "general"]


@pytest.mark.parametrize("name", ["general", "security", "utilities", "mentoring", "pairing", "issue"])
@pytest.mark.parametrize(
"name", ["general", "security", "utilities", "mentoring", "pairing", "issue", "pr-management"]
)
def test_each_guide_is_short_and_ends_with_the_way_back(name: str) -> None:
text = welcome.guide(name) or ""
assert not text.startswith("<!--")
Expand All @@ -462,7 +474,8 @@ def test_the_footer_is_shared_and_filled_in_per_guide() -> None:
mentoring = welcome.guide("mentoring") or ""
pairing = welcome.guide("pairing") or ""
issue = welcome.guide("issue") or ""
for text in (security, general, utilities, mentoring, pairing, issue):
pr_management = welcome.guide("pr-management") or ""
for text in (security, general, utilities, mentoring, pairing, issue, pr_management):
assert "{{" not in text
assert "welcome footer: generated" not in text
assert "`/magpie-setup:welcome-guide security`" in security
Expand All @@ -477,6 +490,8 @@ def test_the_footer_is_shared_and_filled_in_per_guide() -> None:
assert "--family pairing --color=always" in pairing
assert "`/magpie-setup:welcome-guide issue`" in issue
assert "--family issue --color=always" in issue
assert "`/magpie-setup:welcome-guide pr-management`" in pr_management
assert "--family pr-management --color=always" in pr_management


def test_list_prints_every_guide(capsys) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tools/skill-evals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Behavioral eval harness for Apache Magpie skills. Each eval suite tests a skill

Suites are currently implemented for:

- **setup** — 133 cases across 30 steps (step-verify-drift, step-overrides-surface, step-override-bypass, step-m3-baseline-pick, step-m4-install-gates, step-m5-no-repo-offer, step-m5-companion-offer, step-upgrade-companion-offer, step-adopt-settings-merge, verify-default-set, uninstall-default-set, lock-marketplace-parse, adopt-write-floor, setup-prefill-from-floor, preflight-floor, upgrade-adoption-split, verify-floor, adopt-review-process, adopt-security-list, step-reconcile, step-verify, step-config-stamp, step-config-adversarial, step-config-mail, step-adopt-stamp, step-upgrade-stamp, step-delegate-propose, config-vcs-backend, config-local-custom-parser)
- **setup** — 134 cases across 30 steps (step-verify-drift, step-overrides-surface, step-override-bypass, step-m3-baseline-pick, step-m4-install-gates, step-m5-no-repo-offer, step-m5-companion-offer, step-upgrade-companion-offer, step-adopt-settings-merge, verify-default-set, uninstall-default-set, lock-marketplace-parse, adopt-write-floor, setup-prefill-from-floor, preflight-floor, upgrade-adoption-split, verify-floor, adopt-review-process, adopt-security-list, step-reconcile, step-verify, step-config-stamp, step-config-adversarial, step-config-mail, step-adopt-stamp, step-upgrade-stamp, step-delegate-propose, config-vcs-backend, config-local-custom-parser)
- **setup-isolated-setup-install** — 19 cases across 5 steps (runtime-routing, step-snapshot-drift, step-scope-confirm, step-hardware-key, step-roborev-exclusion)
- **setup-privacy-llm** — 6 cases across 2 steps (step-1-resolve, step-4-gate)
- **setup-shared-config-sync** — 12 cases across 2 steps (step-3-decide-action, step-5-draft-commit)
Expand Down
4 changes: 2 additions & 2 deletions tools/skill-evals/evals/setup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Behavioral evals for the `setup` skill.

## Suites (133 cases total)
## Suites (134 cases total)

| Suite | Step | Cases | What it covers |
|---|---|---|---|
Expand All @@ -25,7 +25,7 @@ Behavioral evals for the `setup` skill.
| adopt-review-process | adopt.md § Step 4c | 5 | a fresh adopt finding two deviations in the confirmed documents where one is accepted and one rejected, and a release policy left alone because that family is not in the floor; a re-adoption reviewing only the one family the floor diff added; a re-adoption that adds no family, where edited process documents are still not re-read; a sentence that would weaken a merge confirmation gate, dropped and named while a label deviation beside it is written; and a repo whose only candidate document the maintainer un-ticks, skipping the step |
| adopt-security-list | adopt.md § Step 4b | 4 | an ASF project whose dedicated security list is in its docs and on the ASF page (recommend `security@`); an ASF project with no dedicated list (no recommendation, `private@` only as a flagged fallback); a non-ASF project, which is not asked; and conflicting evidence between `SECURITY.md` and the ASF page (no recommendation, flagged fallback) |
| setup-prefill-from-floor | install.md § Step M0b | 4 | adopted (propose the floor), unadopted (framework defaults), a foreign marketplace called out, and a snapshot lock falling through |
| preflight-floor | preflight-block.md § Pre-flight | 13 | at floor (silent), below floor (update), plugin missing (install), foreign marketplace (ask first, run nothing), no `claude` CLI (print), unadopted (propose setup), `apache/magpie` in an alarming context (update anyway, unasked), unreadable `claude plugin list --json` (silent, treated as unknown not absent), first Magpie run (show the general welcome guide, record it, carry on), first utilities run (show the utilities welcome guide, record it, carry on), first mentoring run (show the mentoring welcome guide, record it, carry on), first pairing run (show the pairing welcome guide, record it, carry on), first issue run (show the issue welcome guide, record it, carry on) |
| preflight-floor | preflight-block.md § Pre-flight | 14 | at floor (silent), below floor (update), plugin missing (install), foreign marketplace (ask first, run nothing), no `claude` CLI (print), unadopted (propose setup), `apache/magpie` in an alarming context (update anyway, unasked), unreadable `claude plugin list --json` (silent, treated as unknown not absent), first Magpie run (show the general welcome guide, record it, carry on), first utilities run (show the utilities welcome guide, record it, carry on), first mentoring run (show the mentoring welcome guide, record it, carry on), first pairing run (show the pairing welcome guide, record it, carry on), first issue run (show the issue welcome guide, record it, carry on), first pr-management run (show the pr-management welcome guide, record it, carry on) |
| upgrade-adoption-split | upgrade.md § Step 0b | 4 | not adopted (nothing staged), adopted (floor raised and staged), already ahead (floor unchanged), snapshot method (falls through) |
| verify-floor | verify.md § Adoption floor | 4 | no lock (not a fault), ahead of floor with extra plugins (not a fault), a shortfall (a fault), a floor plugin the marketplace no longer ships (a fault, not installed around) |
| step-reconcile | reconcile.md § The sweep | 3 | a clean sweep on a pinned-snapshot install (anchor present, config resolved — stamp written, nothing proposed), a renamed step heading stranding an override's anchor (one re-anchor proposal named), a marketplace install whose plugin cache is sandbox-denied (anchor resolution left `unchecked`, config resolution still completes) |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"tags":["local-smoke"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"action": "welcome", "commands": [], "blocks": false}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->

There is no `.apache-magpie.lock`, no `.apache-magpie-overrides/` and no
`.apache-magpie-local/` at the repo root. The running agent is Claude Code
with magpie-setup 0.4.0 and magpie-pr-management 0.4.0 installed.

The user has already seen the initial general welcome guide on an earlier run.
The invoked skill's own frontmatter reads `name: pr-triage` and
`surface_hash: sha256:54dc4ba1be7b36d4`; it belongs to the pr-management family.
This is the first time a pr-management skill has run here.
The user asked it to "triage the open PRs".

The pre-flight command printed:

```json
{
"findings": [
{
"code": "welcome",
"facts": {
"family": "pr-management",
"guide": "# \ud83d\udc26 Magpie here \u2014 your PR-management sidekick\n\nA one-minute tour, then straight back to what you asked for.\n\n## \u2699\ufe0f Configure it\n\nStart with `<upstream>`, the project's review criteria and its area labels.\n`pr-management-code-review-criteria.md` names the criteria sources;\n`pr-management-config.md` carries the labels and queue rules.\n`reviewer-roster.md` tells reviewer routing who covers each area.\n\n- **Configure it for yourself** \u2014 personal configuration, nothing committed \u2014 `/magpie-setup:setup config`\n- **Adopted** \u2014 for your project: shared configuration committed to the repo \u2014 `/magpie-setup:setup adopt` (a maintainers' decision)\n\nOn a pinned-snapshot install, use `/magpie-setup config` or `/magpie-setup adopt`.\n\nOptional companion: roborev reviews your own branch before it becomes a PR;\nthis family helps you review and manage other people's PRs.\nNeither requires the other.\n\n## \ud83d\udd04 The typical flow\n\n1. **Triage the queue** \u2014 propose a disposition for each PR \u2014 *\"triage the open PRs\"*\n2. **Route reviewers** \u2014 use the roster to suggest the right people \u2014 *\"who should review this PR?\"*\n3. **Review** \u2014 inspect the diff and draft inline feedback \u2014 *\"review this PR against our criteria\"*\n4. **Quick-merge** \u2014 screen easy candidates and propose the command for you to run \u2014 *\"which PRs are ready for quick-merge?\"*\n5. **Mentor** \u2014 help a contributor through the next step \u2014 *\"draft helpful feedback for this contributor\"*\n6. **Stale sweep** \u2014 propose follow-ups for inactive PRs \u2014 *\"show me the stale PRs\"*\n7. **Stats** \u2014 see where the queue needs attention \u2014 *\"summarise the PR backlog by area\"*\n\nNothing is written, posted, labelled or closed without your explicit confirmation.\nQuick-merge never merges for you: it hands you the command.\n\n## \ud83d\udcac Just ask\n\n- *\"check my branch before I open my first PR\"*\n- *\"triage the PRs waiting for review\"*\n- *\"who should review this PR?\"*\n- *\"review this PR against our criteria\"*\n- *\"review this PR stack in dependency order\"*\n- *\"which PRs are ready for quick-merge?\"*\n- *\"help this contributor understand the requested change\"*\n- *\"what changed in the queue since the last sweep?\"*\n- *\"summarise the PR backlog by area\"*\n\n## \ud83c\udfa8 Make it yours\n\nChange a workflow by describing the change:\n\n- *\"from now on, group the backlog by our area labels\"*\n- *\"use our review criteria when drafting feedback\"*\n\nEach becomes an override for the PR-management skill it changes.\nUseful to other maintainers? *\"Upstream this change to Magpie\"* prepares a generic PR for you to review before it opens.\n\n## \ud83d\udc4b About this guide\n\n- **Continue** \u2014 show it again next time (three times at most)\n- **Dismiss** \u2014 hide it until the next Magpie upgrade\n- **Don't show again** \u2014 hide it for good; say *\"show the pr-management welcome again\"* to bring it back\n\nShow this again any time: `/magpie-setup:welcome-guide pr-management`\n(`/magpie-setup-welcome-guide pr-management` on a pinned snapshot).\n\n\ud83c\udf08 Prefer it in colour? Run this yourself \u2014 the `!` prefix runs it as a shell command:\n\n```text\n! PYTHONPATH=\".apache-magpie-local:$(git rev-parse --git-common-dir)/../.apache-magpie-local:$(git rev-parse --git-common-dir)/apache-magpie\" python3 -m setup_preflight.welcome show --family pr-management --color=always\n```\n",
"max_shown": 3,
"reason": "first-run",
"shown": 0
},
"scope": "family",
"section": "welcome"
}
],
"rules": {
"welcome": "## welcome \u2014 a first-run welcome guide is due\n\nA welcome guide is due on this run: `facts.family` names it\n(`general`, for the first Magpie skill this user runs here, or the skill's own family),\nand `facts.reason` says why (`first-run`, `again`, or `upgraded` after a dismissal).\n`facts.guide` is the guide. The checker shows at most one guide per run and has already picked it.\n\n1. **Show `facts.guide` to the user verbatim**, as Markdown, before the work they asked for.\n Do not summarise, reword, trim, or add to it, and substitute nothing in it:\n its placeholders (`<security-list>`, `<tracker>`, `<family>`, \u2026) are meant to be read as written.\n2. **Offer the three choices in one line**:\n **Continue** (show again next time) \u00b7 **Dismiss** (hide until the next Magpie upgrade) \u00b7 **Don't show again**.\n3. **Record `seen` at once**, which is what no answer means, then carry on with the user's request in the same turn.\n Never block the requested work on the guide, and never wait for an answer before starting it.\n4. **When the user picks Dismiss or Don't show again** \u2014 now or later in the session \u2014 record that choice;\n it replaces `seen`. Continue needs nothing further.\n\n```bash\nPYTHONPATH=\".apache-magpie-local:$(git rev-parse --git-common-dir)/../.apache-magpie-local:$(git rev-parse --git-common-dir)/apache-magpie\" \\\n python3 -m setup_preflight.welcome record --family <facts.family> --choice <seen|dismiss|never>\n```\n\n`seen` brings the guide back on a later run, `facts.max_shown` recorded showings in all (`facts.shown` so far);\n`dismiss` hides it until the framework version changes; `never` hides it until re-enabled.\nEvery write merges into the personal layer's `reconciled.json` under `welcome.<facts.family>`, keeping every other key;\nnever write it by hand.\nIf the command exits non-zero, say nothing more about it and carry on.\n\nAny guide can be shown again on request with `/magpie-setup:welcome-guide [<family>]`,\nwhich also turns a hidden one back on."
},
"verdict": "action"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"lock": null,
"plugins": [
[
"magpie-setup",
"0.4.0"
],
[
"magpie-pr-management",
"0.4.0"
]
],
"reconciled": {
"welcome": {
"general": {
"choice": "seen",
"shown": 1
}
}
},
"skill": "pr-triage",
"welcome": true,
"hash": "sha256:54dc4ba1be7b36d4"
}
Comment thread
leninworld marked this conversation as resolved.
Loading