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
26 changes: 13 additions & 13 deletions .claude/skills/_shared/park-manifest.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Park manifest — format & conventions (shared)

Canonical definition of the **park manifest**: the pickup contract embedded in a parked PR.
Shared by **`/park`** (which *writes* it) and **`/pickup`** (which *reads and diffs against*
Canonical definition of the **park manifest**: the unpark contract embedded in a parked PR.
Shared by **`/park`** (which *writes* it) and **`/unpark`** (which *reads and diffs against*
it), so the format can't drift between the two. Change it here, once.

> Referenced by `.claude/skills/park/SKILL.md` and `.claude/skills/pickup/SKILL.md`. Not a
> Referenced by `.claude/skills/park/SKILL.md` and `.claude/skills/unpark/SKILL.md`. Not a
> skill itself (no `SKILL.md`), so it won't be invoked directly.

## What a parked PR is
Expand All @@ -20,7 +20,7 @@ three in a place a future agent can find with `gh` alone.

## Where it lives

In the **PR body**, as a markdown block delimited by exact marker lines so `/pickup` can
In the **PR body**, as a markdown block delimited by exact marker lines so `/unpark` can
extract it deterministically:

```
Expand All @@ -46,25 +46,25 @@ PR, machine-locatable by its markers.
2. **Pinned sources (state observed at park time)** — a table, one row per external source.
Each row records the URL **and a snapshot of its state at park time**: for a GitHub PR,
`state` / `merged` / `head SHA` / `base` / `milestone` / `updated`. **The snapshot is what
makes pickup a diff rather than a re-read** — without it, `/pickup` sees "now" with nothing
makes unpark a diff rather than a re-read** — without it, `/unpark` sees "now" with nothing
to compare against. Include the exact command to re-fetch each source.

3. **Observed shape the page assumes** — a **confidence-tagged** summary of the API/behaviour
the docs commit to. Tag it (`LOW`/`MEDIUM`/`HIGH`) — preemptive docs written against an
unmerged diff are usually LOW, and saying so tells pickup how hard to scrutinise.
unmerged diff are usually LOW, and saying so tells unpark how hard to scrutinise.

4. **Re-check checklist** — GitHub task-list (`- [ ]`) of everything to verify or finish on
pickup. **Seed it from the branch's commit trailers** — harvest `Recheck:` / `Gaps:` /
unpark. **Seed it from the branch's commit trailers** — harvest `Recheck:` / `Gaps:` /
`Directive:` from `main..HEAD` (see [`commit-trailers.md`](./commit-trailers.md)); those
*are* the loose ends. Add anything predicted-to-change not already captured. Mark the
highest-risk items.

5. **On pickup, then** — when the trigger fires, run `/pickup <PR>`. It reconciles the docs
5. **On unpark, then** — when the trigger fires, run `/unpark <PR>`. It reconciles the docs
against the now-settled source and takes the PR through the normal `/reflect` → `/finalize`
pipeline to merge, lifting the `parked` / `do not merge yet` labels only after `/finalize`.
**The closing sequence and its ordering live solely in `/pickup` Steps 4–5, which are
**The closing sequence and its ordering live solely in `/unpark` Steps 4–5, which are
authoritative; this manifest deliberately does not restate them — not even as a list.** (A
hand-copied step list here drifted from `/pickup` on every review round, so it was replaced by
hand-copied step list here drifted from `/unpark` on every review round, so it was replaced by
this pointer. Don't re-add one, in any order.) The one guarantee this section makes: the `do not merge yet` guard holds until
`/finalize` completes.

Expand All @@ -78,8 +78,8 @@ not restate them as trailers.
## Finalize is deferred while parked

Critical: **do not run `/finalize` at park time.** Finalize squashes away the episodic
`Recheck:` / `Gaps:` trailers — exactly what `/pickup` needs. The durable distill happens
*after* pickup, once the source has settled. The manifest's "On pickup, then" section records
`Recheck:` / `Gaps:` trailers — exactly what `/unpark` needs. The durable distill happens
*after* unpark, once the source has settled. The manifest's "On unpark, then" section records
this so it isn't forgotten.

## Labels
Expand All @@ -88,4 +88,4 @@ this so it isn't forgotten.
goes ahead."
- `do not merge yet` — the merge guard.

`/pickup` finds parked PRs with `gh pr list --label parked`.
`/unpark` finds parked PRs with `gh pr list --label parked`.
20 changes: 10 additions & 10 deletions .claude/skills/park/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: park
description: "Freeze a release-ready-pending-upstream branch as a parked PR — open (or update) the PR with a machine-diffable pickup manifest, apply the parked / do-not-merge labels, and deliberately defer /finalize so the episodic re-check notes survive. Use when docs are written against an unreleased or still-changing external source (an upstream PR, a preview feature) and must wait for it to ship before merging. Pairs with /pickup."
description: "Freeze a release-ready-pending-upstream branch as a parked PR — open (or update) the PR with a machine-diffable unpark manifest, apply the parked / do-not-merge labels, and deliberately defer /finalize so the episodic re-check notes survive. Use when docs are written against an unreleased or still-changing external source (an upstream PR, a preview feature) and must wait for it to ship before merging. Pairs with /unpark."
---

# Park — freeze a PR that's waiting on the world
Expand All @@ -27,16 +27,16 @@ you'll harvest. This file is *how to build and land the manifest*.
↓ THIS SKILL — when the work can't merge yet
/park → parked PR + manifest [frozen; /finalize DEFERRED]
↓ (wait for the upstream trigger)
/pickup → reconcile docs vs source [thaw + delta]
/unpark → reconcile docs vs source [thaw + delta]
/reflect+/finalize → durable squash commit [resume the normal pipeline]
```

## The one principle: snapshot the source, not just a pointer

A note that says "watch upstream PR X" is nearly useless on pickup — it tells you where to
A note that says "watch upstream PR X" is nearly useless on unpark — it tells you where to
look but not *what changed*. The manifest must capture the source's **state at park time**
(merge status, head SHA, milestone, the observed API shape) so pickup is a **diff**, not a
(merge status, head SHA, milestone, the observed API shape) so unpark is a **diff**, not a
fresh read. Recording the snapshot is the whole value of parking; everything else is
plumbing.

Expand Down Expand Up @@ -79,7 +79,7 @@ gh api repos/<owner>/<repo>/issues/<n> \
--jq '{state, state_reason, milestone: .milestone.title, updated_at, closed_at}'
```

Record URL + snapshot in the sources table, and the re-fetch command for pickup. Add a
Record URL + snapshot in the sources table, and the re-fetch command for unpark. Add a
**confidence-tagged** summary of the API/behaviour the page assumes (LOW for docs written
against an unmerged diff — and say *why* it's low, e.g. "signatures differed between two reads
of the diff").
Expand Down Expand Up @@ -108,7 +108,7 @@ required sections (per the shared spec). The page itself should already carry a
gh pr edit <n> --body-file <body> --add-label parked --add-label "do not merge yet"
```
- Verify the labels stuck (`gh pr view <n> --json labels`).
- **Do not run `/finalize`.** State explicitly that it's deferred until pickup — the manifest
- **Do not run `/finalize`.** State explicitly that it's deferred until unpark — the manifest
records this, but say it in your handoff too.

Present the PR URL, the trigger condition, and the checklist, then stop. Opening a PR is
Expand All @@ -117,13 +117,13 @@ outward-facing — if the branch isn't pushed or the user hasn't asked, confirm
## Limits (read honestly)

- **A stale snapshot is worse than none** — if you record a head SHA or API shape you didn't
actually verify, pickup diffs against fiction. Snapshot only what you checked; leave the
actually verify, unpark diffs against fiction. Snapshot only what you checked; leave the
rest out and flag it in the checklist.
- Park **cannot judge whether the source will ship.** It records a trigger; it doesn't predict
the future. A parked PR that never triggers is dead weight — `/pickup`'s scan mode is how you
the future. A parked PR that never triggers is dead weight — `/unpark`'s scan mode is how you
find and close those.
- It **defers, never distills.** If you're tempted to "just finalize while it's fresh," don't:
you'll squash away the very notes pickup needs. Freshness is preserved *in the manifest*, not
you'll squash away the very notes unpark needs. Freshness is preserved *in the manifest*, not
in a premature durable commit.
- The manifest is only as good as the checklist. If Step 2's harvest is skipped, the loose ends
live only in scattered trailers and the pickup starts blind.
live only in scattered trailers and the unparking starts blind.
20 changes: 10 additions & 10 deletions .claude/skills/pickup/SKILL.md → .claude/skills/unpark/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
name: pickup
name: unpark
description: "Thaw a parked PR — re-fetch each pinned source, diff it against the manifest snapshot taken at park time, report what changed vs what was predicted, then reconcile the docs and resume the normal pipeline (/reflect → /finalize) toward merge. Run with a PR number, or with no argument to scan all parked PRs and report which triggers have fired. Pairs with /park."
---

# Pickup — thaw a parked PR against its moved source
# Unpark — thaw a parked PR against its moved source

`/park` froze a PR with a snapshot of the source it tracks. Time passed; the source moved —
the upstream PR merged, the API changed, a version shipped. This skill **thaws the PR**: it
Expand All @@ -20,7 +20,7 @@ vocabulary for the reflect/finalize handoff is in
```
/park → parked PR + manifest snapshot [frozen]
↓ (the trigger fires)
/pickup → diff source now vs snapshot [THIS SKILL: thaw + reconcile]
/unpark → diff source now vs snapshot [THIS SKILL: thaw + reconcile]
/reflect+/finalize → durable squash commit [resume: predicted-vs-actual, distill]
```
Expand All @@ -45,10 +45,10 @@ For each, read the manifest's `Trigger to pick up:`, snapshot the current source
re-fetch only — don't do the full reconcile), then compare that state to the trigger condition to
decide **fired?**. Use only Step 2's *comparison* — **not** its stop-and-report rule: in scan mode
a not-met trigger is recorded as `fired?=no` and you continue to the next PR, never aborting the
loop. Output a table: PR / trigger / **fired?** / one-line delta. Recommend which to pick up, which are still waiting, and which look abandoned (source
closed-unmerged, or long dead). Then stop — picking one up is Mode B.
loop. Output a table: PR / trigger / **fired?** / one-line delta. Recommend which to unpark, which are still waiting, and which look abandoned (source
closed-unmerged, or long dead). Then stop — unparking one is Mode B.

## Mode B — pick up one PR (PR number given)
## Mode B — unpark one PR (PR number given)

### Step 1 — Extract the manifest & re-fetch sources

Expand All @@ -60,7 +60,7 @@ each pinned source, run its recorded re-fetch command and capture current state.
Compare current vs park-time, field by field. Has `merged` flipped true? Has `head SHA` moved
(the diff may have changed under review)? Is a `milestone` / version now set? For a merged PR,
read the **released** source at the merged tag, not the PR branch. Confirm whether the trigger
condition is genuinely met — **if it isn't, stop and report**; picking up early re-freezes bad
condition is genuinely met — **if it isn't, stop and report**; unparking early re-freezes bad
info.

### Step 3 — Walk the checklist
Expand All @@ -80,7 +80,7 @@ changes land (including the rebase and link checks) come next, in order.

### Step 5 — Resume the pipeline & hand off

Pickup **reconciles; it does not merge.** `/finalize` prepares a squash that operates on the
Unpark **reconciles; it does not merge.** `/finalize` prepares a squash that operates on the
**pushed remote PR head**, so the invariant governing this whole step is:

> Before the `gh pr merge` hand-off, the remote PR branch must already contain **everything
Expand All @@ -101,7 +101,7 @@ enumerating the exact git steps here drifted out of date every review round.
3. **Remote head == local** — everything above pushed. This is the invariant made true; nothing
reaches the squash until it is.
4. **`/finalize`** run — the durable squash deferred at park time, now that the source has
settled; it reconciles the whole arc (park notes + pickup findings + any review) and prepares
settled; it reconciles the whole arc (park notes + unpark findings + any review) and prepares
the `gh pr merge … --body-file` command.
5. **Labels dropped last** — remove `parked` / `do not merge yet` and strip the manifest block
only after `/finalize`. The merge guard holds until then.
Expand All @@ -118,7 +118,7 @@ ahead of `origin`). Then present: the delta report, the doc changes, the finaliz
- It **trusts the snapshot's honesty.** If `/park` recorded an unverified shape, the diff is
against fiction — so re-verify high-risk checklist items from source regardless of what the
snapshot claimed, especially anything tagged LOW.
- It **can't recover a checklist that was never written.** With a thin manifest, pickup
- It **can't recover a checklist that was never written.** With a thin manifest, unpark
degrades to a blind re-review — still useful, but the park-time context is gone.
- Reconciliation edits are real doc changes: drive/verify them like any other, don't assume the
released API matches even a HIGH-confidence snapshot.
Loading