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
2 changes: 2 additions & 0 deletions .agents/skills/audit-a-repo/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ The audit is the fleet's measurement procedure, and the failure shapes it guards
## After the Report

Measuring and fixing are separate phases. Converging is `AUDIT.md` section 10: fixes ship as pull requests on the target repo, one focused pull request per drift class, the Copilot loop driven to green per the `pr-review-conduct` skill, and the maintainer merges. For a repo already stood up, `RESYNC.md` sequences the findings, since order matters (a deletion lands before the re-vendor that would refresh it). Systemic drift shared by many repos is fixed in the hub spec, not hand-patched per repo, and spec questions are escalated rather than resolved silently (`AUDIT.md` section 9).

An audit leaves no checkout behind it, since one left standing is what a later sweep has to tell apart from live work. Each is removed when the run ends, per `repo-worktree` "Listing and Cleanup". The report's pull request is cleaned up once it merges, with `python3 scripts/pr_review.py cleanup <number> --repo ptr727/ProjectTemplate --checkout <hub-base-clone>`. A detached checkout at `main` that only read the hub is removed by hand once it is clean.
11 changes: 5 additions & 6 deletions .agents/skills/backlog-burndown/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,13 @@ Brief on `AGENTS.md` "Context and Delegation Discipline"'s subagent shape.
`repo-worktree`'s post-merge procedure returns the base clone to current develop before proving
the cleanup, and `branching-and-release-model` states that requirement independently. Four workers doing
that concurrently mutate one shared checkout, which `GOVERNANCE.md` "Repository Boundaries and
Write Safety" forbids. A worker also cannot
finish the procedure from inside its own worktree, since removing that worktree leaves it with no
working directory in which to delete its branch.
Write Safety" forbids.

So the whole procedure moves to the orchestrator, which runs it from the base clone at the round's
So the whole procedure moves to the orchestrator, which runs it against the base clone at the round's
cleanup step, while no worker is live in a tree it touches.
It carries the remote half of `drive-pr`'s post-merge cleanup too.
That half is `python3 scripts/pr_review.py cleanup <number> --repo <owner>/<repo>`, run from a hub checkout once for each merged group.
For a merged group both halves are one command, `python3 scripts/pr_review.py cleanup <number> --repo <owner>/<repo> --checkout <base-clone>`, run from a hub checkout once for each merged group, `<base-clone>` being the repository's primary clone.
It deletes the remote branch, then removes the group's worktree and local branch, per `repo-worktree` "Listing and Cleanup", which also says what its exit leaves to do.
Taking that step from the worker without naming a new owner would leave a live remote branch behind every group.
`cleanup` holds the delete's checks, among them the tip against the pull request's `headRefOid`.
It also refuses a branch another open pull request has as its head or base.
Expand Down Expand Up @@ -314,7 +313,7 @@ An abandoned group, and a dead worker's clean tree, have no merged pull request
`repo-worktree`'s verify-before-removing step to read, so the check that step gives way to here is
what it exists to establish, that nothing unmerged is
being thrown away: confirm the branch carries no commit that is not already on develop, and that
its worktree is clean. Both hold, and the worktree and branch go the same way a merged group's do.
its worktree is clean. Both hold, and the worktree and local branch are removed by hand, since `cleanup` needs a merged pull request to read: `git worktree remove <exact-path>`, then `git branch -d <exact-branch>`, which refuses rather than discards where that check was wrong, a refusal stopping the group for the maintainer per "Raising a Blocked Question".
Its claim comment is released and its issue returns to the next round's ranking.
A remote branch that was pushed is reported to the maintainer rather than deleted, since `cleanup` needs a merged pull request to read.
No remote branch is left to report where none was pushed. Either fails, and cleanup stops there and
Expand Down
12 changes: 7 additions & 5 deletions .agents/skills/drive-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ promotion PR once the fix lands, is the early exit this skill exists to prevent.
call, it is run from inside the task's own worktree per step 1, where the feature branch is
checked out, and `gh pr merge --delete-branch` needs to switch that worktree to the base branch
to delete it, which fails when `develop` is already checked out somewhere else, the ordinary
case in this layout. Instead run repo-worktree's post-merge cleanup from the base clone, remove
the worktree and delete the now-merged local task branch, then delete the remote one, which is this
skill's own step rather than that one's, with
`python3 scripts/pr_review.py cleanup <number> --repo <owner>/<repo>`, run from a hub checkout.
case in this layout. Instead run repo-worktree's post-merge cleanup with
`python3 scripts/pr_review.py cleanup <number> --repo <owner>/<repo> --checkout <base-clone>`,
run from a hub checkout outside the task's worktree, `<base-clone>` being the repository's
primary clone. It deletes the remote branch, then removes the task's worktree and local branch.
It decides its local refusals before the remote delete, and `repo-worktree` "Listing and
Cleanup" says what a refusal after it and a `base=kept` line each leave to do.
It takes the pull request rather than a branch name and does the verify-then-delete in
code. It refuses unless the pull request merged into `develop` from a head in that same
repository, and refuses a permanent branch or one any ruleset or branch protection covers. It
Expand All @@ -122,7 +124,7 @@ promotion PR once the fix lands, is the early exit this skill exists to prevent.
It names each, since the delete would close or retarget it.
Where this seat cannot run `cleanup`, report the delete to the maintainer instead of deleting by hand.
Name the pull request number and `--repo` so the maintainer runs the same command.
No direct `git push origin --delete` or ref API delete runs here, since `cleanup` holds the checks.
No direct `git push origin --delete`, ref API delete, `git worktree remove`, or `git branch -D` runs here, since `cleanup` holds the checks.
A pull request opened from a fork follows `upstream-contribution-workflow` instead, and `cleanup` refuses it.
The
repo's auto-delete-head-branches setting is kept off fleet-wide (to protect `develop` and
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/git-commit-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,4 @@ any rewrite. See `references/history-rewrite.md` for the full two-gate rule.

