From 75ef4ca9ab2d70579bcbc2098d2275d9c3160147 Mon Sep 17 00:00:00 2001 From: amcheste-ai-agent <278991699+amcheste-ai-agent@users.noreply.github.com> Date: Sat, 25 Apr 2026 15:22:21 -0400 Subject: [PATCH 1/2] chore(skills): sync setup-repo and publish-release with handbook v0.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related corrections to bring the canonical Claude Code skills in line with the conventions documented in amcheste/engineering-handbook v0.1.0. setup-repo ---------- Adds Step 6: verify CODEOWNERS routing. Without .github/CODEOWNERS, bot-authored PRs (via the amcheste-ai-agent GitHub App) don't auto- route to a human reviewer and disappear from review queues like Graphite. Step 6 checks for the file and surfaces the gap in the summary; setup-repo still doesn't write to the repo itself, so the remediation is a follow-up PR adding the file. This was discovered when overleaf-mcp PR #8 didn't show up in @amcheste's review queue — the repo had been through /setup-repo but never had CODEOWNERS added. publish-release --------------- Step 2 was opening a develop→main GitHub PR for the release promotion. That contradicts the rule baked into the engineering handbook (and into ~/.claude/CLAUDE.md): GitHub's merge button squash-merges by default, which collapses commit ancestry and causes merge conflicts on every subsequent release. The fix is a CLI --no-ff merge. Also adds a branch-protection note: with enforce_admins: true required-PR review, the CLI push to main is rejected. The skill now documents the toggle dance (disable, push, re-enable) and notes the App needs Administration: Read & Write to perform it. The v* tag ruleset has the same problem and same fix. Minor cleanup: hardcoded mac-dev-setup URL in the Summary section becomes placeholder. Co-Authored-By: Claude Opus 4.7 (1M context) Co-Authored-By: amcheste <13696614+amcheste@users.noreply.github.com> --- claude-skills/publish-release/SKILL.md | 44 +++++++++++++++----------- claude-skills/setup-repo/SKILL.md | 32 +++++++++++++++++-- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/claude-skills/publish-release/SKILL.md b/claude-skills/publish-release/SKILL.md index 314d8f6..354a1ee 100644 --- a/claude-skills/publish-release/SKILL.md +++ b/claude-skills/publish-release/SKILL.md @@ -1,6 +1,6 @@ --- name: publish-release -description: Publish a new versioned release. Opens a version bump PR to develop, merges it, promotes develop to main via PR, tags main, and triggers the release pipeline. +description: Publish a new versioned release. Opens a version bump PR to develop, merges it, promotes develop to main via CLI --no-ff merge (never a GitHub PR), tags main, and triggers the release pipeline. --- Publish a new release based on the user's request: $ARGUMENTS @@ -41,35 +41,43 @@ gh pr create \ --base develop \ --head chore/release-v \ --title "chore: release v" \ - --body "Version bump to v. Merge to proceed with the develop→main release PR." + --body "Version bump to v. Merge to proceed with the develop→main CLI release merge." ``` Show the user the PR URL. Wait for CI to pass, then ask them to approve and merge it. -## Step 2 — Promote develop → main +## Step 2 — Promote develop → main via CLI merge -After the version bump PR is merged, open the develop→main release PR: +After the version bump PR is merged, promote develop to main with a non-fast-forward merge from the command line. -```bash -git checkout develop && git pull - -gh pr create \ - --base main \ - --head develop \ - --title "chore: release v" \ - --body "Promotes develop to main for release v. +> **Do NOT use a GitHub PR for this step.** GitHub's merge button squash-merges by default, which flattens every commit on develop into a single new commit on main with no ancestry relationship. On the next release, main and develop have diverged at every commit and every subsequent release hits merge conflicts. A `--no-ff` CLI merge preserves the commit graph so main remains a strict ancestor of develop. -After merging, the tag will be pushed to trigger the release pipeline." +```bash +git fetch origin +git checkout main && git pull +git merge --no-ff origin/develop -m "chore: release v" +git push origin main ``` -Show the user the PR URL and ask them to approve and merge it. +Confirm the push succeeded before moving to Step 3. -## Step 3 — Tag main after merge +> **Branch protection note:** if `main` has `enforce_admins: true` and required-PR review, the CLI push will be rejected. Toggle protection around the push: +> +> ```bash +> # Disable bypass-prevention temporarily +> gh api -X DELETE repos//branches/main/protection/enforce_admins +> +> # ...do the merge and push above... +> +> # Re-enable +> gh api -X POST repos//branches/main/protection/enforce_admins +> ``` +> +> The `amcheste-ai-agent` GitHub App needs `Administration: Read & Write` permission on the install for these toggles. The same dance applies to the `v*` tag ruleset in Step 3 if tag creation is restricted — toggle the ruleset's enforcement to `disabled` around the tag push, then back to `active`. -After the user confirms the develop→main PR is merged: +## Step 3 — Tag main ```bash -git checkout main && git pull git tag -a "v" -m "Release v" git push origin "v" ``` @@ -89,5 +97,5 @@ Show the user the release pipeline URL and confirm the tag was pushed. Let them Tell the user: - What version was tagged on `main` - That the pipeline is running: validate → VM acceptance → publish -- Where to watch it: `https://github.com/amcheste/mac-dev-setup/actions` +- Where to watch it: `https://github.com/amcheste//actions` - That `main` now equals the new release and `develop` is ready for the next cycle diff --git a/claude-skills/setup-repo/SKILL.md b/claude-skills/setup-repo/SKILL.md index 4f42ece..9154ec2 100644 --- a/claude-skills/setup-repo/SKILL.md +++ b/claude-skills/setup-repo/SKILL.md @@ -1,6 +1,6 @@ --- name: setup-repo -description: Apply standard branch model, protection rules, and settings to a GitHub repository. Creates develop branch, sets it as default, protects develop and main, and adds tag protection. +description: Apply standard branch model, protection rules, and settings to a GitHub repository. Creates develop branch, sets it as default, protects develop and main, adds tag protection, and verifies CODEOWNERS routing. --- Configure a GitHub repository with the standard branch model and protection rules: $ARGUMENTS @@ -126,9 +126,35 @@ gh api repos//rulesets \ EOF ``` +## Step 6 — Verify CODEOWNERS routing + +Bot-authored PRs (via the `amcheste-ai-agent` GitHub App) need +`.github/CODEOWNERS` to auto-route review requests to a human reviewer. +Without this file, App-authored PRs don't appear in any reviewer's +queue (Graphite, GitHub's review-requested filter, etc.) and get lost. + +```bash +gh api repos//contents/.github/CODEOWNERS >/dev/null 2>&1 \ + && echo "✓ CODEOWNERS exists" \ + || echo "⚠ CODEOWNERS missing" +``` + +If the file is missing, **do not write it directly** — `setup-repo` only +configures settings/rulesets, never commits to the repo. Instead, surface +the gap in the summary so the user can add it via a PR. The canonical +default content is: + +``` +# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +* @amcheste +``` + +This pairs with the bot-account model documented in the +[engineering handbook](https://github.com/amcheste/engineering-handbook/blob/main/docs/design/claude-bot-account.md). + ## Summary -Report what was configured: +Report what was configured (and any gaps that need a follow-up PR): ``` ✓ develop branch created (or already existed) @@ -136,9 +162,11 @@ Report what was configured: ✓ develop protected — require PR + [checks] ✓ main protected — require PR + [checks] ✓ Tag ruleset active — v* tags protected +✓/⚠ CODEOWNERS verified (or: CODEOWNERS missing — see follow-ups) Next steps: - If using repo-template: copy .github/ files into this repo - Add project-specific lint/test steps to .github/workflows/validate.yml - Update required status check names to match your workflow job names +- If CODEOWNERS was missing, open a PR adding `.github/CODEOWNERS` with `* @amcheste` ``` From 2be45454e045ec4c77692ef4b689c1a20bb20d91 Mon Sep 17 00:00:00 2001 From: amcheste-ai-agent <278991699+amcheste-ai-agent@users.noreply.github.com> Date: Sat, 25 Apr 2026 15:50:51 -0400 Subject: [PATCH 2/2] chore(skills): refuse forks in setup-repo pre-flight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forks follow upstream's conventions, not mine. setup-repo applying my branching model, protection rules, and CODEOWNERS to a fork is a silent error — it changes a repo I don't own conceptually. Adds an isFork check at pre-flight that bails with a clear message. Also notes that any cross-repo audits should pass --source to gh repo list to skip forks. Found while auditing CODEOWNERS coverage across all repos — amcheste/traefik (a fork of upstream traefik) showed up in the "missing CODEOWNERS" list and was correctly excluded from the remediation PRs. The skill should enforce this rule next time without me having to remember it manually. Co-Authored-By: Claude Opus 4.7 (1M context) Co-Authored-By: amcheste <13696614+amcheste@users.noreply.github.com> --- claude-skills/setup-repo/SKILL.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/claude-skills/setup-repo/SKILL.md b/claude-skills/setup-repo/SKILL.md index 9154ec2..463bfe2 100644 --- a/claude-skills/setup-repo/SKILL.md +++ b/claude-skills/setup-repo/SKILL.md @@ -13,7 +13,17 @@ Extract from the user's message: ## Pre-flight 1. Verify the repo exists: `gh repo view ` -2. Show the user what you're about to do and confirm before making any changes +2. **Refuse forks.** `setup-repo` configures the conventions of the *owner* of the repo. A fork is owned by upstream's conventions, not yours — applying your branching model, protections, and CODEOWNERS to it is wrong: + + ```bash + if [ "$(gh repo view --json isFork --jq .isFork)" = "true" ]; then + echo "ERROR: is a fork. setup-repo follows your conventions; forks follow upstream's. Aborting." + exit 1 + fi + ``` + + This also applies to audit scripts that survey "all my repos" — use `gh repo list --source` (filters out forks) instead of plain `gh repo list` so forks don't surface in the report. +3. Show the user what you're about to do and confirm before making any changes ## Step 1 — Ensure develop branch exists