# Make PR

Source: https://flow-next.dev/skills/make-pr/

Close the finished spec on the branch, then open a pull request whose body is a reviewer's briefing that flowctl renders from one authored object.

`/flow-next:make-pr` takes a spec with done tasks and a branch ahead of its base (`main`, or the parent spec’s branch for a [dependent spec](https://flow-next.dev/skills/make-pr/#dependent-specs-chains-and-stacks)), closes the spec on that branch, and opens a pull request whose body is a briefing for the person who has to review it.

## Creates the PR directly: no confirm gate

Invoking the skill **is** the intent, so make-pr pushes and opens the PR **directly - there is no “create / abort” confirm prompt**. The PR opens **ready for review** unless it has open items (open spec questions, a completion review that needs work, incomplete tasks, other unfinished work), which make it a draft. Deferred findings and follow-ups are listed in the body and are not a reason to draft. The only prompts are Phase 0 info prompts to resolve something it cannot derive (no `--base` and no detection match, or no spec detected) - never “do you want to create it?”. The escape hatch is `--dry-run` (print the body without creating), and `--ready` / `--draft` force the draft state.

## The spec closes at the pull request head

When every task of the spec is done, make-pr closes the spec and commits the close as the **last commit on the branch**, before the pull request opens:

```text
chore(flow): close fn-12-export-json-flag
```

The commit carries the spec’s `done` status and every task’s final status. Merging the pull request carries the close to the base, so a protected base that refuses direct pushes is no longer a problem, and [land](https://flow-next.dev/autonomy/land/#after-the-merge) has nothing to write after the merge.

* The close happens before the body is composed, so the body describes the same head the pull request opens at. If the branch moves between the close and the push, make-pr stops and asks for a re-run.
* A failed close, a failed commit, or uncommitted changes in the spec and task files stop make-pr before anything is pushed.
* A spec with open tasks, or with no tasks at all, is not closed. Interactively it opens as a draft; an autonomous run refuses a spec with open tasks.
* An already closed spec is left untouched. `--dry-run` and `--update` never close.
* Creating or starting a follow-up task reopens a closed spec. Finishing that task does not close it again; the next make-pr run does.

In the tracker, a closed spec with an open pull request stays in review. Only a confirmed merge moves the issue to its terminal status.

## Dependent specs: chains and stacks

A spec that depends on another spec (`depends_on_epics`) gets its PR opened against the parent’s branch, so the reviewer sees only that layer’s diff and the dependent spec never waits for the parent’s PR to merge. Make-pr detects the chain from history, never from recorded state: the merge-base with the parent’s branch tip (or, once the parent merged, its PR head) is the boundary of the parent’s work inside this branch. When the parent’s PR is still open, the base is the parent’s branch; on GitHub the PR is also linked into the parent’s stack through the [stacks REST API](https://docs.github.com/en/rest/pulls/stacks) of GitHub’s [stacked pull requests](https://docs.github.com/en/pull-requests/get-started/about-stacked-prs) public preview, so the merge box shows the stack map and GitHub owns sequential merge and retarget; the gh-stack extension is never required. A failed link leaves a plain chain layer with one line on stderr, and on any other host the PR is a plain chain layer to begin with. When the parent already merged, a create run rewrites the branch onto the chain base from the detected boundary before opening the PR; `--dry-run` and `--update` never rewrite.

One bound: chains are linear, one open parent and one child at a time; the build loop parks a second child until the first child’s PR merges. A chained layer with nothing open opens ready like any other PR, which matters because GitHub cannot merge a draft from the stack UI. `--base <ref>` still overrides detection. The branch itself is forked by [work](https://flow-next.dev/skills/work/#dependent-specs-branch-from-the-parent); merging the chain is [land’s job](https://flow-next.dev/autonomy/land/#chains-and-stacks); the [glossary](https://flow-next.dev/reference/glossary/#chain) defines chain, stack, layer, and frontier.

## The briefing, section by section

The body has seven sections in a fixed order. A section with nothing to say has no heading and no placeholder. The excerpts below are from the pull request that shipped this design, trimmed.

**Why.** The intent and the approach, in the author’s words, with its line breaks kept.

```markdown
## Why


Land and make-pr had each grown to thousands of lines of instructions and shell that an
agent re-executed on every run, and most defects reported against them were in that
machinery, not in the decision to merge or in what a reviewer reads. […]
```

**What changes for a user or operator.** What someone running the software will notice.

```markdown
## What changes for a user or operator


Land no longer finds pull requests by itself: you name one, and it merges only with
authorization given in the current session. […] Pull-request bodies look like this one.
```

**Scope.** A size line, then the review steps in the order to read them. Each step has a bold numbered title, one or two sentences on what to check there, and up to ten must-read files as linked rows: the path, what the file does in this change, and the requirement ids it serves. An added, deleted or renamed file carries that marker. Further described files in a step are counted, not listed. Files nobody needs to read end up in one line, and requirement coverage closes the section.

```markdown
## Scope


175 files changed; +8242/-12997 lines; 46 generated, 8 mechanical.


**1. fn-250: land becomes short prose over one named pull request**


Read this group first; it carries most of the risk. Check that spec close commits every
task's final status before the pull request opens, and that land merges only a head-pinned
squash with in-session authorization and writes nothing to the repository after the merge.


- [` plugins/flow-next/skills/flow-next-land/SKILL.md `](…) : Arguments and the authorization rule: land merges only what the user or the calling flow authorized in this session. [fn-250:R4, fn-250:R6, fn-250:R9]
- added [` plugins/flow-next/tests/test_make_pr_close.py `](…) : Runs the close fence against real repositories: incomplete task, failed close, dry run, already closed. [fn-250:R2]


[…]


Rest of diff: 8 mechanical files; 46 generated files; 93 not described files


Coverage fn-251: R1 → group 5; R2 → group 5; R3 → group 5; R4 → group 5
```

A declared requirement that no step covers is named in its own table, so anything the spec promised and the diff did not deliver stays visible. A spec with no requirement ids gets no coverage line and no tags.

**Blast radius.** Who and what the change can reach, what to read first, and what is unproven.

```markdown
## Blast radius


Everyone who lands with flow-next, and every tool that drives land or reads its verdict
line, its ledger or its config keys. […] Unproven: land's merge, stack and branch-delete
paths have never run against a live pull request.
```

**Verification.** A checklist that ticks only what passed. A failed or unverified item renders unticked with its note. A recorded fact that is neither, such as a measurement, renders as a plain item.

```markdown
## Verification


- [x] Unit suite and lint: Each task records the full suite and the linter at exit 0 on its final tree (4,921 to 5,042 tests per run).
- [ ] unverified: Land merge, stack and branch-delete paths: R4 to R8 are prose pinned by substring tests. They never ran against a live pull request.
```

A bug fix adds its own cells. When a task ran the [defect route](https://flow-next.dev/choosing-your-route/#bug-or-defect), the checklist carries `Prior fixes`, `Cause` (the confirmed mechanism and the commit that introduced it), `Base`, `Head` and `Live` cells. A step that did not happen or a live check that did not run renders unverified with its reason; the full record stays with the task. A [hill climb](https://flow-next.dev/choosing-your-route/#hill-climb) adds cells for the metric and target, the baseline and final values with the percent change, the attempt counts (kept, reverted, inconclusive), the kept commits in order, the harness proof, the final gate and the best untried idea; an unmet target renders unverified, never passed.

**Tradeoffs.** The alternatives that were rejected, and why.

**Open items.** What is unfinished. Work with no evidence behind it belongs here, not in Verification.

An unattended run (`flow --auto`) also writes its **Decisions list** into the body, under Tradeoffs: each default it chose, finding it declined and review it skipped on your behalf, with the evidence behind it. A human-only call that blocks nothing else lands under Open items, which keeps the PR a draft until you settle it.

The body ends with one invisible HTML comment carrying the artifact id, the base SHA and the head SHA. There is no size threshold and no line budget: every pull request gets the same form, and the body is as long as its authored content. A body above 65,000 characters stops the run with the file kept, never truncated.

Mentions in authored prose are made inert, so a body never pings `@someone` by accident. Issue and pull request numbers, URLs and commit SHAs stay live links. That includes closing keywords: `fixes #12` in the authored text closes that issue on merge, as it would in a body you wrote by hand.

## What the agent writes and what flowctl fills

The agent authors one object, the aid artifact, and nothing else. flowctl validates it, stores it and renders the Markdown in one pass. The agent never assembles sections by hand and makes no extra model call.

| The agent authors (judgment)                                            | flowctl fills (mechanics)                                                                                                     |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Why, user impact, blast radius, tradeoffs, open items                   | Section order and headings; empty sections dropped                                                                            |
| The review steps: order, title, what to check, requirement and task ids | Step numbering, the ten-row cap, the counted remainders                                                                       |
| The must-read files: path, one-line purpose, requirement ids            | Each file’s added, deleted or renamed state, its line counts and its diff link                                                |
| Which files are safe to skim, where a pattern does not already say so   | Every file the agent did not mention, accounted for under Rest of diff; state files, lockfiles and mirrors classed by pattern |
| Verification items and whether each passed, failed or is unverified     | The ticks, the coverage lines, the uncovered-requirement table                                                                |

The agent’s input is sparse: it lists the files a reviewer must read and lets flowctl account for the rest of the diff. The stored artifact is always complete. Everything the agent cites must trace to the export, a receipt, or a commit:

```bash
flowctl spec export-cognitive-aid <spec-id> --base <ref> --json
```

Validation rejects a claim with no source, a file listed twice, an unsafe path, and a summary that is only whitespace, and it reports every violation at once with its field path. When attribution is unknown the body says so. An invalid or stale artifact renders nothing, and make-pr falls back to a plainly labeled body built from the export’s goal, tasks, verification and open items.

## Several specs in one pull request

An integration branch that closes several specs gets **one** body. The artifact lists every spec in `specIds`, requirement ids are qualified (`fn-250:R4`), each spec gets at least one review step with its short id in the title, and coverage prints one line per spec. Commits that belong to no spec get a step of their own. The title names the combined change.

When no spec names the current branch, make-pr finds the specs the branch closes and hosts the body on the highest-numbered one. The same read is available to you:

```bash
flowctl spec closed-in-range --base origin/main          # one spec id per line
flowctl spec closed-in-range --base origin/main --json   # {"spec_ids": [...]}
```

It is read-only and never fetches. [Land](https://flow-next.dev/autonomy/land/#a-pull-request-that-closes-several-specs) selects the same set from the pull request’s remote head.

## House style shortens the body

The renderer sets no length budget, so the length of a body is the length of what the agent wrote. If your team wants shorter bodies, say so in the project’s instruction file (`AGENTS.md`, `CLAUDE.md`, or your host’s equivalent). The agent reads that file in the same context as the [prose contract](https://flow-next.dev/skills/prose/), and a length budget or reading level written there reaches every artifact it drafts, this one included:

```markdown
## Pull request bodies


- Why: three sentences at most.
- One sentence per file row. At most five review steps.
- Tradeoffs: only alternatives a reviewer would otherwise ask about.
```

A house rule shortens the authored text. It cannot drop or reshape a section: the section order, the verification checklist and the coverage lines belong to the renderer.

## One portable artifact, three render surfaces

Make-pr stores the artifact as an immutable v1 JSON generation under `.flow/artifacts/<spec-id>/pr-cognitive-aid/<artifactId>.json`. Its artifact id and base/head SHAs decide currentness; a stale or invalid generation remains evidence but cannot supply current verification claims. The file stays local: `flowctl init` keeps new generations and their write lock out of broad staging. A repository that already tracks them can untrack them once (local files are kept):

```bash
flowctl init
git rm --cached --ignore-unmatch -- '.flow/artifacts/*/pr-cognitive-aid/*.json' '.flow/artifacts/*/pr-cognitive-aid/.write.lock'
```

The same validated object drives the stored artifact and the Markdown briefing. For a visual walkthrough of the diff, run [`/flow-next:visual`](https://flow-next.dev/skills/visual/) or ask the agent for an HTML page. The artifact accepts four optional authored strings (`userImpact`, `blastRadius`, `tradeoffs`, `openItems`) and an optional `outcome` (`pass`, `fail`, `unverified`) on each verification cell. These are additive: the schema version stays 1, and an older artifact renders without gaining a pass claim.

For downstream integrations, Flow-Next ships a maximum-normal v1 fixture with metadata pinning its source path, source commit, and exact SHA-256. Consumers such as Flow Swarm vendor byte-identical fixture and metadata copies, validate the local digest, and run without cross-repository network access in CI. The metadata also pins a strict `<100 ms p95` validation-plus-Markdown-render budget over 30 warm runs.

## Invocation

| Flag                  | Effect                                                                                                                                                                                                                                                                         |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--draft` / `--ready` | Force draft or ready state. Without either, the PR opens ready unless it has open items.                                                                                                                                                                                       |
| `--base <ref>`        | Override base-branch detection, including the parent-branch base of a [chained spec](https://flow-next.dev/skills/make-pr/#dependent-specs-chains-and-stacks). A branch name resolves against `origin/<branch>`, refreshed first, so a stale local base never widens the diff. |
| `--update`            | Refresh the body of the open pull request on this branch against the current diff. Never closes a spec and never rewrites a branch.                                                                                                                                            |
| `--memory`            | Write a `knowledge/architecture-patterns/` memory entry alongside the PR.                                                                                                                                                                                                      |
| `--dry-run`           | Print the body without closing the spec, pushing, or opening a PR.                                                                                                                                                                                                             |

The `--no-mermaid` flag is removed. The briefing has no diagrams to skip, and an unknown flag is refused.

## App/bot-authored PRs: `FLOW_PR_CREATE_CMD`

By default make-pr opens the PR with `gh pr create`, so the PR is authored by whatever identity `gh` is logged in as. Repos that require App- or bot-authored PRs - the canonical case is a single-maintainer repo with `required_approving_review_count: 1`, where GitHub forbids approving your own PR, making a human-authored PR unmergeable - can interpose the create call:

```bash
export FLOW_PR_CREATE_CMD=/path/to/wrapper
```

The wrapper receives make-pr’s stable argument contract - `--title <t> --body-file <f> [--draft] --base <branch> --head <branch>` - and must exit 0 and print the PR URL on success. Extra logging is tolerated: make-pr extracts the last `…/pull/<n>` (or Bitbucket’s `…/pull-requests/<n>`) from the combined output rather than trusting it verbatim. The command runs through `sh -c`, so it behaves the same whether the agent’s shell is bash or zsh, it may carry its own arguments (`FLOW_PR_CREATE_CMD="my-wrapper --org acme"`), and a path with spaces needs shell quoting. On failure the wrapper’s combined output is surfaced verbatim, and wrappers that proxy `gh` inherit make-pr’s eventual-consistency retry for free.

[`/flow-next:features`](https://flow-next.dev/skills/features/#shipping-a-maintain-pass) opens its maintain PRs through the same variable with the same contract, which is how a non-GitHub host ships a maintain pass.

Scope is the create call only - authorship is fixed at creation. `gh` remains a preflight requirement (`gh pr view` / `gh pr edit`, `--update` mode, and the post-create link repair all use it), and identity work - minting App installation tokens - belongs to the wrapper, not flow-next. Wrapper-free alternative for short runs: export `GH_TOKEN` with an App installation token; `gh` prefers it over stored auth. Installation tokens expire after an hour, which is exactly when the wrapper seam earns its keep.

## Tracker linkage and Linear Diffs

When the [tracker bridge](https://flow-next.dev/integrations/tracker-sync/) is active and the spec is linked, make-pr **unconditionally** links the new PR to its tracker issue - no separate opt-in. For Linear that means a non-closing `Ref WOR-N` in the body (plus a rich attachment on the GraphQL transport), which makes the PR render as a [Linear Diff](https://flow-next.dev/integrations/tracker-sync/#linear-diffs-review-the-pr-inside-the-issue) inside the issue; for GitHub it is a native `Refs #N` cross-link. Non-closing is deliberate: the issue stays in review while the pull request is open, and only a confirmed merge moves it to its terminal status.

## Unattended runs

Under `flow --auto` (or `mode:autonomous`) the Phase 0 prompts never fire: a gap that would need input hard-errors instead, and a spec with open tasks is refused. The PR is created directly, ready unless it has open items, and its URL is printed so the run can move on while a human reviews.

## What gets forbidden

* No quoting raw diff content. The diff is the diff; the body is the map to it.
* No `gh pr merge` from the skill. Merging belongs to a human or to an authorized [land](https://flow-next.dev/autonomy/land/) call.
* No inflating scope beyond what the diff supports. If the spec covered more than the diff did, the uncovered criteria appear as gaps, not as accomplishments.
* No tick without a pass. A gate that never ran is written as unverified, with the gap named.

## Worked example

```plaintext
/flow-next:make-pr fn-12-export-json-flag
```

```text
Closed fn-12-export-json-flag on feat/export-json (chore(flow): close fn-12-export-json-flag)
Aid artifact written: 2 review steps, 3 must-read files, R1-R3 covered
Opened: https://github.com/acme/api/pull/214 (ready for review)
```

The reviewer opens the pull request and finds the reading order, the files that matter, and a checklist that ticks only what ran green.

* Never hand-write a body when a spec exists. The rendered one keeps the coverage lines and the honest checklist that manual bodies drift away from.
* A branch with no spec still gets a PR when you ask for one: make-pr never creates a spec just to open it, and uses the session’s handoff as the body.
* After new commits, `/flow-next:make-pr --update` refreshes the body against the current diff.

## Dynamic usage

Recipes that compose with make-pr in the [cookbook](https://flow-next.dev/guides/cookbook/):

* [Evidence-first](https://flow-next.dev/guides/cookbook/#evidence-first) - the coverage lines are the spec’s acceptance criteria proving themselves in the PR.
* [One-shot chains](https://flow-next.dev/guides/cookbook/#one-shot-chains) - “work the spec and open the PR when review ships” ends here without a second prompt.
* [Team patterns](https://flow-next.dev/guides/cookbook/#team-patterns) - the PR briefing is handover object #6; your reviewer starts from the reading order, not from zero.

## Next step

```bash
/flow-next:resolve-pr
```
