Skip to content
Back to skills

Make Pr

ASecurity

catstack-local overlay for the draft-pr skill: adds extra gates before publishing a PR that touches engine/hooks, engine|corpus|product skills -- hook e2e coverage, three-harness install, and ecosystem boundaries. Loaded automatically instead of draft-pr for PRs in this repo.

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 19, 2026
ai-agentspythongogitapi

Works with

  • cursor
  • api

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add EdbertChan/catstack --skill make-pr --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Make Pr?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Make Pr
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/edbertchan-make-pr/badge)](https://www.skillsdirectory.com/skills/edbertchan-make-pr)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: make-pr
description: >
  catstack-local overlay for the draft-pr skill: adds extra gates before
  publishing a PR that touches engine/hooks, engine|corpus|product skills --
  hook e2e coverage, three-harness install, and ecosystem boundaries.
  Loaded automatically instead of draft-pr for PRs in this repo.
---

# make-pr (catstack overlay)

Use `draft-pr`'s schema, confirmation rules, and diff-atomicity gate exactly
as documented -- this file only adds repo-local rules on top.

## Review unit from path

Declare exactly one review unit that matches the dominant changed paths:

| Paths | Review Unit |
| --- | --- |
| `engine/` (hooks, engine skills, scripts) | `engine-runtime` |
| `corpus/skills/` | `corpus-lesson` |
| `product/skills/` | `product-skill` |

One review unit per PR. Repo-root `tests/` and the `docs/ecosystem.md`
inventory row ride along; every other doc, including the root `README.md`,
is its own `docs` unit. `drafter.config.json` is the only source of these
rules, and preflight reads it rather than a copy. A change whose
code reads another unit's output is two stacked PRs, producer first -- not
one PR with the coupling explained in Slice Rationale.

## Stack on top, or overwrite the branch

A PR that already exists gets one of two treatments, decided from its current
diff against its base -- never from how much work went into it, and never by
asking the user which they would prefer:

- **Stack on top** when the claim still holds and what is being added is
  small: a fix to code the PR already ships, the base merged in, or docs for
  the same claim. Push another commit to the same branch, or open a new PR
  based on it.
- **Overwrite the branch** when the diff no longer matches the claim. Any one
  of these is enough:
  - it carries work already on the base
  - it mixes review units
  - it ships a second claim

  Save the old head as `backup/pr<number>-<short-sha>`, say where it went,
  then `git push --force-with-lease=<branch>:<old-sha>` and rewrite the title
  and body to the slice that remains.

The record that gets read later is the PR's final diff and body, so a branch
whose diff stopped matching its title is rewritten, not explained in Slice
Rationale. `--force-with-lease` and the backup branch are the floor, not a
precaution to skip: a lease failure means another session moved that branch,
so re-read it and decide again instead of forcing past it.

## Order of operations

Run these in order. Do not draft a PR body, then discover the review unit
from a mixed working tree.

1. **Choose one publish path before the first push.** Pick the repo's
   merge-queue / stack tool when the repo uses one, otherwise the host's
   ordinary open-PR command. Keep that choice for the whole slice — do not
   push with one tool and open the PR with another. In this repo that means
   `mergify stack push` (then refresh title/body); elsewhere it is usually
   `gh pr create` / `gh pr edit`.
2. **Classify the slice before drafting the body.** Run commit-scoped
   preflight in dry-run mode first; that is enough to learn the unit and
   the gate list:

```sh
python3 engine/skills/make-pr/scripts/preflight.py --base origin/main --commit-only --dry-run
```

3. Draft the description from the declared Review Unit.
4. **Check the draft against the same commit-scoped slice** (pass the
   description file into preflight), then push and publish only via the
   path chosen in step 1.

## Preflight (run first — before the body)

```sh
python3 engine/skills/make-pr/scripts/preflight.py --base origin/main --commit-only --dry-run
python3 engine/skills/make-pr/scripts/preflight.py --base origin/main --commit-only --body-file <draft-description.md>
```

Default classification is **commit-scoped**: paths from
`git diff --name-only <merge-base>`. Untracked files are ignored so a
parking-lot file cannot expand or mix the review unit. Use `--staged` to
add the index; use `--include-untracked` only when you mean to classify
those files (it fails when untracked count is high — park them or pass
`--paths`). Prefer `--commit-only` (or the default) for Invoker and PR
units.

Claims about the repository's own past (how long, how many, who wrote it,
never/always, first/last) are banned from PR descriptions. The premises a
change depends on are proven in the session, so the description never needs
one. `--body-file` sends the draft to the background judge with the
`pr-description-history-claims` dictionary; a hit fails preflight, and so does
a draft no judge could read. Delete the sentence rather than adding evidence
beside it.

