From c4e34c4bfff6c7aa787c6cb217c316716bcdbfff Mon Sep 17 00:00:00 2001 From: Claudius Agent Date: Tue, 21 Jul 2026 09:22:44 +0000 Subject: [PATCH 1/6] docs(git-and-github): plain-language-first PR body template Replace the "Why this PR exists" skeleton with TL;DR -> User story -> Reproduction scenario (Base flow / Actual behavior / Expected behavior) -> Detailed discussion. The first four sections stay jargon-free for a technical PM or external reviewer; implementation detail (What was done/Testing/Breaking changes/Checklist/Attribution) now nests under Detailed discussion for implementors and AI agents. push/SKILL.md and gh-cli-fallback.md updated to match; the pinning test rewritten for the new contract. Co-Authored-By: Claude Sonnet 5 --- .claude-plugin/plugin.json | 2 +- CHANGELOG.md | 6 ++ skills/git-and-github/SKILL.md | 43 ++++++--- .../references/gh-cli-fallback.md | 36 ++++++-- skills/push/SKILL.md | 2 +- tests/test_pr_body_template.py | 87 +++++++++++++------ 6 files changed, 123 insertions(+), 53 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3faec6c..64d7ec3 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "claudius", - "version": "5.12.1", + "version": "5.13.0", "description": "Collection of specialized development agents and skills for Claude Code", "author": { "name": "lklimek", diff --git a/CHANGELOG.md b/CHANGELOG.md index ee236aa..9826639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use ## [Unreleased] +## [5.13.0] - 2026-07-21 + +### Changed + +- **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\**, I want to \, to achieve \.") -> `## Reproduction scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first four sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. `skills/push/SKILL.md` and `skills/git-and-github/references/gh-cli-fallback.md` updated to match. Pinned by `tests/test_pr_body_template.py`. + ## [5.12.1] - 2026-07-20 ### Fixed diff --git a/skills/git-and-github/SKILL.md b/skills/git-and-github/SKILL.md index 4c75db7..923cbeb 100644 --- a/skills/git-and-github/SKILL.md +++ b/skills/git-and-github/SKILL.md @@ -57,26 +57,41 @@ Always ask explicit confirmation before every push, even if the user agreed earl ### Creating a PR -Check for a PR template first. If a template exists, read and fill it in. When applicable, include an informal user story (what the user can achieve, no technical details -- start with "Imagine you are..."). +Check for a PR template first. If a template exists, read and fill it in, folding its required content into the skeleton below rather than replacing it. -The PR body **must lead with a `## Why this PR exists` section** — reviewers read motivation before mechanics. Use this skeleton (drop empty sections): +The PR body **must lead with a plain-language summary before any implementation detail** — a technical product manager or an external reviewer with no code context must understand the first four sections at a glance. Use this skeleton (drop whole sections or sub-sections that don't apply, keep the section order): ```markdown -## Why this PR exists -- **Problem**: 1-2 plain-language sentences on what's broken or missing. -- **What breaks without it**: a concrete reproduction or threat scenario — numbered steps or a short narrative showing the actual failure/misbehaviour, not an abstract claim. -- **Blocking relationship**: prerequisite for / depends on / stacked atop PR #N, if any. - -## What was done -## Testing -## Breaking changes -## Checklist -## Attribution +**TL;DR:** + +## User story +As a ****, I want to , to achieve . + +## Reproduction scenario +### Base flow + + +### Actual behavior + + +### Expected behavior + + +## Detailed discussion +### What was done +### Testing +### Breaking changes +### Checklist +### Attribution ``` -`Why this PR exists` comes first; the remaining sections follow in that order. Always create PRs as drafts. +**`TL;DR` / `User story` / `Reproduction scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a ``..." shape as the Issues `### User Story` below, phrased for a change already made. `Reproduction scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Reproduction scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. + +**`Detailed discussion` is for implementors and AI agents** — it may get as technical as needed: problem/rationale, code-level specifics, and the sub-sections above. + +`TL;DR` → `User story` → `Reproduction scenario` → `Detailed discussion`, in that order. Always create PRs as drafts. -**PR descriptions describe net final state only** — no development history, changelog, or step-by-step iteration/debugging narrative; that belongs in commit messages. Concise final testing/verification results (the `## Testing` section) describe the final state, not history, and are expected. +**PR descriptions describe net final state only** — no development history, changelog, or step-by-step iteration/debugging narrative; that belongs in commit messages. This doesn't conflict with `### Actual behavior`, which describes the pre-existing problem being solved, not the PR's own iteration history. Concise final testing/verification results (the `### Testing` sub-section) describe the final state, not history, and are expected. ### Reviewing a PR diff --git a/skills/git-and-github/references/gh-cli-fallback.md b/skills/git-and-github/references/gh-cli-fallback.md index 4b5f296..f62be4f 100644 --- a/skills/git-and-github/references/gh-cli-fallback.md +++ b/skills/git-and-github/references/gh-cli-fallback.md @@ -12,38 +12,56 @@ Check for a PR template first: git ls-tree HEAD --name-only -r .github/ | grep -i pull_request_template ``` -If a template exists, read and fill it in. When applicable, include an informal user story (what the user can achieve, no technical details -- start with "Imagine you are..."). +If a template exists, read and fill it in, folding its required content into the skeleton below. See the main skill's §Creating a PR for the full rationale (plain language up top, technical detail in `Detailed discussion`). Always create PRs as drafts: ```bash gh pr create --draft --title ": " --body "$(cat <<'EOF' -## Issue being fixed or feature implemented +**TL;DR:** -### User Story +## User story -### Details +As a ****, I want to , to achieve . -Closes # +## Reproduction scenario + +### Base flow + + + +### Actual behavior + + + +### Expected behavior -## What was done? + + +## Detailed discussion + +### What was done -## How has this been tested? +Closes # + +### Testing -## Breaking Changes +### Breaking changes None -## Checklist +### Checklist - [x] I have performed a self-review of my own code - [x] I have added or updated relevant tests - [x] I have made corresponding changes to the documentation if needed +### Attribution + 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent EOF )" diff --git a/skills/push/SKILL.md b/skills/push/SKILL.md index ae533ad..e25ef3b 100644 --- a/skills/push/SKILL.md +++ b/skills/push/SKILL.md @@ -30,7 +30,7 @@ Load `claudius:git-and-github` skill first — all commit, push, PR, and attribu 4. **Push** to remote 5. **PR** - - PR body MUST lead with a "Why this PR exists" section per `git-and-github` §Creating a PR + - PR body MUST follow the TL;DR → User story → Reproduction scenario → Detailed discussion skeleton per `git-and-github` §Creating a PR - If PR exists for this branch: update its title and description to reflect current changes - If no PR: create a draft PR with summary + test plan per `git-and-github` diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index db6a1b4..0131247 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -1,10 +1,11 @@ -"""Regression guard: the canonical PR-body template must lead with "Why this PR exists". +"""Regression guard: the canonical PR-body template leads with a plain-language summary. PR descriptions are owned by ONE skill (`git-and-github`); `push` delegates to it. -This test pins the contract so a refactor can't silently demote the rationale section -below the mechanics: `git-and-github/SKILL.md` must define a "Why this PR exists" -heading positioned AHEAD of the "What was done"/"Testing" sections, and `push/SKILL.md` -must reference it rather than inlining a duplicate template. +This test pins the contract so a refactor can't silently demote the human-readable +sections below implementation detail: `git-and-github/SKILL.md` must define, in +order, `TL;DR` -> `## User story` -> `## Reproduction scenario` (with `### Base flow`, +`### Actual behavior`, `### Expected behavior`) -> `## Detailed discussion`, and +`push/SKILL.md` must reference that skeleton rather than inlining a duplicate. """ from __future__ import annotations @@ -15,44 +16,74 @@ GIT_GITHUB = REPO_ROOT / "skills" / "git-and-github" / "SKILL.md" PUSH = REPO_ROOT / "skills" / "push" / "SKILL.md" -WHY = "Why this PR exists" +USER_STORY = "## User story" +REPRO = "## Reproduction scenario" +DETAILED = "## Detailed discussion" -def test_git_github_has_why_section() -> None: +def test_git_github_has_required_headings() -> None: text = GIT_GITHUB.read_text(encoding="utf-8") - assert f"## {WHY}" in text, ( - f"{GIT_GITHUB}: missing '## {WHY}' heading in PR-body template" - ) + for heading in ( + "**TL;DR:**", + USER_STORY, + REPRO, + "### Base flow", + "### Actual behavior", + "### Expected behavior", + DETAILED, + ): + assert heading in text, f"{GIT_GITHUB}: missing '{heading}' in PR-body template" -def test_why_section_leads_what_and_testing() -> None: +def test_sections_are_ordered() -> None: text = GIT_GITHUB.read_text(encoding="utf-8") - why = text.index(f"## {WHY}") - what = text.index("## What was done") - testing = text.index("## Testing") - assert why < what, f"{GIT_GITHUB}: '{WHY}' must precede 'What was done'" - assert why < testing, f"{GIT_GITHUB}: '{WHY}' must precede 'Testing'" + tldr = text.index("**TL;DR:**") + user_story = text.index(USER_STORY) + repro = text.index(REPRO) + base_flow = text.index("### Base flow") + actual = text.index("### Actual behavior") + expected = text.index("### Expected behavior") + detailed = text.index(DETAILED) + assert tldr < user_story < repro < detailed, ( + f"{GIT_GITHUB}: sections must appear in order TL;DR -> User story -> " + "Reproduction scenario -> Detailed discussion" + ) + assert repro < base_flow < actual < expected < detailed, ( + f"{GIT_GITHUB}: 'Reproduction scenario' sub-sections must appear in order " + "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" + ) -def test_why_section_demands_reproduction_and_blocking() -> None: - """The skeleton must prompt for a concrete repro/threat scenario and blocking relationship.""" +def test_user_facing_sections_demand_plain_language() -> None: + """TL;DR / User story / Reproduction scenario must be scoped to plain, user-observable info.""" text = GIT_GITHUB.read_text(encoding="utf-8") lowered = text.lower() - assert "reproduction" in lowered or "threat scenario" in lowered, ( - f"{GIT_GITHUB}: '{WHY}' skeleton must ask for a reproduction or threat scenario" + assert "plain language" in lowered or "plain-language" in lowered, ( + f"{GIT_GITHUB}: template must require plain language in the user-facing sections" ) - assert "blocking" in lowered, ( - f"{GIT_GITHUB}: '{WHY}' skeleton must ask for the blocking relationship" + assert "user-observable" in lowered, ( + f"{GIT_GITHUB}: template must scope user-facing sections to user-observable behavior" ) -def test_push_references_why_without_inlining_template() -> None: +def test_detailed_discussion_holds_implementation_content() -> None: + """Former 'Why this PR exists' content (What was done/Testing/etc.) now lives under Detailed discussion.""" + text = GIT_GITHUB.read_text(encoding="utf-8") + detailed = text.index(DETAILED) + for sub_heading in ("### What was done", "### Testing", "### Breaking changes", "### Checklist", "### Attribution"): + pos = text.index(sub_heading) + assert pos > detailed, ( + f"{GIT_GITHUB}: '{sub_heading}' must be nested under '{DETAILED}'" + ) + + +def test_push_references_skeleton_without_inlining_it() -> None: text = PUSH.read_text(encoding="utf-8") - assert WHY in text, f"{PUSH}: must reference the '{WHY}' section" assert "git-and-github" in text, ( f"{PUSH}: must delegate to git-and-github for the template" ) - # No duplicated skeleton: push references the section, it doesn't redefine the heading. - assert f"## {WHY}" not in text, ( - f"{PUSH}: must not inline a duplicate '## {WHY}' template — delegate to git-and-github" - ) + # No duplicated skeleton: push references the sections, it doesn't redefine the headings. + for heading in (USER_STORY, REPRO, DETAILED): + assert heading not in text, ( + f"{PUSH}: must not inline a duplicate '{heading}' template — delegate to git-and-github" + ) From a9e7e0524ce24dfbe06f1d25af19025a06423b47 Mon Sep 17 00:00:00 2001 From: Claudius Agent Date: Tue, 21 Jul 2026 09:27:44 +0000 Subject: [PATCH 2/6] docs(git-and-github): rename PR-body "Reproduction scenario" to "Scenario" "Reproduction" presumes a bug and reads oddly on feature PRs (nothing is being reproduced). "Scenario" keeps the Base flow/Actual/Expected behavior substructure and applies equally to bugs and features. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 2 +- skills/git-and-github/SKILL.md | 6 +++--- .../references/gh-cli-fallback.md | 2 +- skills/push/SKILL.md | 2 +- tests/test_pr_body_template.py | 20 +++++++++---------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9826639..bd0e0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use ### Changed -- **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\**, I want to \, to achieve \.") -> `## Reproduction scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first four sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. `skills/push/SKILL.md` and `skills/git-and-github/references/gh-cli-fallback.md` updated to match. Pinned by `tests/test_pr_body_template.py`. +- **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\**, I want to \, to achieve \.") -> `## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first four sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. `skills/push/SKILL.md` and `skills/git-and-github/references/gh-cli-fallback.md` updated to match. Pinned by `tests/test_pr_body_template.py`. ## [5.12.1] - 2026-07-20 diff --git a/skills/git-and-github/SKILL.md b/skills/git-and-github/SKILL.md index 923cbeb..fa9d70b 100644 --- a/skills/git-and-github/SKILL.md +++ b/skills/git-and-github/SKILL.md @@ -67,7 +67,7 @@ The PR body **must lead with a plain-language summary before any implementation ## User story As a ****, I want to , to achieve . -## Reproduction scenario +## Scenario ### Base flow @@ -85,11 +85,11 @@ As a ****, I want to , to achieve . ### Attribution ``` -**`TL;DR` / `User story` / `Reproduction scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a ``..." shape as the Issues `### User Story` below, phrased for a change already made. `Reproduction scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Reproduction scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. +**`TL;DR` / `User story` / `Scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a ``..." shape as the Issues `### User Story` below, phrased for a change already made. `Scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. **`Detailed discussion` is for implementors and AI agents** — it may get as technical as needed: problem/rationale, code-level specifics, and the sub-sections above. -`TL;DR` → `User story` → `Reproduction scenario` → `Detailed discussion`, in that order. Always create PRs as drafts. +`TL;DR` → `User story` → `Scenario` → `Detailed discussion`, in that order. Always create PRs as drafts. **PR descriptions describe net final state only** — no development history, changelog, or step-by-step iteration/debugging narrative; that belongs in commit messages. This doesn't conflict with `### Actual behavior`, which describes the pre-existing problem being solved, not the PR's own iteration history. Concise final testing/verification results (the `### Testing` sub-section) describe the final state, not history, and are expected. diff --git a/skills/git-and-github/references/gh-cli-fallback.md b/skills/git-and-github/references/gh-cli-fallback.md index f62be4f..e54d2e6 100644 --- a/skills/git-and-github/references/gh-cli-fallback.md +++ b/skills/git-and-github/references/gh-cli-fallback.md @@ -24,7 +24,7 @@ gh pr create --draft --title ": " --body "$(cat <<'EOF' As a ****, I want to , to achieve . -## Reproduction scenario +## Scenario ### Base flow diff --git a/skills/push/SKILL.md b/skills/push/SKILL.md index e25ef3b..c71d15f 100644 --- a/skills/push/SKILL.md +++ b/skills/push/SKILL.md @@ -30,7 +30,7 @@ Load `claudius:git-and-github` skill first — all commit, push, PR, and attribu 4. **Push** to remote 5. **PR** - - PR body MUST follow the TL;DR → User story → Reproduction scenario → Detailed discussion skeleton per `git-and-github` §Creating a PR + - PR body MUST follow the TL;DR → User story → Scenario → Detailed discussion skeleton per `git-and-github` §Creating a PR - If PR exists for this branch: update its title and description to reflect current changes - If no PR: create a draft PR with summary + test plan per `git-and-github` diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index 0131247..1020004 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -3,7 +3,7 @@ PR descriptions are owned by ONE skill (`git-and-github`); `push` delegates to it. This test pins the contract so a refactor can't silently demote the human-readable sections below implementation detail: `git-and-github/SKILL.md` must define, in -order, `TL;DR` -> `## User story` -> `## Reproduction scenario` (with `### Base flow`, +order, `TL;DR` -> `## User story` -> `## Scenario` (with `### Base flow`, `### Actual behavior`, `### Expected behavior`) -> `## Detailed discussion`, and `push/SKILL.md` must reference that skeleton rather than inlining a duplicate. """ @@ -17,7 +17,7 @@ PUSH = REPO_ROOT / "skills" / "push" / "SKILL.md" USER_STORY = "## User story" -REPRO = "## Reproduction scenario" +SCENARIO = "## Scenario" DETAILED = "## Detailed discussion" @@ -26,7 +26,7 @@ def test_git_github_has_required_headings() -> None: for heading in ( "**TL;DR:**", USER_STORY, - REPRO, + SCENARIO, "### Base flow", "### Actual behavior", "### Expected behavior", @@ -39,23 +39,23 @@ def test_sections_are_ordered() -> None: text = GIT_GITHUB.read_text(encoding="utf-8") tldr = text.index("**TL;DR:**") user_story = text.index(USER_STORY) - repro = text.index(REPRO) + scenario = text.index(SCENARIO) base_flow = text.index("### Base flow") actual = text.index("### Actual behavior") expected = text.index("### Expected behavior") detailed = text.index(DETAILED) - assert tldr < user_story < repro < detailed, ( + assert tldr < user_story < scenario < detailed, ( f"{GIT_GITHUB}: sections must appear in order TL;DR -> User story -> " - "Reproduction scenario -> Detailed discussion" + "Scenario -> Detailed discussion" ) - assert repro < base_flow < actual < expected < detailed, ( - f"{GIT_GITHUB}: 'Reproduction scenario' sub-sections must appear in order " + assert scenario < base_flow < actual < expected < detailed, ( + f"{GIT_GITHUB}: 'Scenario' sub-sections must appear in order " "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" ) def test_user_facing_sections_demand_plain_language() -> None: - """TL;DR / User story / Reproduction scenario must be scoped to plain, user-observable info.""" + """TL;DR / User story / Scenario must be scoped to plain, user-observable info.""" text = GIT_GITHUB.read_text(encoding="utf-8") lowered = text.lower() assert "plain language" in lowered or "plain-language" in lowered, ( @@ -83,7 +83,7 @@ def test_push_references_skeleton_without_inlining_it() -> None: f"{PUSH}: must delegate to git-and-github for the template" ) # No duplicated skeleton: push references the sections, it doesn't redefine the headings. - for heading in (USER_STORY, REPRO, DETAILED): + for heading in (USER_STORY, SCENARIO, DETAILED): assert heading not in text, ( f"{PUSH}: must not inline a duplicate '{heading}' template — delegate to git-and-github" ) From 8ac15b305e6dc1270b93bdbc341eb06c72a4b5dc Mon Sep 17 00:00:00 2001 From: Claudius Agent Date: Tue, 21 Jul 2026 09:42:03 +0000 Subject: [PATCH 3/6] docs(git-and-github): externalize PR/issue body templates, unify issue skeleton Move the literal PR-body skeleton out of SKILL.md prose into references/pr-body-template.md; SKILL.md and gh-cli-fallback.md now link to it instead of inlining a copy (gh CLI fallback fills the template and passes it via --body-file). Unify the Issues section onto the same TL;DR -> User story -> Scenario -> Detailed discussion shape as PRs (references/issue-body-template.md), replacing the old bare "feature/enhancement issues need a User Story" bullet. Scenario doubles as the bug-report reproduction; feature requests with nothing to reproduce may drop it. tests/test_pr_body_template.py repointed at the external template file; new tests/test_issue_body_template.py pins the issue template's contract. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 4 +- skills/git-and-github/SKILL.md | 34 +------ .../references/gh-cli-fallback.md | 59 ++--------- .../references/issue-body-template.md | 34 +++++++ .../references/pr-body-template.md | 32 ++++++ tests/test_issue_body_template.py | 77 +++++++++++++++ tests/test_pr_body_template.py | 97 +++++++++++-------- 7 files changed, 214 insertions(+), 123 deletions(-) create mode 100644 skills/git-and-github/references/issue-body-template.md create mode 100644 skills/git-and-github/references/pr-body-template.md create mode 100644 tests/test_issue_body_template.py diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0e0d2..915d90c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use ### Changed -- **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\**, I want to \, to achieve \.") -> `## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first four sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. `skills/push/SKILL.md` and `skills/git-and-github/references/gh-cli-fallback.md` updated to match. Pinned by `tests/test_pr_body_template.py`. +- **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\**, I want to \, to achieve \.") -> `## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first three sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. +- **Issue-body template** (`skills/git-and-github/SKILL.md` §Issues): unified onto the same `TL;DR` -> `User story` -> `Scenario` -> `Detailed discussion` shape as PRs, replacing the old "feature/enhancement issues need a `### User Story`" bullet. For bug reports, `Scenario` doubles as the reproduction; feature requests with nothing to reproduce may drop it. +- **Both templates externalized**: the literal skeletons moved out of `SKILL.md` prose into `skills/git-and-github/references/pr-body-template.md` and `references/issue-body-template.md`; `SKILL.md` and `references/gh-cli-fallback.md` now link to them instead of inlining a copy (the `gh` CLI fallback now fills in the template file and passes it via `--body-file`); `push/SKILL.md` continues to delegate to `git-and-github` without inlining its own copy. Pinned by `tests/test_pr_body_template.py` and the new `tests/test_issue_body_template.py`. ## [5.12.1] - 2026-07-20 diff --git a/skills/git-and-github/SKILL.md b/skills/git-and-github/SKILL.md index fa9d70b..fcae6cd 100644 --- a/skills/git-and-github/SKILL.md +++ b/skills/git-and-github/SKILL.md @@ -57,35 +57,11 @@ Always ask explicit confirmation before every push, even if the user agreed earl ### Creating a PR -Check for a PR template first. If a template exists, read and fill it in, folding its required content into the skeleton below rather than replacing it. +Check for a PR template first. If a template exists, read and fill it in, folding its required content into the skeleton linked below rather than replacing it. -The PR body **must lead with a plain-language summary before any implementation detail** — a technical product manager or an external reviewer with no code context must understand the first four sections at a glance. Use this skeleton (drop whole sections or sub-sections that don't apply, keep the section order): +The PR body **must lead with a plain-language summary before any implementation detail** — a technical product manager or an external reviewer with no code context must understand everything before `Detailed discussion` at a glance. Copy [pr-body-template.md](references/pr-body-template.md) and fill it in. -```markdown -**TL;DR:** - -## User story -As a ****, I want to , to achieve . - -## Scenario -### Base flow - - -### Actual behavior - - -### Expected behavior - - -## Detailed discussion -### What was done -### Testing -### Breaking changes -### Checklist -### Attribution -``` - -**`TL;DR` / `User story` / `Scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a ``..." shape as the Issues `### User Story` below, phrased for a change already made. `Scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. +**`TL;DR` / `User story` / `Scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a ``..." shape as the Issues `User story` below, phrased for a change already made. `Scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. **`Detailed discussion` is for implementors and AI agents** — it may get as technical as needed: problem/rationale, code-level specifics, and the sub-sections above. @@ -101,9 +77,9 @@ See [pr-review.md](references/pr-review.md) for the full procedure: fetching PR ### Issues -Before creating, search existing issues (open + closed) and PRs for duplicates. If found, show to user and ask before proceeding. Check for issue templates. Always append attribution footer. +Before creating, search existing issues (open + closed) and PRs for duplicates. If found, show to user and ask before proceeding. Check for issue templates first; if one exists, fold its required content into the skeleton linked below rather than replacing it. -**Feature/enhancement issues must include a `### User Story` section** -- an informal story describing what the user can achieve (no technical details). Start with "As a **\**, I want to ... so that ...". Multiple personas are fine. Place user stories before technical details. +Issue bodies use the same plain-language-first skeleton as PRs (see §Creating a PR for the full rationale). Copy [issue-body-template.md](references/issue-body-template.md) and fill it in: `TL;DR` → `User story` → `Scenario` → `Detailed discussion`. `User story` uses the same "As a **\**, I want to ..., to achieve ..." shape as PRs — multiple personas are fine, repeat the line. Always append the attribution footer. ## Safety Rules diff --git a/skills/git-and-github/references/gh-cli-fallback.md b/skills/git-and-github/references/gh-cli-fallback.md index e54d2e6..8500c24 100644 --- a/skills/git-and-github/references/gh-cli-fallback.md +++ b/skills/git-and-github/references/gh-cli-fallback.md @@ -12,59 +12,12 @@ Check for a PR template first: git ls-tree HEAD --name-only -r .github/ | grep -i pull_request_template ``` -If a template exists, read and fill it in, folding its required content into the skeleton below. See the main skill's §Creating a PR for the full rationale (plain language up top, technical detail in `Detailed discussion`). +If a template exists, read and fill it in, folding its required content into the linked skeleton below. See the main skill's §Creating a PR for the full rationale (plain language up top, technical detail in `Detailed discussion`). -Always create PRs as drafts: +Fill in [pr-body-template.md](pr-body-template.md), save the completed body to a file, then create the PR as a draft, passing the body via `--body-file` (not `--body` — avoids shell-escaping the whole skeleton inline): ```bash -gh pr create --draft --title ": " --body "$(cat <<'EOF' -**TL;DR:** - -## User story - -As a ****, I want to , to achieve . - -## Scenario - -### Base flow - - - -### Actual behavior - - - -### Expected behavior - - - -## Detailed discussion - -### What was done - - - -Closes # - -### Testing - - - -### Breaking changes - -None - -### Checklist - -- [x] I have performed a self-review of my own code -- [x] I have added or updated relevant tests -- [x] I have made corresponding changes to the documentation if needed - -### Attribution - -🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent -EOF -)" +gh pr create --draft --title ": " --body-file /path/to/filled-in-pr-body.md ``` ## Reviewing a PR @@ -87,6 +40,12 @@ Check for issue templates before creating: git ls-tree HEAD --name-only -r .github/ | grep -i issue_template ``` +If none exists, fill in [issue-body-template.md](issue-body-template.md) (see the main skill's §Issues), save the completed body to a file, then: + +```bash +gh issue create --title "" --body-file /path/to/filled-in-issue-body.md +``` + ## Elevated Permissions (ghsudo) -- Optional Fallback If you use a **read-only default token** with `gh`, install [ghsudo](https://github.com/lklimek/ghsudo) (`pip install ghsudo`) for write operations. When a `gh` or `git` command fails with HTTP 403 (Forbidden), 404 (Not Found), or "Resource not accessible", re-run it through ghsudo. **Never fork the repository** — forking creates a separate repo and breaks push/PR workflows. GitHub may return 404 instead of 403 for private resources when the token lacks sufficient permissions. diff --git a/skills/git-and-github/references/issue-body-template.md b/skills/git-and-github/references/issue-body-template.md new file mode 100644 index 0000000..dfe3b88 --- /dev/null +++ b/skills/git-and-github/references/issue-body-template.md @@ -0,0 +1,34 @@ +# Issue Body Template + +Canonical issue-body skeleton for `git-and-github` §Issues — same plain-language-first +shape as [`pr-body-template.md`](pr-body-template.md). Drop whole sections or sub-sections +that don't apply, keep the section order. `TL;DR` / `User story` / `Scenario` are +plain-language and user-facing; `Detailed discussion` is technical, for implementors and +AI agents. + +For a bug report, `Scenario` is the reproduction: `Base flow` gets there, `Actual behavior` +is the bug, `Expected behavior` is the fix target. For a feature request with nothing to +reproduce, drop `Scenario` and describe the gap in `User story` / `Detailed discussion` +instead. + +```markdown +**TL;DR:** <one plain-language sentence describing the problem or request> + +## User story +As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. + +## Scenario +### Base flow +<the ordinary steps that lead to this situation — plain narrative> + +### Actual behavior +<what happens today — the bug, gap, or missing capability> + +### Expected behavior +<what should happen, or become possible, once this is addressed> + +## Detailed discussion +<technical notes, proposed approach, logs, links> + +### Attribution +``` diff --git a/skills/git-and-github/references/pr-body-template.md b/skills/git-and-github/references/pr-body-template.md new file mode 100644 index 0000000..163a46f --- /dev/null +++ b/skills/git-and-github/references/pr-body-template.md @@ -0,0 +1,32 @@ +# PR Body Template + +Canonical PR-body skeleton for `git-and-github` §Creating a PR. Drop whole sections or +sub-sections that don't apply, keep the section order. `TL;DR` / `User story` / `Scenario` +are plain-language and user-facing; `Detailed discussion` is technical, for implementors +and AI agents. + +```markdown +**TL;DR:** <one plain-language sentence describing what this PR does> + +## User story +As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. + +## Scenario +### Base flow +<the ordinary steps that lead to this situation — plain narrative> + +### Actual behavior +<what happens today — the bug, gap, or missing capability> + +### Expected behavior +<what should happen, or become possible, after this PR> + +## Detailed discussion +### What was done +<description of changes; add `Closes #<issue-number>` here if applicable> + +### Testing +### Breaking changes +### Checklist +### Attribution +``` diff --git a/tests/test_issue_body_template.py b/tests/test_issue_body_template.py new file mode 100644 index 0000000..b19614e --- /dev/null +++ b/tests/test_issue_body_template.py @@ -0,0 +1,77 @@ +"""Regression guard: the canonical issue-body template lives in one external file. + +Mirrors `test_pr_body_template.py`: the literal skeleton lives ONLY in +`skills/git-and-github/references/issue-body-template.md`; `git-and-github/SKILL.md` +must reference it (§Issues), not inline a duplicate. Pins the template's heading +set/ordering — same plain-language-first shape as the PR-body template. +""" + +from __future__ import annotations + +import re +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +GIT_GITHUB = REPO_ROOT / "skills" / "git-and-github" / "SKILL.md" +TEMPLATE = REPO_ROOT / "skills" / "git-and-github" / "references" / "issue-body-template.md" + +USER_STORY = "## User story" +SCENARIO = "## Scenario" +DETAILED = "## Detailed discussion" + +SKELETON_HEADINGS = ( + "**TL;DR:**", + USER_STORY, + SCENARIO, + "### Base flow", + "### Actual behavior", + "### Expected behavior", + DETAILED, + "### Attribution", +) + + +def test_template_has_required_headings() -> None: + text = TEMPLATE.read_text(encoding="utf-8") + for heading in SKELETON_HEADINGS: + assert heading in text, f"{TEMPLATE}: missing '{heading}'" + + +def test_template_sections_are_ordered() -> None: + text = TEMPLATE.read_text(encoding="utf-8") + tldr = text.index("**TL;DR:**") + user_story = text.index(USER_STORY) + scenario = text.index(SCENARIO) + base_flow = text.index("### Base flow") + actual = text.index("### Actual behavior") + expected = text.index("### Expected behavior") + detailed = text.index(DETAILED) + attribution = text.index("### Attribution") + assert tldr < user_story < scenario < detailed, ( + f"{TEMPLATE}: sections must appear in order TL;DR -> User story -> Scenario -> Detailed discussion" + ) + assert scenario < base_flow < actual < expected < detailed, ( + f"{TEMPLATE}: 'Scenario' sub-sections must appear in order " + "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" + ) + assert detailed < attribution, ( + f"{TEMPLATE}: 'Attribution' must be nested under 'Detailed discussion'" + ) + + +def test_skill_references_template_without_inlining_it() -> None: + text = GIT_GITHUB.read_text(encoding="utf-8") + assert "references/issue-body-template.md" in text, ( + f"{GIT_GITHUB}: §Issues must link to references/issue-body-template.md" + ) + + +def test_skill_issues_section_scoped_to_shared_template() -> None: + """§Issues should point at the shared skeleton, not restate a bespoke one.""" + text = GIT_GITHUB.read_text(encoding="utf-8") + issues_heading = text.index("### Issues") + issues_section = text[issues_heading:] + for heading in SKELETON_HEADINGS: + assert re.search(r"^" + re.escape(heading), issues_section, re.MULTILINE) is None, ( + f"{GIT_GITHUB}: §Issues must not inline a duplicate '{heading}' heading — delegate to issue-body-template.md" + ) diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index 1020004..b62e0a9 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -1,42 +1,50 @@ -"""Regression guard: the canonical PR-body template leads with a plain-language summary. - -PR descriptions are owned by ONE skill (`git-and-github`); `push` delegates to it. -This test pins the contract so a refactor can't silently demote the human-readable -sections below implementation detail: `git-and-github/SKILL.md` must define, in -order, `TL;DR` -> `## User story` -> `## Scenario` (with `### Base flow`, -`### Actual behavior`, `### Expected behavior`) -> `## Detailed discussion`, and -`push/SKILL.md` must reference that skeleton rather than inlining a duplicate. +"""Regression guard: the canonical PR-body template lives in one external file. + +The literal skeleton lives ONLY in `skills/git-and-github/references/pr-body-template.md`; +`git-and-github/SKILL.md` and `push/SKILL.md` must reference it, not inline a duplicate. +This test pins the template's heading set/ordering (`TL;DR` -> `## User story` -> +`## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> +`## Detailed discussion`) and guards against the skeleton drifting back into the skills. """ from __future__ import annotations +import re from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent GIT_GITHUB = REPO_ROOT / "skills" / "git-and-github" / "SKILL.md" PUSH = REPO_ROOT / "skills" / "push" / "SKILL.md" +TEMPLATE = REPO_ROOT / "skills" / "git-and-github" / "references" / "pr-body-template.md" USER_STORY = "## User story" SCENARIO = "## Scenario" DETAILED = "## Detailed discussion" +SKELETON_HEADINGS = ( + "**TL;DR:**", + USER_STORY, + SCENARIO, + "### Base flow", + "### Actual behavior", + "### Expected behavior", + DETAILED, + "### What was done", + "### Testing", + "### Breaking changes", + "### Checklist", + "### Attribution", +) -def test_git_github_has_required_headings() -> None: - text = GIT_GITHUB.read_text(encoding="utf-8") - for heading in ( - "**TL;DR:**", - USER_STORY, - SCENARIO, - "### Base flow", - "### Actual behavior", - "### Expected behavior", - DETAILED, - ): - assert heading in text, f"{GIT_GITHUB}: missing '{heading}' in PR-body template" - - -def test_sections_are_ordered() -> None: - text = GIT_GITHUB.read_text(encoding="utf-8") + +def test_template_has_required_headings() -> None: + text = TEMPLATE.read_text(encoding="utf-8") + for heading in SKELETON_HEADINGS: + assert heading in text, f"{TEMPLATE}: missing '{heading}'" + + +def test_template_sections_are_ordered() -> None: + text = TEMPLATE.read_text(encoding="utf-8") tldr = text.index("**TL;DR:**") user_story = text.index(USER_STORY) scenario = text.index(SCENARIO) @@ -45,45 +53,48 @@ def test_sections_are_ordered() -> None: expected = text.index("### Expected behavior") detailed = text.index(DETAILED) assert tldr < user_story < scenario < detailed, ( - f"{GIT_GITHUB}: sections must appear in order TL;DR -> User story -> " - "Scenario -> Detailed discussion" + f"{TEMPLATE}: sections must appear in order TL;DR -> User story -> Scenario -> Detailed discussion" ) assert scenario < base_flow < actual < expected < detailed, ( - f"{GIT_GITHUB}: 'Scenario' sub-sections must appear in order " + f"{TEMPLATE}: 'Scenario' sub-sections must appear in order " "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" ) -def test_user_facing_sections_demand_plain_language() -> None: +def _heading_at_line_start(text: str, heading: str) -> bool: + """True if `heading` appears as its own line (a real heading), not just quoted in prose.""" + return re.search(r"^" + re.escape(heading), text, re.MULTILINE) is not None + + +def test_skill_references_template_without_inlining_it() -> None: + text = GIT_GITHUB.read_text(encoding="utf-8") + assert "references/pr-body-template.md" in text, ( + f"{GIT_GITHUB}: must link to references/pr-body-template.md" + ) + for heading in SKELETON_HEADINGS: + assert not _heading_at_line_start(text, heading), ( + f"{GIT_GITHUB}: must not inline a duplicate '{heading}' heading — delegate to pr-body-template.md" + ) + + +def test_skill_demands_plain_language_for_user_facing_sections() -> None: """TL;DR / User story / Scenario must be scoped to plain, user-observable info.""" text = GIT_GITHUB.read_text(encoding="utf-8") lowered = text.lower() assert "plain language" in lowered or "plain-language" in lowered, ( - f"{GIT_GITHUB}: template must require plain language in the user-facing sections" + f"{GIT_GITHUB}: must require plain language in the user-facing PR sections" ) assert "user-observable" in lowered, ( - f"{GIT_GITHUB}: template must scope user-facing sections to user-observable behavior" + f"{GIT_GITHUB}: must scope user-facing PR sections to user-observable behavior" ) -def test_detailed_discussion_holds_implementation_content() -> None: - """Former 'Why this PR exists' content (What was done/Testing/etc.) now lives under Detailed discussion.""" - text = GIT_GITHUB.read_text(encoding="utf-8") - detailed = text.index(DETAILED) - for sub_heading in ("### What was done", "### Testing", "### Breaking changes", "### Checklist", "### Attribution"): - pos = text.index(sub_heading) - assert pos > detailed, ( - f"{GIT_GITHUB}: '{sub_heading}' must be nested under '{DETAILED}'" - ) - - def test_push_references_skeleton_without_inlining_it() -> None: text = PUSH.read_text(encoding="utf-8") assert "git-and-github" in text, ( f"{PUSH}: must delegate to git-and-github for the template" ) - # No duplicated skeleton: push references the sections, it doesn't redefine the headings. for heading in (USER_STORY, SCENARIO, DETAILED): - assert heading not in text, ( + assert not _heading_at_line_start(text, heading), ( f"{PUSH}: must not inline a duplicate '{heading}' template — delegate to git-and-github" ) From fc7623e6d86f86b0f13efc39b6a00a57b93bc50f Mon Sep 17 00:00:00 2001 From: Claudius Agent <noreply@anthropic.com> Date: Tue, 21 Jul 2026 09:47:03 +0000 Subject: [PATCH 4/6] docs(git-and-github): add trailing "Prior work" section to both templates Links to possibly related/similar PRs or issues, each with a one-sentence summary of how it relates to the one being filed. Last section in both pr-body-template.md and issue-body-template.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --- CHANGELOG.md | 1 + skills/git-and-github/references/issue-body-template.md | 3 +++ skills/git-and-github/references/pr-body-template.md | 3 +++ tests/test_issue_body_template.py | 5 +++++ tests/test_pr_body_template.py | 5 +++++ 5 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915d90c..0bf7978 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use - **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\<role\>**, I want to \<what-to-do\>, to achieve \<user-goal\>.") -> `## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first three sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. - **Issue-body template** (`skills/git-and-github/SKILL.md` §Issues): unified onto the same `TL;DR` -> `User story` -> `Scenario` -> `Detailed discussion` shape as PRs, replacing the old "feature/enhancement issues need a `### User Story`" bullet. For bug reports, `Scenario` doubles as the reproduction; feature requests with nothing to reproduce may drop it. - **Both templates externalized**: the literal skeletons moved out of `SKILL.md` prose into `skills/git-and-github/references/pr-body-template.md` and `references/issue-body-template.md`; `SKILL.md` and `references/gh-cli-fallback.md` now link to them instead of inlining a copy (the `gh` CLI fallback now fills in the template file and passes it via `--body-file`); `push/SKILL.md` continues to delegate to `git-and-github` without inlining its own copy. Pinned by `tests/test_pr_body_template.py` and the new `tests/test_issue_body_template.py`. +- Both templates gained a trailing `### Prior work` section — links to possibly related/similar PRs or issues, each with a one-sentence summary of how it relates to the one being filed. ## [5.12.1] - 2026-07-20 diff --git a/skills/git-and-github/references/issue-body-template.md b/skills/git-and-github/references/issue-body-template.md index dfe3b88..7879dde 100644 --- a/skills/git-and-github/references/issue-body-template.md +++ b/skills/git-and-github/references/issue-body-template.md @@ -31,4 +31,7 @@ As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. <technical notes, proposed approach, logs, links> ### Attribution + +### Prior work +<links to possibly related or similar PRs/issues, each with a one-sentence summary of how it relates to this one; omit if none> ``` diff --git a/skills/git-and-github/references/pr-body-template.md b/skills/git-and-github/references/pr-body-template.md index 163a46f..060ad65 100644 --- a/skills/git-and-github/references/pr-body-template.md +++ b/skills/git-and-github/references/pr-body-template.md @@ -29,4 +29,7 @@ As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. ### Breaking changes ### Checklist ### Attribution + +### Prior work +<links to possibly related or similar PRs/issues, each with a one-sentence summary of how it relates to this one; omit if none> ``` diff --git a/tests/test_issue_body_template.py b/tests/test_issue_body_template.py index b19614e..eb3ea5f 100644 --- a/tests/test_issue_body_template.py +++ b/tests/test_issue_body_template.py @@ -28,6 +28,7 @@ "### Expected behavior", DETAILED, "### Attribution", + "### Prior work", ) @@ -47,6 +48,7 @@ def test_template_sections_are_ordered() -> None: expected = text.index("### Expected behavior") detailed = text.index(DETAILED) attribution = text.index("### Attribution") + prior_work = text.index("### Prior work") assert tldr < user_story < scenario < detailed, ( f"{TEMPLATE}: sections must appear in order TL;DR -> User story -> Scenario -> Detailed discussion" ) @@ -57,6 +59,9 @@ def test_template_sections_are_ordered() -> None: assert detailed < attribution, ( f"{TEMPLATE}: 'Attribution' must be nested under 'Detailed discussion'" ) + assert prior_work == max(text.index(h) for h in SKELETON_HEADINGS), ( + f"{TEMPLATE}: 'Prior work' must be the last section in the skeleton" + ) def test_skill_references_template_without_inlining_it() -> None: diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index b62e0a9..56f90c1 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -34,6 +34,7 @@ "### Breaking changes", "### Checklist", "### Attribution", + "### Prior work", ) @@ -52,6 +53,7 @@ def test_template_sections_are_ordered() -> None: actual = text.index("### Actual behavior") expected = text.index("### Expected behavior") detailed = text.index(DETAILED) + prior_work = text.index("### Prior work") assert tldr < user_story < scenario < detailed, ( f"{TEMPLATE}: sections must appear in order TL;DR -> User story -> Scenario -> Detailed discussion" ) @@ -59,6 +61,9 @@ def test_template_sections_are_ordered() -> None: f"{TEMPLATE}: 'Scenario' sub-sections must appear in order " "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" ) + assert prior_work == max(text.index(h) for h in SKELETON_HEADINGS), ( + f"{TEMPLATE}: 'Prior work' must be the last section in the skeleton" + ) def _heading_at_line_start(text: str, heading: str) -> bool: From b9a8f178d47f29b64dc2b0559e20f019aaf1e713 Mon Sep 17 00:00:00 2001 From: Claudius Agent <noreply@anthropic.com> Date: Tue, 21 Jul 2026 09:51:33 +0000 Subject: [PATCH 5/6] style: ruff format test_pr_body_template.py, test_issue_body_template.py Fixes CI's ruff format --check failure on PR #76. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --- tests/test_issue_body_template.py | 8 ++++++-- tests/test_pr_body_template.py | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_issue_body_template.py b/tests/test_issue_body_template.py index eb3ea5f..d1fc25d 100644 --- a/tests/test_issue_body_template.py +++ b/tests/test_issue_body_template.py @@ -13,7 +13,9 @@ REPO_ROOT = Path(__file__).resolve().parent.parent GIT_GITHUB = REPO_ROOT / "skills" / "git-and-github" / "SKILL.md" -TEMPLATE = REPO_ROOT / "skills" / "git-and-github" / "references" / "issue-body-template.md" +TEMPLATE = ( + REPO_ROOT / "skills" / "git-and-github" / "references" / "issue-body-template.md" +) USER_STORY = "## User story" SCENARIO = "## Scenario" @@ -77,6 +79,8 @@ def test_skill_issues_section_scoped_to_shared_template() -> None: issues_heading = text.index("### Issues") issues_section = text[issues_heading:] for heading in SKELETON_HEADINGS: - assert re.search(r"^" + re.escape(heading), issues_section, re.MULTILINE) is None, ( + assert ( + re.search(r"^" + re.escape(heading), issues_section, re.MULTILINE) is None + ), ( f"{GIT_GITHUB}: §Issues must not inline a duplicate '{heading}' heading — delegate to issue-body-template.md" ) diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index 56f90c1..ccab564 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -15,7 +15,9 @@ REPO_ROOT = Path(__file__).resolve().parent.parent GIT_GITHUB = REPO_ROOT / "skills" / "git-and-github" / "SKILL.md" PUSH = REPO_ROOT / "skills" / "push" / "SKILL.md" -TEMPLATE = REPO_ROOT / "skills" / "git-and-github" / "references" / "pr-body-template.md" +TEMPLATE = ( + REPO_ROOT / "skills" / "git-and-github" / "references" / "pr-body-template.md" +) USER_STORY = "## User story" SCENARIO = "## Scenario" From 70b301fe6384687c5faec96dfc7e777497ea2b48 Mon Sep 17 00:00:00 2001 From: Claudius Agent <noreply@anthropic.com> Date: Tue, 21 Jul 2026 10:14:16 +0000 Subject: [PATCH 6/6] docs(git-and-github): clarify template copy scope, keep Attribution as true footer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Copilot review comments on #76: - "Copy pr-body-template.md and fill it in" (and the issue/gh-cli-fallback equivalents) was ambiguous — could be read as copying the whole file, including its title and explanatory prose, not just the fenced skeleton. Both template files and all four pointer sentences now say explicitly to copy only the fenced block. - Prior work sat after Attribution in both templates, contradicting the skill's "append the attribution footer" contract (the footer should be the true last section). Moved Prior work to just before Attribution in both templates; tests updated to assert Attribution is last. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --- CHANGELOG.md | 2 +- skills/git-and-github/SKILL.md | 4 ++-- skills/git-and-github/references/gh-cli-fallback.md | 4 ++-- .../references/issue-body-template.md | 13 +++++++------ .../git-and-github/references/pr-body-template.md | 12 +++++++----- tests/test_issue_body_template.py | 8 ++++---- tests/test_pr_body_template.py | 9 +++++++-- 7 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60016e6..6393b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/). This project use - **PR-body template** (`skills/git-and-github/SKILL.md` §Creating a PR): replaced the "Why this PR exists" skeleton with a plain-language-first structure — `TL;DR` (one sentence) -> `## User story` ("As a **\<role\>**, I want to \<what-to-do\>, to achieve \<user-goal\>.") -> `## Scenario` (`### Base flow` / `### Actual behavior` / `### Expected behavior`) -> `## Detailed discussion` (nests the former `What was done`/`Testing`/`Breaking changes`/`Checklist`/`Attribution` sub-sections, plus problem statement and blocking relationship). The first three sections must stay free of specialized terms and code identifiers — aimed at a technical product manager or external reviewer; `Detailed discussion` is the technical section for implementors and AI agents. - **Issue-body template** (`skills/git-and-github/SKILL.md` §Issues): unified onto the same `TL;DR` -> `User story` -> `Scenario` -> `Detailed discussion` shape as PRs, replacing the old "feature/enhancement issues need a `### User Story`" bullet. For bug reports, `Scenario` doubles as the reproduction; feature requests with nothing to reproduce may drop it. - **Both templates externalized**: the literal skeletons moved out of `SKILL.md` prose into `skills/git-and-github/references/pr-body-template.md` and `references/issue-body-template.md`; `SKILL.md` and `references/gh-cli-fallback.md` now link to them instead of inlining a copy (the `gh` CLI fallback now fills in the template file and passes it via `--body-file`); `push/SKILL.md` continues to delegate to `git-and-github` without inlining its own copy. Pinned by `tests/test_pr_body_template.py` and the new `tests/test_issue_body_template.py`. -- Both templates gained a trailing `### Prior work` section — links to possibly related/similar PRs or issues, each with a one-sentence summary of how it relates to the one being filed. +- Both templates gained a `### Prior work` section (links to possibly related/similar PRs or issues, each with a one-sentence summary of how it relates to the one being filed), placed between `Checklist`/`Detailed discussion` and `Attribution` so the mandatory attribution footer stays the true last section, per Copilot review on #76. Same review flagged the template pointer sentences (`SKILL.md`, `gh-cli-fallback.md`) as ambiguous about copying the whole template file vs. just its fenced skeleton — both templates and all four pointer sentences now say explicitly to copy only the fenced block. ## [5.13.0] - 2026-07-20 diff --git a/skills/git-and-github/SKILL.md b/skills/git-and-github/SKILL.md index fcae6cd..04bd445 100644 --- a/skills/git-and-github/SKILL.md +++ b/skills/git-and-github/SKILL.md @@ -59,7 +59,7 @@ Always ask explicit confirmation before every push, even if the user agreed earl Check for a PR template first. If a template exists, read and fill it in, folding its required content into the skeleton linked below rather than replacing it. -The PR body **must lead with a plain-language summary before any implementation detail** — a technical product manager or an external reviewer with no code context must understand everything before `Detailed discussion` at a glance. Copy [pr-body-template.md](references/pr-body-template.md) and fill it in. +The PR body **must lead with a plain-language summary before any implementation detail** — a technical product manager or an external reviewer with no code context must understand everything before `Detailed discussion` at a glance. Copy the skeleton from [pr-body-template.md](references/pr-body-template.md) (the fenced block only, not the page's title or explanatory text) and fill it in. **`TL;DR` / `User story` / `Scenario` are user-facing** — plain language only, no specialized terms, no internal implementation details, no code identifiers. Describe strictly user-observable behavior (for an API/CLI, the calling developer *is* the user). `User story` follows the same "As a `<role>`..." shape as the Issues `User story` below, phrased for a change already made. `Scenario` isn't only for bugs: for a new feature, `Actual behavior` is what's missing/impossible today and `Expected behavior` is what becomes possible after this PR — no failure or "actual" bug is required. For a pure internal change with no user-observable effect, drop `User story` and `Scenario` entirely and say so in `Detailed discussion`. Note any blocking relationship (prerequisite for / depends on / stacked atop PR #N) in `Detailed discussion`. @@ -79,7 +79,7 @@ See [pr-review.md](references/pr-review.md) for the full procedure: fetching PR Before creating, search existing issues (open + closed) and PRs for duplicates. If found, show to user and ask before proceeding. Check for issue templates first; if one exists, fold its required content into the skeleton linked below rather than replacing it. -Issue bodies use the same plain-language-first skeleton as PRs (see §Creating a PR for the full rationale). Copy [issue-body-template.md](references/issue-body-template.md) and fill it in: `TL;DR` → `User story` → `Scenario` → `Detailed discussion`. `User story` uses the same "As a **\<role\>**, I want to ..., to achieve ..." shape as PRs — multiple personas are fine, repeat the line. Always append the attribution footer. +Issue bodies use the same plain-language-first skeleton as PRs (see §Creating a PR for the full rationale). Copy the skeleton from [issue-body-template.md](references/issue-body-template.md) (the fenced block only, not the page's title or explanatory text) and fill it in: `TL;DR` → `User story` → `Scenario` → `Detailed discussion`. `User story` uses the same "As a **\<role\>**, I want to ..., to achieve ..." shape as PRs — multiple personas are fine, repeat the line. Always append the attribution footer last. ## Safety Rules diff --git a/skills/git-and-github/references/gh-cli-fallback.md b/skills/git-and-github/references/gh-cli-fallback.md index 8500c24..3e65088 100644 --- a/skills/git-and-github/references/gh-cli-fallback.md +++ b/skills/git-and-github/references/gh-cli-fallback.md @@ -14,7 +14,7 @@ git ls-tree HEAD --name-only -r .github/ | grep -i pull_request_template If a template exists, read and fill it in, folding its required content into the linked skeleton below. See the main skill's §Creating a PR for the full rationale (plain language up top, technical detail in `Detailed discussion`). -Fill in [pr-body-template.md](pr-body-template.md), save the completed body to a file, then create the PR as a draft, passing the body via `--body-file` (not `--body` — avoids shell-escaping the whole skeleton inline): +Fill in the skeleton from [pr-body-template.md](pr-body-template.md) (the fenced block only, not the page's title or explanatory text), save the completed body to a file, then create the PR as a draft, passing the body via `--body-file` (not `--body` — avoids shell-escaping the whole skeleton inline): ```bash gh pr create --draft --title "<type>: <description>" --body-file /path/to/filled-in-pr-body.md @@ -40,7 +40,7 @@ Check for issue templates before creating: git ls-tree HEAD --name-only -r .github/ | grep -i issue_template ``` -If none exists, fill in [issue-body-template.md](issue-body-template.md) (see the main skill's §Issues), save the completed body to a file, then: +If none exists, fill in the skeleton from [issue-body-template.md](issue-body-template.md) (the fenced block only, not the page's title or explanatory text; see the main skill's §Issues), save the completed body to a file, then: ```bash gh issue create --title "<title>" --body-file /path/to/filled-in-issue-body.md diff --git a/skills/git-and-github/references/issue-body-template.md b/skills/git-and-github/references/issue-body-template.md index 7879dde..e4f7644 100644 --- a/skills/git-and-github/references/issue-body-template.md +++ b/skills/git-and-github/references/issue-body-template.md @@ -1,10 +1,11 @@ # Issue Body Template Canonical issue-body skeleton for `git-and-github` §Issues — same plain-language-first -shape as [`pr-body-template.md`](pr-body-template.md). Drop whole sections or sub-sections -that don't apply, keep the section order. `TL;DR` / `User story` / `Scenario` are -plain-language and user-facing; `Detailed discussion` is technical, for implementors and -AI agents. +shape as [`pr-body-template.md`](pr-body-template.md). Copy only the contents of the fenced +block below into the issue body — not this page's title or explanatory text. Drop whole +sections or sub-sections that don't apply, keep the section order. `TL;DR` / `User story` / +`Scenario` are plain-language and user-facing; `Detailed discussion` is technical, for +implementors and AI agents. For a bug report, `Scenario` is the reproduction: `Base flow` gets there, `Actual behavior` is the bug, `Expected behavior` is the fix target. For a feature request with nothing to @@ -30,8 +31,8 @@ As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. ## Detailed discussion <technical notes, proposed approach, logs, links> -### Attribution - ### Prior work <links to possibly related or similar PRs/issues, each with a one-sentence summary of how it relates to this one; omit if none> + +### Attribution ``` diff --git a/skills/git-and-github/references/pr-body-template.md b/skills/git-and-github/references/pr-body-template.md index 060ad65..39760aa 100644 --- a/skills/git-and-github/references/pr-body-template.md +++ b/skills/git-and-github/references/pr-body-template.md @@ -1,9 +1,10 @@ # PR Body Template -Canonical PR-body skeleton for `git-and-github` §Creating a PR. Drop whole sections or -sub-sections that don't apply, keep the section order. `TL;DR` / `User story` / `Scenario` -are plain-language and user-facing; `Detailed discussion` is technical, for implementors -and AI agents. +Canonical PR-body skeleton for `git-and-github` §Creating a PR. Copy only the contents of +the fenced block below into the PR body — not this page's title or explanatory text. Drop +whole sections or sub-sections that don't apply, keep the section order. `TL;DR` / +`User story` / `Scenario` are plain-language and user-facing; `Detailed discussion` is +technical, for implementors and AI agents. ```markdown **TL;DR:** <one plain-language sentence describing what this PR does> @@ -28,8 +29,9 @@ As a **<role>**, I want to <what-to-do>, to achieve <user-goal>. ### Testing ### Breaking changes ### Checklist -### Attribution ### Prior work <links to possibly related or similar PRs/issues, each with a one-sentence summary of how it relates to this one; omit if none> + +### Attribution ``` diff --git a/tests/test_issue_body_template.py b/tests/test_issue_body_template.py index d1fc25d..f15441b 100644 --- a/tests/test_issue_body_template.py +++ b/tests/test_issue_body_template.py @@ -58,11 +58,11 @@ def test_template_sections_are_ordered() -> None: f"{TEMPLATE}: 'Scenario' sub-sections must appear in order " "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" ) - assert detailed < attribution, ( - f"{TEMPLATE}: 'Attribution' must be nested under 'Detailed discussion'" + assert detailed < prior_work < attribution, ( + f"{TEMPLATE}: 'Prior work' must sit between 'Detailed discussion' and 'Attribution'" ) - assert prior_work == max(text.index(h) for h in SKELETON_HEADINGS), ( - f"{TEMPLATE}: 'Prior work' must be the last section in the skeleton" + assert attribution == max(text.index(h) for h in SKELETON_HEADINGS), ( + f"{TEMPLATE}: 'Attribution' (the mandatory footer) must be the last section in the skeleton" ) diff --git a/tests/test_pr_body_template.py b/tests/test_pr_body_template.py index ccab564..10c0665 100644 --- a/tests/test_pr_body_template.py +++ b/tests/test_pr_body_template.py @@ -55,7 +55,9 @@ def test_template_sections_are_ordered() -> None: actual = text.index("### Actual behavior") expected = text.index("### Expected behavior") detailed = text.index(DETAILED) + checklist = text.index("### Checklist") prior_work = text.index("### Prior work") + attribution = text.index("### Attribution") assert tldr < user_story < scenario < detailed, ( f"{TEMPLATE}: sections must appear in order TL;DR -> User story -> Scenario -> Detailed discussion" ) @@ -63,8 +65,11 @@ def test_template_sections_are_ordered() -> None: f"{TEMPLATE}: 'Scenario' sub-sections must appear in order " "Base flow -> Actual behavior -> Expected behavior, before 'Detailed discussion'" ) - assert prior_work == max(text.index(h) for h in SKELETON_HEADINGS), ( - f"{TEMPLATE}: 'Prior work' must be the last section in the skeleton" + assert checklist < prior_work < attribution, ( + f"{TEMPLATE}: 'Prior work' must sit between 'Checklist' and 'Attribution'" + ) + assert attribution == max(text.index(h) for h in SKELETON_HEADINGS), ( + f"{TEMPLATE}: 'Attribution' (the mandatory footer) must be the last section in the skeleton" )