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
51 changes: 46 additions & 5 deletions .claude/skills/pr-flow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,41 @@ branch cut from `v2/main` arrives at `v1/main` carrying the whole v2 tree
(Copilot). And never cut from a milestone-merge branch — it carries release-only
commits that will show up in your PR's diff.

Working in a git worktree is fine and often preferable. ⚠️ **A worktree needs a
real `npm install`, not a symlinked `node_modules`** — a symlink passes
lint/test/coverage and then fails all Storybook story files on Vite's
`fs.allow`.
**Always cut it into a new git worktree, never by switching branches in the
primary checkout** (#2661). This repo is routinely worked by several agent
sessions on one machine at once, and they share the primary checkout: a
`git switch` there moves the branch out from under every other session using
it, and carries one task's uncommitted edits, build output and `node_modules`
state into another's. A worktree per issue gives each task its own tree and
leaves the primary checkout alone.

Run it from the primary checkout. The worktree is a **sibling** directory named
for the checkout plus the issue number (`mcp-inspector-v2` →
`../mcp-inspector-v2-2661`), never a directory inside the repo, where every
lint, format and typecheck scope would see a second copy of the source:

```sh
git fetch origin
git worktree add --no-track "../$(basename "$PWD")-<ISSUE_NUMBER>" \
-b v2/fix/<ISSUE_NUMBER>-<slug> origin/v2/main # origin/v1/main for v1
cd "../$(basename "$PWD")-<ISSUE_NUMBER>"
npm install
```

- `--no-track` keeps the new branch from tracking `origin/v2/main`, so
`git status` does not report your work as "ahead of v2/main" and the first
push is an explicit `git push -u origin <branch>` to a branch of its own name.
- ⚠️ **The `npm install` is part of the step, not optional.** A worktree starts
with no `node_modules`, and it needs a **real** install — a symlinked
`node_modules` passes lint/test/coverage and then fails all Storybook story
files on Vite's `fs.allow`. Background it if you have edits to make first;
the gate needs it finished.
- Every later step — edits, the gate, commits, the push — runs **inside the
worktree**.

For order-dependent PRs on one issue, **stack** them: each branch is based on the
previous one, not all cut from `v2/main`.
previous one, not all cut from `v2/main` — so the child's worktree is cut from
the parent branch (`git worktree add … -b <child> <parent-branch>`).

## 3. Sign off every commit

Expand Down Expand Up @@ -453,3 +481,16 @@ nothing else.
`Done` is only for work that **shipped** — a merged PR, or a parent whose last
sub-issue closed. Anything else (duplicate, won't fix, not planned, obsolete)
means nothing shipped, so **delete the card** instead; see `/board-ops`.

**Then remove the worktree and its local branch**, from the primary checkout,
once the PR shows as merged (or closed, for work abandoned). A stale worktree
keeps its branch checked out — git refuses to check that branch out anywhere
else — and holds a full `node_modules` on disk per issue:

```sh
git worktree remove "../$(basename "$PWD")-<ISSUE_NUMBER>"
git branch -D v2/fix/<ISSUE_NUMBER>-<slug>
```

`git worktree remove` refuses a worktree with uncommitted or untracked changes;
read what is there before reaching for `--force`.
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ skills; the rules are here.
- **Every v2 board item has a Priority.** Priority is a **board field**, not a label, so an unboarded issue has nowhere to store it. Derive it with the rubric in the `issue-triage` skill rather than asserting it. Board #11 has no Priority field; a v1 issue gets a Status and nothing else.
- **`Incoming` ⇔ no milestone; everything past it ⇔ milestoned — on board #28.** Board #11 is exempt for the reason above: a v1 issue has no bucket to take, so its Status is set on its own and the audit's milestone checks do not apply to it. A `[GHSA-` **advisory draft** on #28 is exempt too, for a different reason: a draft card cannot carry a milestone, so its approval act is **accepting the advisory**, which moves it `Incoming` → `Todo`; its milestone arrives with the public issue after publication. The rest of the invariant is unchanged: assigning the milestone _is_ the approval act, so the two always go together. `Todo` asserts a maintainer signed off, so never park an unreviewed issue there — that erases the distinction and quietly promotes unreviewed work into the queue. An issue created through the documented flow skips `Incoming` entirely, because filing it _was_ the approval.
- **`Done` means the work shipped.** Exactly two things earn a card a place in Done: its **PR merged**, or it is a **parent whose last sub-issue closed**. Anything else — duplicate, won't fix, not planned, obsolete, superseded — means nothing shipped, so the card is **deleted**. Done is read as the record of what a milestone actually delivered; a duplicate sitting there makes that record wrong in a way nobody can detect later. Deleting a card touches the board only — the issue keeps its labels and comments and stays searchable forever.
- **When work begins**, assign the issue to yourself, create a feature branch and set Status to **In Progress**. **Branch names start with the target version segment** — `v2/fix/2071-oauth-resource-metadata`, `v1/fix/proxy-ssrf-pin` — matching the base branches themselves.
- **When work begins**, assign the issue to yourself, set Status to **In Progress**, and create the feature branch **in a new git worktree** — never by switching branches in the primary checkout, which concurrent agent sessions share (#2661); remove the worktree once the PR merges. The recipe is the `pr-flow` skill, step 2. **Branch names start with the target version segment** — `v2/fix/2071-oauth-resource-metadata`, `v1/fix/proxy-ssrf-pin` — matching the base branches themselves.
- **When work is complete**, run `npm run format` then `npm run local:gate`, **sign off every commit** (`git commit -s` — the repo-owned `DCO` check, `.github/workflows/dco.yml`, fails a PR on any unsigned commit with no partial credit; it gates merges into `v2/main` as a **required** status check, set by the `v2/main - DCO` repository ruleset (#2621) and pinned to GitHub Actions so only the workflow can satisfy it; a PR showing the check as missing or "expected" is an outage, not a pass), open a PR against the matching base branch with **`Closes #<ISSUE_NUMBER>` as the body's first line**, and set Status to **In Review**.
- **After opening a PR, run a Copilot review loop to exhaustion — unprompted.** Request a review, wait for the round to post _or_ for Copilot's session to end without one, answer every comment, and request again whenever a fix was pushed. Stop on the **first** clean round (no confirming round "just to be sure"), a round holding only out-of-scope findings, or two rounds in a row where Copilot's session ends without posting. **Weigh each finding against the issue the PR closes and decline scope expansion** — pre-existing behavior, new capabilities, and hardening the issue did not ask for — because that is what turns a review cycle into overbuilding. The recipe is the `pr-flow` skill, step 7.
- **Attach screenshots as proof of functionality** for any web-UI or TUI change. Put them in a **`pr-screenshots/`** folder off the repo root — it is **gitignored**, so the images are staged for upload and never committed — and name them for what they show.
Expand Down
Loading