## Never run destructive git commands without being asked

`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and other commands that discard work require explicit developer instruction. Never use them as a convenience inside a larger task. One narrow cleanup exception applies to `git branch -D <exact-task-branch>` after a squash merge. It requires live proof that the pull request for that exact branch merged and a clean worktree at the verified head SHA, per `repo-worktree`. The exception never applies to `develop`, an unmerged branch, an unresolved pull request, or a branch with uncommitted work.
`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and other commands that discard work require explicit developer instruction. Never use them as a convenience inside a larger task. One narrow cleanup exception applies to `git branch -D <exact-task-branch>` after a squash merge. It requires live proof that the pull request for that exact branch merged and a clean worktree with the branch at the verified head SHA or behind it, per `repo-worktree`. The exception never applies to `develop`, an unmerged branch, an unresolved pull request, or a branch with uncommitted work.
93 changes: 25 additions & 68 deletions .agents/skills/merge-and-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,77 +169,34 @@ skill covers all of it, scoped down by what the maintainer actually asks for.
not have, an interrupted loop, a fix landed by hand outside that skill, or a maintainer
merge in the GitHub UI. `git worktree list` for any worktree still registered under this
task's feature branches, `git branch -vv` for any local feature branch, `git ls-remote
--heads origin` for any matching remote feature branch. For each, verify it finished by
reading GitHub's own state with the exact fields this check needs, not a bare listing, and
stop and report rather than guessing when selection is not exactly one match, on a non-1
count exit non-zero rather than returning empty with success, an ambiguous or missing match
must fail loud, not read as an empty value still safe to act on: `gh pr list --head
"<branch>" --state merged --repo owner/repo --json
number,baseRefName,mergedAt,headRefOid,headRefName,headRepository --jq 'if length == 1 then
.[0] else error("expected exactly one merged PR for this head, got \(length)") end'`.
`--head` is expected to match exactly (verified against `gh` 2.97.0 on this repo, a bare
prefix of a real branch name returned nothing), but confirming `headRefName` equals `<branch>`
costs one field and is cheap insurance against a future `gh` behavior change, not a workaround
for a known partial-match case. Confirm `headRepository` is non-null and its `nameWithOwner` equals
`owner/repo`, the owner alone is not enough, a same-owner PR against an identically named
branch in a different repository must never pass this check either. Confirm `baseRefName`
is `develop` (a different merged pull request can share the same head branch name against a
different base, and that is never this sweep's target) and `mergedAt` is set. Compare tips
only where a remote branch actually exists. `git ls-remote --heads --exit-code -- origin
"refs/heads/<branch>"` is the exact-match form and must be, in that argument order. `--heads
origin "<branch>"` alone still tail-matches, a bare `topic/x` pattern also returns an unrelated
`other/topic/x` if one exists. `--` placed after `origin` instead of before it is not
equivalent either, verified empirically: with a `refs/heads/other/--` ref present, `--heads
origin -- "refs/heads/<branch>"` matched both that ref and the intended one, while `--heads --
origin "refs/heads/<branch>"` matched only the one intended. Exit status is a tri-state, not a
stdin-emptiness check: `--exit-code` makes exit `2` mean query succeeded, branch gone, most
likely a prior cleanup attempt got interrupted after the remote delete but before the local
one, so skip straight to the local-tip check below and never attempt the remote delete a
second time. Exit `0` means it matched. Anything else is a failed query, a network or auth
problem, and stops and reports rather than being read as absence, an unreachable remote and a
genuinely gone branch both print nothing to stdout, only the exit code tells them apart.
Where the remote branch does exist, its tip must match that exact pull request's `headRefOid`
before its own delete proceeds, proving nothing landed on it since. Where a local branch
still exists too, its tip (`git rev-parse --verify "refs/heads/<branch>"`) must independently
match `headRefOid` before its own delete proceeds. Neither side needs the other to exist, a
prior interrupted attempt may have deleted one side already and left only the other, so
verify and delete whichever side is still there and skip whichever already is not, never
block one side's cleanup on the other side's absence. No `--` on `rev-parse`, verified
empirically: `git rev-parse -- "<branch>"` treats the argument after `--` as a path rather
than a revision and never resolves a SHA at all. The fully-qualified form needs no `--`
regardless, since `refs/heads/<branch>` never itself starts with `-`, and `--verify` fails
loudly rather than guessing when it does not resolve. Every branch or
worktree-path placeholder below is the real value, substituted as its own quoted argument
(a shell variable expansion such as `"$branch"`, or an argv element), never handed to `eval`
or `sh -c` for a second round of shell parsing, the only way an embedded `$()` or backtick
would actually run. A valid ref can start with `-` or carry a shell metacharacter, which is
why it stays quoted regardless. `--` marks the
end of options wherever a command supports it.
`git merge-base --is-ancestor <branch> develop` must never be used for either tip check, a
squash merge (drive-pr's own merge method) never makes the feature tip a literal ancestor of
`develop`, so the check reports every already-finished branch as unmerged. Only once GitHub
confirms it, and only when a local worktree or branch is still there to remove, remove the
worktree by its exact path (a dirty worktree stops cleanup rather than discarding uncommitted
work), `git worktree remove "<worktree-path>"`, `git worktree list` names it, then delete the
local branch. `git branch
-d` has the identical squash blindness as `git merge-base --is-ancestor` and refuses too, so
use `git branch -D -- "<exact-branch>"` here, safe only because the GitHub-state check just
proved that exact branch finished, the narrow post-squash exception git-commit-conventions
describes, never applied to an unverified branch. Then, only when the remote branch still
exists, delete it with `python3 scripts/pr_review.py cleanup <number> --repo owner/repo`, run
from a hub checkout, `<number>` being the merged pull request the check above returned. It
re-reads that pull request and repeats the merged, base, head-repository, and tip checks in
code, refuses a permanent or protected branch, and confirms the delete by reading the ref
back. Exit `0` is done, and any other exit stops and reports: `1` is a refusal, `2` an
unanswered read or an unconfirmed delete, and `64` a target under an owner other than the hub
checkout's own `origin` or that `origin` unreadable, a registry refusal being a `1`. It runs
under the host-safety installer's `Bash(python3 scripts/pr_review.py:*)` allow rule,
which is what lets auto mode complete it.
--heads origin` for any matching remote feature branch. For each such branch, read the one
merged pull request into `develop` it headed, failing loud on any count other than one rather
than reading an empty answer as safe: `gh pr list --head "<branch>" --base develop --state
merged --repo owner/repo --json number --jq 'if length == 1 then .[0].number else
error("expected exactly one merged PR for this head, got \(length)") end'`. Then run
`python3 scripts/pr_review.py cleanup <number> --repo owner/repo --checkout <base-clone>`
from a hub checkout outside the worktree it removes, `<base-clone>` being the repository's
primary clone, per `repo-worktree` "Listing and Cleanup". It re-reads that pull request and
repeats the merged, base, head-repository, and tip checks in code, refuses a permanent or
protected branch, deletes the remote branch and confirms it by reading the ref back, treats
a remote branch already gone as done, so a prior interrupted attempt finishes rather than
stops, then removes the worktree and local branch. Its local refusals, a dirty worktree
among them, are decided before the remote delete, and `repo-worktree` "Listing and Cleanup"
says what a refusal after it and a `base=kept` line each leave to do. Exit `0` is done, and
any other exit stops and reports: `1` is a refusal, `2` an unanswered read or an unconfirmed
delete, and `64` a target under an owner other than the hub checkout's own `origin` or that
`origin` unreadable, a registry refusal being a `1`. It runs under the host-safety
installer's `Bash(python3 scripts/pr_review.py:*)` allow rule, which is what lets auto mode
complete it.
It also refuses while another open pull request in the repository has the branch as its head or base, naming each.
Where this seat cannot run `cleanup`, report the delete to the maintainer instead of deleting by hand, naming the pull request number and `--repo`.
No direct `git push origin --delete` or ref API delete runs from this skill, because `cleanup` is the one place holding the checks.
Where this seat cannot run `cleanup`, report the cleanup to the maintainer instead of running its steps by hand, naming the pull request number and `--repo`.
No direct `git push origin --delete`, ref API delete, `git worktree remove`, or `git branch -D` runs on a feature branch from this skill, because `cleanup` is the one place holding the checks.
Never apply this sweep to `develop` or `main`
themselves, only to feature branches a drive-pr loop created.
- What no single pull request names: run
`python3 scripts/pr_review.py cleanup --sweep --repo owner/repo --checkout <base-clone>`
and report what it lists. It writes nothing but a fetch, and `--apply` runs only on the
maintainer's go-ahead, since the report reaches other sessions' finished work too.

## Mechanics Live Elsewhere

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/pr-review-conduct/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ is not a stopping condition here, and neither is patience running out. Reporting
PR was opened is an early exit unless the maintainer explicitly instructed the agent not to
monitor or drive its review.

After an authorized merge, run the `repo-worktree` post-merge cleanup procedure unless the user explicitly asks to retain the checkout or branch. The pull request loop is incomplete while its finished worktree or local task branch remains. It is also incomplete until the base clone returns to fetched and fast-forwarded `develop`.
After an authorized merge, run the `repo-worktree` post-merge cleanup procedure unless the user explicitly asks to retain the checkout or branch. For a merge into `develop` that procedure is `python3 scripts/pr_review.py cleanup <number> --repo <owner>/<repo> --checkout <base-clone>`, run from a hub checkout. The pull request loop is incomplete while its finished worktree or local task branch remains, except a promotion branch merged into `main`, which `repo-worktree` leaves for its sweep. It is also incomplete until the base clone returns to fetched and fast-forwarded `develop`.

## Every finding ends in one of five outcomes

Expand Down
Loading
Loading