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
10 changes: 7 additions & 3 deletions SOURCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,8 +517,10 @@ History-changing operations use the shared PR preservation reference to back up
retained local files affected by tracking deletions and verify restoration.

Scoped PR layers follow `pr`’s publish step, which links every layer in T3; CLI
publication does not substitute for that link. Layers get `pr`’s review step before
publication does not substitute for that link. Layers get `pr`’s Prepare and Review steps before
`submit`, or `link` when it creates PRs or marks them ready, and `gh stack merge` runs only from `merge`’s land step.
Each layer runs its held `review-fix-loop` status command unchanged once its SHA is pushed;
after a restack, the loop reruns for that layer as a repair pass.

Ordinary publication uses `submit --auto --open` or `link --open`, with drafts only
on request, to match `pr`. Branch deletion after a merge goes through `repo-cleanup`
Expand Down Expand Up @@ -1068,10 +1070,12 @@ other nonignored untracked source.
- [mattpocock/skills / skills/engineering/pr](https://github.com/mattpocock/skills/tree/d81f3a183412e71a5b1e84ca21bc1a35eea03a60/skills/engineering/pr) — PR-body structure, visual selection, evidence, and merge-risk guidance adapted into `references/pr-body.md`; import baseline `d81f3a183412e71a5b1e84ca21bc1a35eea03a60`. Upstream credits [HumanLayer / show-me](https://github.com/humanlayer/skills/tree/ca7c8088db69e315a8b2deea43820270457f8f3c/plugins/show-me/skills/show-me) for visual guidance; the origin revision used by Matt is unrecorded. HumanLayer MIT notice checked at `ca7c8088db69e315a8b2deea43820270457f8f3c` in `licenses/humanlayer-skills.txt`.

The local name and UI are `pr`, with automatic discovery. It owns PR text, publication, and
watching. Fixing feedback moved to `review-sweep` and merging to `merge`. Publishing reuses
matching review evidence, and runs `show-dont-tell` then the review loop when there is none.
watching. Fixing feedback moved to `review-sweep` and merging to `merge`. Publishing runs `show-dont-tell`, commits, then runs the review
loop on that commit and pushes after it; earlier review counts only for the same commit.
`scripts/pr_state.py` reads one head's checks, threads, comments, and reviews through every page,
replacing hand-read readiness with `blockers`, `pending`, and `to_settle`.
It also gates on the head's `review-fix-loop` commit status context, kept apart from CI
checks, including a same-named check run: anything but success blocks.
The body guide uses Summary/Evidence/Merge Danger by default, defers to project
PR templates, and preserves local why/effect/root-cause/stack facts. It expands
visual selection to algorithm, runtime, UI, file, interaction, diff, and complete
Expand Down
6 changes: 5 additions & 1 deletion skills/gh-stack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ their callers and use separate stacks for independent work.
[PR's publish step](../pr/SKILL.md#publish). A CLI operation doesn't link its layers
to the thread.
- Before `gh stack submit`, or `gh stack link` when it creates PRs or marks them ready, run
[PR's review step](../pr/SKILL.md#publish) on each layer against its immediate base.
PR's [Prepare and Review steps](../pr/SKILL.md#publish) on each layer against its immediate
base.
Run each layer's held `review-fix-loop` status command unchanged once its SHA is pushed, as
[Review Fix Loop's finish step](../review-fix-loop/SKILL.md#6-finish) says. After a restack,
rerun the loop for that layer as a [repair pass](../review-fix-loop/SKILL.md#4-route-repairs).
Land through [Merge](../merge/SKILL.md): run
`gh stack merge` only from its land step.

Expand Down
6 changes: 3 additions & 3 deletions skills/implement/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ reported as a gap, and the user can open what you showed.
keep: go to Merge, which runs review, opens the PR, and lands it. Changes go
back to stage 3. Scrap goes to [Repo Cleanup](../repo-cleanup/SKILL.md).
- **PR:** PR runs review, publishes, and watches to the requested state.
- **Land:** go to Merge; a stack lands bottom-up as one merge set. Until hard
gates exist, Merge's requirements are the gate: review of the current head,
required checks, and passing proof. Land is done when GitHub reports every PR
- **Land:** go to Merge; a stack lands bottom-up as one merge set. Merge's
requirements are the gate: review of the current head, which `pr_state`
enforces, required checks, and passing proof. Land is done when GitHub reports every PR
merged and the result is shown. A closed PR did not land.

Provisional work never merges: Land becomes Try, and a requested draft or
Expand Down
16 changes: 10 additions & 6 deletions skills/merge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ limits.
PR open and use [Show Me](../show-me/SKILL.md) to show the user what happened, including
anything that landed first.

`pr_state` blocks a head without a passing `review-fix-loop` status, including after a base
update or restack. A caller with its own repair owner gets the gap back. Without one, take
the head through PR's [Prepare and Review steps](../pr/SKILL.md#publish), publish any fixes,
and read the state again. A new head of reviewed work is a
[repair pass](../review-fix-loop/SKILL.md#4-route-repairs).

A caller that keeps its own repair owner gets each failure, comment, and stop back, and resumes
that owner. You repair nothing.

Expand All @@ -42,12 +48,10 @@ limits.
branch, or for a stack layer its parent in the scoped set. A different base is a stop.

Every PR, including docs-only changes, needs review of its current head that includes
[Show, Don't Tell](../show-dont-tell/SKILL.md), plus required verification. Reuse matching
evidence. A scoped rerun after a repair counts. So does review from before the base moved,
once you've read the new base commits and they don't affect the files or behavior this change
depends on. If evidence is missing or stale, a caller with its own repair owner gets the gap
back. Otherwise run [PR's review step](../pr/SKILL.md#publish), publish any fixes, and go back
to step 2.
[Show, Don't Tell](../show-dont-tell/SKILL.md), plus required verification. The head's
passing `review-fix-loop` status records the review and its Show, Don't Tell. Step 2 restores
a missing one. Route missing or stale verification the same way as a stale review, then go
back to step 2.

A head you didn't verify holds commits nobody reviewed. Read them, then go back to step 2. It
counts as verified once the evidence above covers it.
Expand Down
25 changes: 14 additions & 11 deletions skills/pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ remote description. Writing text commits and pushes nothing.

## Publish

Watching an existing PR starts at step 3.

1. **Get it reviewed.** Reuse review evidence that matches the complete diff against the
immediate base and included [Show, Don't Tell](../show-dont-tell/SKILL.md). Otherwise run
Show, Don't Tell on that diff, including docs-only changes, then
[Review Fix Loop](../review-fix-loop/SKILL.md) on the result. Continue once review and
verification pass.
2. **Publish it.** Follow [publish](references/publish.md). Publish ready for review unless
the user asks for a draft. In T3, link each PR you create, push to, or take over, including
every stack layer.
3. **Watch it.** Follow [watch](references/watch.md).
Watching an existing PR starts at step 4.

1. **Prepare it.** Run [Show, Don't Tell](../show-dont-tell/SKILL.md) on the complete diff
against the immediate base, including docs-only changes, unless earlier evidence covers
this change. Then [commit](references/publish.md#commit).
2. **Get it reviewed.** Run [Review Fix Loop](../review-fix-loop/SKILL.md) on the committed
head. A new head of reviewed work, such as after a base update or restack, is a
[repair pass](../review-fix-loop/SKILL.md#4-route-repairs). Earlier review of this exact
commit [finishes it](../review-fix-loop/SKILL.md#6-finish) and posts the status. Continue
once review and verification pass.
3. **Publish it.** Follow [publish](references/publish.md#push). Publish ready for review
unless the user asks for a draft. In T3, link each PR you create, push to, or take over,
including every stack layer.
4. **Watch it.** Follow [watch](references/watch.md).

## Report

Expand Down
22 changes: 14 additions & 8 deletions skills/pr/references/publish.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
# Publish

## Commit and push
## Commit

Inspect status, the complete diff, and staged files. Stage only owned paths and
preserve unrelated work. Keep the feature branch; on the default branch, create
Commit before review, so the review covers a committed head. Inspect status, the
complete diff, and staged files. Stage only owned paths and preserve unrelated
work. Keep the feature branch; on the default branch, create
`agent/<short-description>` first. Use the caller's immediate base, including
a parent layer for stacks.

Commit owned edits with a terse subject, run checks needed by final staging,
and push with upstream tracking. Push existing ahead commits without an empty
commit; reuse an already published head when nothing changed. For a caller-owned
restack, use force-with-lease only on the named branch after review. Return a
lease failure to the caller as changed remote state.
Commit owned edits with a terse subject, and run checks needed by final staging.

## Push

Push after review, with upstream tracking. Push existing ahead commits without an
empty commit; reuse an already published head when nothing changed. Then run any
held `review-fix-loop` status command as
[Review Fix Loop's finish step](../../review-fix-loop/SKILL.md#6-finish) says. For a
caller-owned restack, use force-with-lease only on the named branch after review.
Return a lease failure to the caller as changed remote state.

Before rebasing or restacking, follow [Preserve local files](preserve-local-files.md)
for retained files affected by tracking deletions. Verify restoration after the
Expand Down
7 changes: 7 additions & 0 deletions skills/pr/references/watch.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ when a result looks wrong.
fixes something, check that review evidence covers the change. If it's missing or stale,
rerun [review](../SKILL.md#publish) on the affected scope. Then [publish](publish.md), post
held replies, and read the state again.
- **No passing review status:** take the head through PR's
[Prepare and Review steps](../SKILL.md#publish), unless posting its status already failed;
then report that blocker. A new head of reviewed work is a
[repair pass](../../review-fix-loop/SKILL.md#4-route-repairs).
- **A missing check:** explain it, including workflows filtered out by a stacked PR's base or
by draft status.
- **Behind or conflicted:** read what the new base commits touch, then decide with
Expand Down Expand Up @@ -64,6 +68,9 @@ Ready means `pr_state` reports `ready`, every `to_settle` item has a decision, a
[feedback handling](../../review-sweep/references/pr-feedback.md#finish) is complete. Report
any unmet repository requirement.

`pr_state` blocks until the head has a passing `review-fix-loop` commit status. It reports that
status under `review_status`, apart from `checks`.

GitHub stops nothing on a repo with no required checks, so `pr_state` then holds every check to
that bar: a failure blocks, and a head with workflows but no checks waits. One of these still
counts as ready once Review Sweep dismissed it for this head with evidence, such as the same
Expand Down
17 changes: 15 additions & 2 deletions skills/pr/scripts/pr_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
PROVISIONAL_HEADING = "## Provisional"
PROVISIONAL_BLOCKER = (f"the body has an open '{PROVISIONAL_HEADING}' section; "
"it must not merge until that section is removed")
REVIEW_CONTEXT = "review-fix-loop"
REVIEW_BLOCKER = f"no passing '{REVIEW_CONTEXT}' status on this head; run Review Fix Loop on it"

# baseRefOid stops at the PR's last sync with its base, so read the branch's live tip instead.
SUMMARY_QUERY = """
Expand Down Expand Up @@ -210,6 +212,10 @@ def check_result(node):
return {"name": name, "state": state, "required": node["isRequired"], "url": url}


def is_review_record(node):
return node["__typename"] == "StatusContext" and node["context"] == REVIEW_CONTEXT


def has_workflows(gh, pr, head):
pages = graphql_pages(gh, WORKFLOWS_QUERY, owner=pr["owner"], name=pr["name"],
expression=f"{head}:.github/workflows")
Expand All @@ -227,7 +233,9 @@ def read_state(gh, pr):
"""Read until the PR holds still, so every part describes one state of it."""
for _ in range(READ_ATTEMPTS):
summary = read_summary(gh, pr)
checks = [check_result(node) for node in all_nodes(gh, CHECKS_QUERY, pr, checks_page)]
nodes = all_nodes(gh, CHECKS_QUERY, pr, checks_page)
records = [check_result(node) for node in nodes if is_review_record(node)]
checks = [check_result(node) for node in nodes if not is_review_record(node)]
state = {
"summary": summary,
"behind_by": commits_behind_base(gh, pr, summary),
Expand All @@ -236,6 +244,7 @@ def read_state(gh, pr):
"comments": all_nodes(gh, COMMENTS_QUERY, pr),
"reviews": all_nodes(gh, REVIEWS_QUERY, pr),
"checks": checks,
"review_status": records[0] if records else None,
"expects_checks": bool(checks) or has_workflows(gh, pr, summary["headRefOid"]),
}
if read_summary(gh, pr) == summary:
Expand Down Expand Up @@ -274,7 +283,9 @@ def assess_merge_state(state, blockers, pending):
blockers.append("the branch is behind its base")
elif merge_state == "BLOCKED" and not (pending or blockers):
blockers.append("GitHub reports an unmet merge requirement")
elif merge_state == "UNSTABLE" and all(check["state"] == "pass" for check in state["checks"]):
elif merge_state == "UNSTABLE" and all(
check["state"] == "pass" for check in [*state["checks"], state["review_status"]]
if check):
blockers.append("GitHub reports a failing status that no check here shows; "
"read the head and test-merge targets")

Expand All @@ -293,6 +304,8 @@ def assess(state, expected_head=None):

if state.get("provisional"):
blockers.append(PROVISIONAL_BLOCKER)
if (state["review_status"] or {}).get("state") != "pass":
blockers.append(REVIEW_BLOCKER)
if summary["isDraft"]:
blockers.append("the PR is a draft")
if summary["reviewDecision"] == "CHANGES_REQUESTED":
Expand Down
Loading
Loading