A PR that adds rule lines to skill prose also runs
`engine/skills/make-pr/scripts/rule_scope_check.py`, which sends each added
rule to the judge with the `incident-scoped-rule` dictionary: a rule whose main
sentence is one named tool, label, command or error message fails preflight, and
so does a rule no judge could read. Rewrite the lead as the general lesson and
keep the name as an example. It runs beside
`scripts/ci/check_no_dated_provenance.py`, which reads shapes rather than
meaning and stays as it is.

It reads the commit-scoped diff, prints the review unit from the table above,
fails on any mix of review units (printing one `split` line per unit), and
runs every gate below for the hooks and skills actually touched. Paste its
output into the PR's Test Plan. The sections below describe what it runs; you
only run them by hand if it fails.

A failing preflight or `validate-pr-body.mjs` is a stop, in headless and
merge-gate runs too: do not emit a publishable body that says "needs
splitting" or "not ready to merge". Output the `split` lines and the failing
check's message instead, so whatever publishes gets nothing to publish
(fail-safe defaults, Saltzer & Schroeder 1975,
https://web.mit.edu/Saltzer/www/publications/protection/Basic.html).

## Extra gate: hook e2e coverage

Before publishing any PR that touches `engine/hooks/<name>/`:

```sh
python3 scripts/ci/check_hook_test_coverage.py engine/hooks/<name>
```

Must pass for every touched hook that has a `detect.py`. If it fails, add
the missing positive test (reproduces the bad case the hook exists to
catch, asserts it fires) or negative test (a clean case, asserts it stays
silent) first -- do not publish around the gate. This is in addition to,
not instead of, `draft-pr`'s own diff-atomicity gate.

## Extra gate: skills three-harness install

Before publishing any PR that adds or changes a skill under
`engine/skills/`, `corpus/skills/`, or `product/skills/`:

```sh
python3 scripts/ci/check_skill_test_coverage.py --base <actual-pr-base> --head HEAD
python3 scripts/ci/check_skills_three_harnesses.py
python3 scripts/ci/check_ecosystem_boundaries.py
```

All must pass. The coverage command is diff-aware: each changed skill must
change its own colocated or explicitly mapped test in that direct PR slice;
tests inherited from a parent or child stack slice do not count. A skill MUST install to Claude, Cursor, and Codex (unless listed
in `CLAUDE_ONLY_SKILLS`). Do not publish a skill that only documents a
single harness. Do not land a skill in the wrong bucket.

`engine/hooks/auto-pr`'s delivered instruction already tells the agent to run these
checks as part of its auto-triggered flow; a human asking for a PR
interactively should run them too before publishing.

## Extra gate: fixture vs live on ship closeout

On stack/PR closeout for workers or integrations whose Goal includes live
side effects (Linear, deploy, live mine, external APIs):

- Require an explicit **fixture vs live** split in the Test Plan and Summary.
- Either include live evidence from the same turn, or prefix unsettled live
  claims with `{{CAT-UNVERIFIED: <claim> -- cannot verify: <reason>}}`.
- Visual Proof that only shows UI registration must not be framed as product
  e2e of the live side effect.

Follow `prove-it-ship-gate` and Invoker or locally installed `prove-it`.

Files in this skill

  • SKILL.md5.7 KB
  • scripts/description_check.py4.3 KB
  • scripts/preflight.py13 KB
  • tests/fires_example.md776 B
  • tests/stays_silent_example.md320 B
  • tests/test_description_check.py4.4 KB
  • tests/test_preflight.py14.8 KB
  • tests/test_skill_text.py978 B
  • tests/test_stack_or_overwrite.py1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.