@muggleai/works 5.6.0 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-LTULYOOW.js → chunk-5PYK7DWB.js} +15 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/hooks/README.md +15 -3
- package/dist/plugin/hooks/hooks.json +41 -1
- package/dist/plugin/scripts/gc-state.sh +66 -0
- package/dist/plugin/scripts/guard-run-job-object.ps1 +256 -0
- package/dist/plugin/scripts/guard-run.mjs +327 -0
- package/dist/plugin/scripts/guardrail-offer-ran.sh +35 -0
- package/dist/plugin/scripts/guardrail-pr-terminal.sh +21 -0
- package/dist/plugin/scripts/guardrail-record-tests.sh +5 -4
- package/dist/plugin/scripts/guardrail-terminal-gate.sh +38 -0
- package/dist/plugin/scripts/guardrails.mjs +91 -3
- package/dist/plugin/scripts/pr-watch-guards.sh +46 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +3 -3
- package/dist/plugin/skills/_shared/vcs/CLAUDE.md +2 -1
- package/dist/plugin/skills/_shared/vcs/common/branch-standing.md +19 -0
- package/dist/plugin/skills/_shared/vcs/common/ci-rollup.md +16 -0
- package/dist/plugin/skills/_shared/vcs/common/push-to-branch.md +16 -0
- package/{plugin/skills/_shared/vcs/github → dist/plugin/skills/_shared/vcs/common}/verify-working-tree.md +4 -4
- package/dist/plugin/skills/_shared/vcs/github/pr-edit.md +2 -0
- package/dist/plugin/skills/_shared/vcs/github/reply-line-comment.md +2 -0
- package/dist/plugin/skills/_shared/vcs/github/signed-commits.md +28 -0
- package/dist/plugin/skills/_shared/vcs/github/top-level-comment.md +2 -0
- package/dist/plugin/skills/_shared/vcs/github/unresolved-threads.md +39 -39
- package/dist/plugin/skills/_shared/vcs/github.md +5 -2
- package/dist/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +2 -2
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +1 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/signed-commits.md +15 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +2 -2
- package/dist/plugin/skills/_shared/vcs/gitlab.md +6 -3
- package/dist/plugin/skills/_shared/vcs/post-signature.md +31 -0
- package/dist/plugin/skills/do/build.md +58 -56
- package/dist/plugin/skills/do/open-prs/forward.md +85 -84
- package/dist/plugin/skills/do/open-prs/update.md +3 -3
- package/dist/plugin/skills/do/per-comment-replies.md +2 -2
- package/dist/plugin/skills/do/resolve-conflicts.md +59 -58
- package/dist/plugin/skills/do/unit-tests.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -0
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +11 -4
- package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +29 -5
- package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +3 -1
- package/dist/plugin/skills/muggle-pr-followup/contract.md +171 -158
- package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +221 -101
- package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +2 -2
- package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/reconcile.md +9 -5
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +191 -175
- package/dist/plugin/skills/muggle-pr-followup/stop.md +31 -0
- package/dist/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +14 -5
- package/dist/plugin/skills/muggle-test-prepare/steps/start-services.md +2 -2
- package/dist/release-manifest.json +4 -4
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/hooks/README.md +15 -3
- package/plugin/hooks/hooks.json +41 -1
- package/plugin/scripts/gc-state.sh +66 -0
- package/plugin/scripts/guard-run-job-object.ps1 +256 -0
- package/plugin/scripts/guard-run.mjs +327 -0
- package/plugin/scripts/guardrail-offer-ran.sh +35 -0
- package/plugin/scripts/guardrail-pr-terminal.sh +21 -0
- package/plugin/scripts/guardrail-record-tests.sh +5 -4
- package/plugin/scripts/guardrail-terminal-gate.sh +38 -0
- package/plugin/scripts/guardrails.mjs +91 -3
- package/plugin/scripts/pr-watch-guards.sh +46 -0
- package/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +3 -3
- package/plugin/skills/_shared/vcs/CLAUDE.md +2 -1
- package/plugin/skills/_shared/vcs/common/branch-standing.md +19 -0
- package/plugin/skills/_shared/vcs/common/ci-rollup.md +16 -0
- package/plugin/skills/_shared/vcs/common/push-to-branch.md +16 -0
- package/{dist/plugin/skills/_shared/vcs/github → plugin/skills/_shared/vcs/common}/verify-working-tree.md +4 -4
- package/plugin/skills/_shared/vcs/github/pr-edit.md +2 -0
- package/plugin/skills/_shared/vcs/github/reply-line-comment.md +2 -0
- package/plugin/skills/_shared/vcs/github/signed-commits.md +28 -0
- package/plugin/skills/_shared/vcs/github/top-level-comment.md +2 -0
- package/plugin/skills/_shared/vcs/github/unresolved-threads.md +39 -39
- package/plugin/skills/_shared/vcs/github.md +5 -2
- package/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-create.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +2 -2
- package/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +1 -1
- package/plugin/skills/_shared/vcs/gitlab/signed-commits.md +15 -0
- package/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +2 -2
- package/plugin/skills/_shared/vcs/gitlab.md +6 -3
- package/plugin/skills/_shared/vcs/post-signature.md +31 -0
- package/plugin/skills/do/build.md +58 -56
- package/plugin/skills/do/open-prs/forward.md +85 -84
- package/plugin/skills/do/open-prs/update.md +3 -3
- package/plugin/skills/do/per-comment-replies.md +2 -2
- package/plugin/skills/do/resolve-conflicts.md +59 -58
- package/plugin/skills/do/unit-tests.md +1 -1
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -0
- package/plugin/skills/muggle-pr-followup/SKILL.md +11 -4
- package/plugin/skills/muggle-pr-followup/arm-watcher.md +29 -5
- package/plugin/skills/muggle-pr-followup/bootstrap.md +3 -1
- package/plugin/skills/muggle-pr-followup/contract.md +171 -158
- package/plugin/skills/muggle-pr-followup/evals/evals.json +221 -101
- package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +2 -2
- package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -1
- package/plugin/skills/muggle-pr-followup/reconcile.md +9 -5
- package/plugin/skills/muggle-pr-followup/state-schemas.md +191 -175
- package/plugin/skills/muggle-pr-followup/stop.md +31 -0
- package/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +14 -5
- package/plugin/skills/muggle-test-prepare/steps/start-services.md +2 -2
- package/dist/plugin/skills/_shared/vcs/github/push-to-branch.md +0 -10
- package/plugin/skills/_shared/vcs/github/push-to-branch.md +0 -10
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# CI rollup (tool-agnostic)
|
|
2
|
+
|
|
3
|
+
The single instruction every CI-poll site references — sites link here and never name a provider's rollup recipe inline. Fetch the CI state for a PR/MR head and fold it into one of three buckets: **red**, **pending**, **green**.
|
|
4
|
+
|
|
5
|
+
Resolve the provider per [`../detect-vcs.md`](../detect-vcs.md), then run its rollup recipe — the recipe owns the fetch command and the raw per-check/per-job states:
|
|
6
|
+
|
|
7
|
+
- `github` → [`../github/pr-checks.md`](../github/pr-checks.md) — the check-run rollup for the head SHA.
|
|
8
|
+
- `gitlab` → [`../gitlab/mr-pipeline.md`](../gitlab/mr-pipeline.md) — the pipeline-job rollup for the head.
|
|
9
|
+
|
|
10
|
+
Both recipes fold into the same buckets, so a caller branches on the token only to fetch, then reasons on the bucket alone:
|
|
11
|
+
|
|
12
|
+
- **red** — one or more entries in the `fail` bucket (a failed check-run; a `failed` job). Candidate for fix-ci; the dispatch carries the failing names.
|
|
13
|
+
- **pending** — nothing failed but an entry has not settled (`pending` bucket; a `running` / `pending` / `created` job). The result may yet go green.
|
|
14
|
+
- **green** — every entry passed, was skipped, or was cancelled, or there are none at all.
|
|
15
|
+
|
|
16
|
+
The rollup is **non-monotonic** — it flips red↔green and resets on every push. A caller that records a red result keys it on the **head SHA**, not a monotonic id, so the record re-arms on the next push.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Push to branch (tool-agnostic)
|
|
2
|
+
|
|
3
|
+
The single instruction every commit/push site references — sites link here and never embed provider commands inline.
|
|
4
|
+
|
|
5
|
+
## Signing gate — never push unsigned commits
|
|
6
|
+
|
|
7
|
+
Resolve the provider per [`../detect-vcs.md`](../detect-vcs.md), then run its signed-commits recipe — the recipe owns the preflight and the actual commands:
|
|
8
|
+
|
|
9
|
+
- `github` → [`../github/signed-commits.md`](../github/signed-commits.md) — local signing working: commit and push normally; broken: create the commits server-signed (`createCommitOnBranch`) and skip the push (the remote already has them). Rebase/force-push follows the same recipe's replay path.
|
|
10
|
+
- `gitlab` → [`../gitlab/signed-commits.md`](../gitlab/signed-commits.md) — local signing working: commit and push normally; broken: stop and escalate (no server-side signing).
|
|
11
|
+
|
|
12
|
+
Never disable signing to make a push go through.
|
|
13
|
+
|
|
14
|
+
## After the push
|
|
15
|
+
|
|
16
|
+
Capture the new head SHA — from the local branch, or from the mutation response on the server-signed path — and append it to `last_seen.pushed_shas[]` so resolve-reminder can recognize it.
|
|
@@ -8,10 +8,10 @@ git remote get-url origin # remote matches <owner>/<repo>
|
|
|
8
8
|
git rev-parse --abbrev-ref HEAD # current branch matches PR's headRefName
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Accept any remote URL form for `<owner>/<repo
|
|
11
|
+
Accept any remote URL form for `<owner>/<repo>`, with or without trailing `.git`, where `<host>` is the provider host (`github.com`, or the GitLab instance host):
|
|
12
12
|
|
|
13
|
-
- `https
|
|
14
|
-
- `git
|
|
15
|
-
- `ssh://git
|
|
13
|
+
- `https://<host>/<owner>/<repo>`
|
|
14
|
+
- `git@<host>:<owner>/<repo>`
|
|
15
|
+
- `ssh://git@<host>/<owner>/<repo>`
|
|
16
16
|
|
|
17
17
|
Any mismatch → abort; the calling skill reports the wrong checkout to the user with its own message.
|
|
@@ -6,3 +6,5 @@ For `open-prs/update.md` when E2E state flips (passing↔failing) or validation
|
|
|
6
6
|
gh pr edit <pr-number> --repo <owner>/<repo> --title "<new-title>"
|
|
7
7
|
gh pr edit <pr-number> --repo <owner>/<repo> --body-file <file>
|
|
8
8
|
```
|
|
9
|
+
|
|
10
|
+
The `<file>` body must end with the Muggle Works signature. Because the description is re-posted on each refresh, strip the old signature (from the `<!-- muggle-works:signature -->` marker to the end) before re-appending it — see [`../post-signature.md`](../post-signature.md).
|
|
@@ -8,3 +8,5 @@ gh api --method POST \
|
|
|
8
8
|
repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
|
|
9
9
|
-f body="<reply-text>"
|
|
10
10
|
```
|
|
11
|
+
|
|
12
|
+
The `<reply-text>` must end with the loop signature block — the `<!-- muggle-do:bot -->` detection marker above the Muggle Works line. See [`../post-signature.md`](../post-signature.md).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Signed commits without local signing
|
|
2
|
+
|
|
3
|
+
**Never push unsigned commits.** When the machine has no working signing setup, do not fall back to unsigned `git commit` + `git push` — create the commits **remotely** with GraphQL `createCommitOnBranch`, which GitHub signs server-side. And never "fix" a signing failure by disabling it: no `--no-gpg-sign`, no `-c commit.gpgsign=false` — a broken signing setup is fixed or routed around via the remote path, not turned off.
|
|
4
|
+
|
|
5
|
+
## Preflight
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git -C <repo-path> log --format='%G?' origin/<branch>..HEAD # unpushed branch: <base>..HEAD
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Any `N` (no signature) among the commits about to leave the machine → the push is blocked; use the remote path below. `G`/`E`/`U` are signed commits (locally unverifiable is fine — GitHub-signed commits show `E` without GitHub's key in the local keyring). Signing configured and working → commit and push normally.
|
|
12
|
+
|
|
13
|
+
## Remote signed commit
|
|
14
|
+
|
|
15
|
+
1. The branch must exist on the remote; create it at the base if absent: `gh api repos/<owner>/<repo>/git/refs -f ref=refs/heads/<branch> -f sha=<base-sha>`.
|
|
16
|
+
2. Stage the change (`git add <files>`), then take each file's canonical bytes from the **index** — `git cat-file blob :<path>` — and base64 them. Index blobs, never working-copy reads: the index holds git's normalized content (line endings, filters), so the remote tree matches what a local commit would have produced.
|
|
17
|
+
3. One `createCommitOnBranch` mutation per commit: `branch {repositoryNameWithOwner, branchName}`, `message {headline, body}`, `expectedHeadOid` = the branch's current remote head (a concurrency lease — a concurrent push 409s instead of being clobbered), `fileChanges.additions` (path + base64 contents; removals via `fileChanges.deletions`).
|
|
18
|
+
4. Sync the local branch to the result: `git fetch origin <branch>`, verify `git diff --cached origin/<branch>` is **empty** (remote tree identical to what was staged), then `git reset --hard origin/<branch>`. A non-empty diff means the payload missed a file — fix and re-commit; never leave local and remote diverged.
|
|
19
|
+
|
|
20
|
+
## Rebase / force-push
|
|
21
|
+
|
|
22
|
+
A local rebase mints new local commits — unsigned on this machine — so the `--force-with-lease` path is equally blocked. Replay instead:
|
|
23
|
+
|
|
24
|
+
1. Create a temp ref at the new base tip.
|
|
25
|
+
2. Re-create each branch commit on it via `createCommitOnBranch` (same per-commit file snapshots from `git cat-file blob <commit>:<path>`; conflicted files carry the resolved content).
|
|
26
|
+
3. Move the branch ref with a lease: verify the branch's current remote head is still the expected pre-rebase SHA, then `gh api -X PATCH repos/<owner>/<repo>/git/refs/heads/<branch> -f sha=<new-head> -F force=true`. Delete the temp ref.
|
|
27
|
+
|
|
28
|
+
The replayed commits are GitHub-signed; the guarded ref move is the only force operation.
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
# Unresolved comment threads
|
|
2
|
-
|
|
3
|
-
For the watcher's dispatch trigger and the resolve-reminder stage. GraphQL only — REST does not expose `isResolved`/`isOutdated`.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
gh api graphql -F owner=<owner> -F name=<repo> -F number=<n> -f query='
|
|
7
|
-
query($owner: String!, $name: String!, $number: Int!) {
|
|
8
|
-
repository(owner: $owner, name: $name) {
|
|
9
|
-
pullRequest(number: $number) {
|
|
10
|
-
reviewThreads(first: 100) {
|
|
11
|
-
nodes {
|
|
12
|
-
id
|
|
13
|
-
isResolved
|
|
14
|
-
isOutdated
|
|
15
|
-
comments(first: 100) {
|
|
16
|
-
nodes {
|
|
17
|
-
databaseId
|
|
18
|
-
pullRequestReview { databaseId }
|
|
19
|
-
author { login }
|
|
20
|
-
body
|
|
21
|
-
createdAt
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}'
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Filter client-side to `isResolved == false`. Walk each thread's comments in `createdAt` order and classify by the loop marker (see [`../../pr-followup-helpers/loop-signature.md`](../../pr-followup-helpers/loop-signature.md)), not by `author.login` — the login is ambiguous under a shared account:
|
|
32
|
-
|
|
33
|
-
- **Addressed, awaiting resolve** — the **newest** comment carries the loop marker `<!-- muggle-do:bot -->`. The loop has replied and nothing newer is waiting. → resolve-reminder.
|
|
34
|
-
- **Unaddressed human comment** — the newest comment lacks the marker and is newer than the thread's newest loop-marked comment (or the thread has no loop comment yet). → actionable: the round should address it.
|
|
35
|
-
- **Not addressed** — indeterminate (e.g. no comments).
|
|
36
|
-
|
|
37
|
-
Each comment exposes its owning review as `pullRequestReview.databaseId` — the watcher collects this from an actionable thread's newest comment to build its dispatch list.
|
|
38
|
-
|
|
39
|
-
A loop comment also cites a `<short-sha>` from `last_seen.pushed_shas[]` in its body, which tells *which* push addressed the thread.
|
|
1
|
+
# Unresolved comment threads
|
|
2
|
+
|
|
3
|
+
For the watcher's dispatch trigger and the resolve-reminder stage. GraphQL only — REST does not expose `isResolved`/`isOutdated`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
gh api graphql -F owner=<owner> -F name=<repo> -F number=<n> -f query='
|
|
7
|
+
query($owner: String!, $name: String!, $number: Int!) {
|
|
8
|
+
repository(owner: $owner, name: $name) {
|
|
9
|
+
pullRequest(number: $number) {
|
|
10
|
+
reviewThreads(first: 100) {
|
|
11
|
+
nodes {
|
|
12
|
+
id
|
|
13
|
+
isResolved
|
|
14
|
+
isOutdated
|
|
15
|
+
comments(first: 100) {
|
|
16
|
+
nodes {
|
|
17
|
+
databaseId
|
|
18
|
+
pullRequestReview { databaseId }
|
|
19
|
+
author { login }
|
|
20
|
+
body
|
|
21
|
+
createdAt
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Filter client-side to `isResolved == false`. Walk each thread's comments in `createdAt` order and classify by the loop marker (see [`../../pr-followup-helpers/loop-signature.md`](../../pr-followup-helpers/loop-signature.md)), not by `author.login` — the login is ambiguous under a shared account:
|
|
32
|
+
|
|
33
|
+
- **Addressed, awaiting resolve** — the **newest** comment carries the loop marker `<!-- muggle-do:bot -->`. The loop has replied and nothing newer is waiting. → resolve-reminder.
|
|
34
|
+
- **Unaddressed human comment** — the newest comment lacks the marker and is newer than the thread's newest loop-marked comment (or the thread has no loop comment yet). → actionable: the round should address it. This holds **regardless of `isOutdated`** — a thread whose anchored line has since moved (a rebase or a later push) still carries its unanswered question, and skipping it leaves that question unanswered for good. Whether the moved code already mooted the concern is a judgment for the round to make and reply to, not a reason to skip; the marker rule then stops re-dispatch on the next poll. Both the watcher's dispatch trigger and the resolve-reminder stage ignore `isOutdated`.
|
|
35
|
+
- **Not addressed** — indeterminate (e.g. no comments).
|
|
36
|
+
|
|
37
|
+
Each comment exposes its owning review as `pullRequestReview.databaseId` — the watcher collects this from an actionable thread's newest comment to build its dispatch list.
|
|
38
|
+
|
|
39
|
+
A loop comment also cites a `<short-sha>` from `last_seen.pushed_shas[]` in its body, which tells *which* push addressed the thread.
|
|
@@ -11,11 +11,14 @@ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`
|
|
|
11
11
|
| [`pr-metadata`](github/pr-metadata.md) | Snapshot PR state, head SHA, branch, conflict + `behind_by` out-of-date detection — watcher + bootstrap. |
|
|
12
12
|
| [`submitted-reviews`](github/submitted-reviews.md) | Fetch a review by id / watcher's body-only-review check. |
|
|
13
13
|
| [`pr-checks`](github/pr-checks.md) | Check-run rollup for the head SHA — watcher's CI poll. |
|
|
14
|
+
| [`ci-rollup`](common/ci-rollup.md) | Tool-agnostic CI poll — dispatches to `pr-checks` / `mr-pipeline`, folds into red/pending/green. |
|
|
15
|
+
| [`branch-standing`](common/branch-standing.md) | Tool-agnostic conflict + out-of-date detection — dispatches to `pr-metadata` / `mr-metadata`. |
|
|
14
16
|
| [`line-comments-for-review`](github/line-comments-for-review.md) | Pull a review's line comments — `/muggle-do` per-comment routing. |
|
|
15
17
|
| [`unresolved-threads`](github/unresolved-threads.md) | GraphQL unresolved-thread state — watcher's dispatch trigger + resolve-reminder. |
|
|
16
18
|
| [`reply-line-comment`](github/reply-line-comment.md) | POST a threaded reply on a line comment. |
|
|
17
19
|
| [`top-level-comment`](github/top-level-comment.md) | POST a top-level PR comment — resolve-reminder + overflow. |
|
|
18
|
-
| [`push-to-branch`](
|
|
19
|
-
| [`
|
|
20
|
+
| [`push-to-branch`](common/push-to-branch.md) | Signing-gated push + capture new SHA after address-reviews work. |
|
|
21
|
+
| [`signed-commits`](github/signed-commits.md) | Never-push-unsigned rule: `%G?` preflight, server-signed commits via `createCommitOnBranch`, rebase replay. |
|
|
22
|
+
| [`verify-working-tree`](common/verify-working-tree.md) | Three checks bootstrap runs before seeding state. |
|
|
20
23
|
| [`pr-edit`](github/pr-edit.md) | Refresh title or body when address-reviews mode flips state. |
|
|
21
24
|
| [`loop-user-identity`](github/loop-user-identity.md) | Resolve the GitHub login of the loop user. |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The GitLab identity that owns the authenticated `glab` token. Resolve-reminder thread classification and reply attribution need this.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
glab api user
|
|
6
|
+
glab api user | jq -r '.username'
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Cache in `state.md` under `Loop user:`; re-resolve only when missing.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Open a merge request
|
|
2
2
|
|
|
3
|
-
For `open-prs`. Push the branch first (see [`../
|
|
3
|
+
For `open-prs`. Push the branch first (see [`../common/push-to-branch.md`](../common/push-to-branch.md)), then open the MR.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
glab mr create -R <group>/<project> \
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Incoming feedback for the watcher's poll and the address-reviews fetch. GitLab has **no review envelope** — there is no submitted-review object grouping a summary body with line comments. Feedback arrives as individual notes, each belonging to a discussion (a thread). This recipe is the `submitted-reviews` analogue.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
glab api projects/:id/merge_requests
|
|
6
|
+
glab api projects/:id/merge_requests/<iid>/discussions --paginate
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Each discussion has `id` and a `notes[]` array; each note has `id`, `author.username`, `body`, `created_at`, `system` (a `true` flag marks GitLab's own activity entries — skip them).
|
|
@@ -16,7 +16,7 @@ glab mr view <iid> -R <group>/<project> -F json
|
|
|
16
16
|
`detailed_merge_status == "need_rebase"` reports a behind branch only when the project enforces "fast-forward merge"; otherwise it stays `mergeable` while behind. Detect out-of-date straight from commit ancestry instead — independent of merge-method config:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
glab api projects/:id/repository/compare?from=<
|
|
19
|
+
glab api "projects/:id/repository/compare?from=<head_sha>&to=<target_branch>" | jq '.commits | length'
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Compare lists the commits reachable from `to` but not `from`, so `from=<head_sha>&to=<target_branch>` yields exactly the base commits the head lacks — non-zero ⇒ out of date; zero ⇒ current with base.
|
|
@@ -9,7 +9,7 @@ glab ci status -R <group>/<project> -b <source_branch>
|
|
|
9
9
|
Or straight from the API for the latest pipeline and its jobs:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
glab api projects/:id/merge_requests
|
|
12
|
+
glab api projects/:id/merge_requests/<iid>/pipelines | jq -r '.[0].id'
|
|
13
13
|
glab api projects/:id/pipelines/<pipeline-id>/jobs --paginate
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -4,6 +4,6 @@ Used by `/muggle-do` per-comment inline replies. A threaded reply is a new note
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
glab api --method POST \
|
|
7
|
-
projects/:id/merge_requests
|
|
7
|
+
projects/:id/merge_requests/<iid>/discussions/<discussion-id>/notes \
|
|
8
8
|
-f body="<reply-text>"
|
|
9
9
|
```
|
|
@@ -4,7 +4,7 @@ Mark a thread resolved once the loop's reply has addressed it — the resolve-re
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
glab api --method PUT \
|
|
7
|
-
"projects/:id/merge_requests
|
|
7
|
+
"projects/:id/merge_requests/<iid>/discussions/<discussion-id>?resolved=true"
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Resolves every resolvable note in the thread at once. Only resolvable (diff/line) threads accept this; a non-resolvable discussion returns an error.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Signed commits without local signing
|
|
2
|
+
|
|
3
|
+
**Never push unsigned commits.** GitLab has no server-side signing analogue — commits created through its commits API are unsigned, so a broken local signing setup has no remote route around it. And never "fix" a signing failure by disabling it: no `--no-gpg-sign`, no `-c commit.gpgsign=false`.
|
|
4
|
+
|
|
5
|
+
## Preflight
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git -C <repo-path> log --format='%G?' origin/<branch>..HEAD # unpushed branch: <base>..HEAD
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Any `N` (no signature) among the commits about to leave the machine → the push is blocked. `G`/`E`/`U` are signed commits (locally unverifiable is fine). Signing configured and working → commit and push normally.
|
|
12
|
+
|
|
13
|
+
## Blocked
|
|
14
|
+
|
|
15
|
+
Unsigned commits bound for an MR branch → stop and escalate to the owner to configure local signing. A local rebase mints new unsigned commits, so the force-push path is equally blocked. Never push unsigned.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
For the watcher's dispatch trigger and the resolve-reminder stage. REST exposes resolution directly — no GraphQL needed, unlike GitHub.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
glab api projects/:id/merge_requests
|
|
7
|
-
|
|
6
|
+
glab api projects/:id/merge_requests/<iid>/discussions --paginate \
|
|
7
|
+
| jq '[.[] | select(.notes[0].resolvable == true) | select(any(.notes[]; .resolved == false))]'
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
A discussion is resolvable when its notes carry `resolvable == true` (diff/line threads are; the MR description and system notes are not). A thread is **unresolved** when any of its notes has `resolved == false`.
|
|
@@ -4,7 +4,7 @@ Reusable `glab` / `git` snippets used by `muggle-pr-followup` (watcher + bootstr
|
|
|
4
4
|
|
|
5
5
|
Skills assume a working `glab auth status`. Auth errors surface verbatim from `glab`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`glab api` fills `:id` itself with the URL-encoded path of the current directory's project — run recipes from the MR worktree and leave `:id` as written (elsewhere, substitute the encoded path yourself: `mygroup/myproj` → `mygroup%2Fmyproj`). `<iid>` is the MR's per-project internal id (the `!123` number), not the global id; glab has no placeholder for it — substitute it. `glab api` has no `--jq` flag — pipe to `jq`.
|
|
8
8
|
|
|
9
9
|
## Index
|
|
10
10
|
|
|
@@ -12,6 +12,8 @@ The project ref `:id` is `<group>/<project>` — URL-encode it for `glab api` (`
|
|
|
12
12
|
| :----- | :------- |
|
|
13
13
|
| [`mr-metadata`](gitlab/mr-metadata.md) | Snapshot MR state, head SHA, branch, conflict + out-of-date detection — watcher + bootstrap. |
|
|
14
14
|
| [`mr-pipeline`](gitlab/mr-pipeline.md) | Pipeline-job rollup for the head SHA — watcher's CI poll. |
|
|
15
|
+
| [`ci-rollup`](common/ci-rollup.md) | Tool-agnostic CI poll — dispatches to `pr-checks` / `mr-pipeline`, folds into red/pending/green. |
|
|
16
|
+
| [`branch-standing`](common/branch-standing.md) | Tool-agnostic conflict + out-of-date detection — dispatches to `pr-metadata` / `mr-metadata`. |
|
|
15
17
|
| [`mr-discussions`](gitlab/mr-discussions.md) | Fetch incoming notes/discussions — watcher's feedback poll. |
|
|
16
18
|
| [`unresolved-discussions`](gitlab/unresolved-discussions.md) | Unresolved-discussion state — watcher's dispatch trigger + resolve-reminder. |
|
|
17
19
|
| [`reply-discussion`](gitlab/reply-discussion.md) | POST a threaded reply on a discussion. |
|
|
@@ -20,5 +22,6 @@ The project ref `:id` is `<group>/<project>` — URL-encode it for `glab api` (`
|
|
|
20
22
|
| [`mr-edit`](gitlab/mr-edit.md) | Refresh title or description when address-reviews mode flips state. |
|
|
21
23
|
| [`mr-create`](gitlab/mr-create.md) | Open an MR + capture its URL for handoff. |
|
|
22
24
|
| [`loop-user-identity`](gitlab/loop-user-identity.md) | Resolve the GitLab username of the loop user. |
|
|
23
|
-
| [`push-to-branch`](
|
|
24
|
-
| [`
|
|
25
|
+
| [`push-to-branch`](common/push-to-branch.md) | Signing-gated push + capture new SHA after address-reviews work. |
|
|
26
|
+
| [`signed-commits`](gitlab/signed-commits.md) | Never-push-unsigned rule: `%G?` preflight; no server-side signing analogue → stop and escalate. |
|
|
27
|
+
| [`verify-working-tree`](common/verify-working-tree.md) | Three checks bootstrap runs before seeding state. |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# PR post signature
|
|
2
|
+
|
|
3
|
+
Every pull-request / merge-request body, comment, and review-thread reply that muggle works posts ends with a signature line. Under a single-account workflow the automation posts as the repo owner, so the signature is what tells a reader — and a reviewer — that the post came from Muggle Works and which command produced it.
|
|
4
|
+
|
|
5
|
+
## The line
|
|
6
|
+
|
|
7
|
+
Append this as the last line of the posted body:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
🤖 _Posted by `<command>` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`<command>` is the slash-command of the skill whose `gh` / `glab` call posts the body:
|
|
14
|
+
|
|
15
|
+
- `/muggle-do` — PR/MR descriptions, per-comment thread replies, top-level reference replies, and resolve-reminders.
|
|
16
|
+
- `/muggle-pr-visual-walkthrough` — a walkthrough comment the walkthrough skill posts itself (Mode A).
|
|
17
|
+
|
|
18
|
+
Name the command that owns the post, not the one that generated the content. When the walkthrough hands its rendered block back for embedding (Mode B/C), the caller owns the post, so the caller's command is what the signature names.
|
|
19
|
+
|
|
20
|
+
## Editable bodies (PR / MR description)
|
|
21
|
+
|
|
22
|
+
A description is re-posted whenever state changes, so its signature must not stack. Precede the line with a hidden marker and treat the pair as one unit:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
<!-- muggle-works:signature -->
|
|
26
|
+
🤖 _Posted by `/muggle-do` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Before writing an edited body, delete everything from the `<!-- muggle-works:signature -->` marker to the end of the body, then append the block fresh. This keeps exactly one signature no matter how many times the description is refreshed.
|
|
30
|
+
|
|
31
|
+
A comment or reply is posted once and never edited, so it needs no dedup marker — append the line alone. A loop-authored reply already carries the `<!-- muggle-do:bot -->` detection marker (defined in loop-signature.md) directly above this line; that marker stays, and this line replaces the old visible text.
|
|
@@ -1,56 +1,58 @@
|
|
|
1
|
-
# Build Agent (Stage 3)
|
|
2
|
-
|
|
3
|
-
Implement the code change for this dev cycle. Read the frozen requirements from stage 2, produce the edits in each affected repo's worktree, and commit.
|
|
4
|
-
|
|
5
|
-
## Turn preamble
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
**Stage 3 — Build** — implementing the change per the frozen requirements.
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Inputs
|
|
12
|
-
|
|
13
|
-
- `requirements.md` from stage 2: goal, acceptance criteria, affected repos.
|
|
14
|
-
- `state.md` from pre-flight: worktree path per repo, target branch, anything else the build needs to know about the environment.
|
|
15
|
-
|
|
16
|
-
## Your job
|
|
17
|
-
|
|
18
|
-
For each affected repo:
|
|
19
|
-
|
|
20
|
-
1. **Re-read `requirements.md`.** Treat goal + AC as frozen. If something is unclear at this stage, that's a pre-flight bug — escalate, do not improvise.
|
|
21
|
-
2. **Apply the change** in the repo's worktree. Edit existing files first; create new files only when the requirements demand it. Match the surrounding code's style, naming, and file layout.
|
|
22
|
-
3. **Don't add what wasn't asked for.** No speculative abstractions, no extra logging, no "while I'm here" refactors. Three similar lines is better than a premature abstraction.
|
|
23
|
-
4. **Cover new logic with tests.** If you added or changed non-trivial logic (a hook, reducer, parser, state machine, branching util), write its unit tests now — Stage 5 only *runs* the suite, it never authors tests. Untested new logic is a Definition-of-Done failure, not a Stage-5 gap.
|
|
24
|
-
5. **Commit** with a conventional-commit subject:
|
|
25
|
-
- `feat(<scope>): <short>` for new behavior
|
|
26
|
-
- `fix(<scope>): <short>` for bug fixes
|
|
27
|
-
- `refactor(<scope>): <short>` for reshape
|
|
28
|
-
- `docs(...)`, `chore(...)`, `test(...)` as appropriate
|
|
29
|
-
|
|
30
|
-
The body explains *why* when the why is non-obvious. The diff already says *what*.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
1
|
+
# Build Agent (Stage 3)
|
|
2
|
+
|
|
3
|
+
Implement the code change for this dev cycle. Read the frozen requirements from stage 2, produce the edits in each affected repo's worktree, and commit.
|
|
4
|
+
|
|
5
|
+
## Turn preamble
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
**Stage 3 — Build** — implementing the change per the frozen requirements.
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
|
|
13
|
+
- `requirements.md` from stage 2: goal, acceptance criteria, affected repos.
|
|
14
|
+
- `state.md` from pre-flight: worktree path per repo, target branch, anything else the build needs to know about the environment.
|
|
15
|
+
|
|
16
|
+
## Your job
|
|
17
|
+
|
|
18
|
+
For each affected repo:
|
|
19
|
+
|
|
20
|
+
1. **Re-read `requirements.md`.** Treat goal + AC as frozen. If something is unclear at this stage, that's a pre-flight bug — escalate, do not improvise.
|
|
21
|
+
2. **Apply the change** in the repo's worktree. Edit existing files first; create new files only when the requirements demand it. Match the surrounding code's style, naming, and file layout.
|
|
22
|
+
3. **Don't add what wasn't asked for.** No speculative abstractions, no extra logging, no "while I'm here" refactors. Three similar lines is better than a premature abstraction.
|
|
23
|
+
4. **Cover new logic with tests.** If you added or changed non-trivial logic (a hook, reducer, parser, state machine, branching util), write its unit tests now — Stage 5 only *runs* the suite, it never authors tests. Untested new logic is a Definition-of-Done failure, not a Stage-5 gap.
|
|
24
|
+
5. **Commit** with a conventional-commit subject:
|
|
25
|
+
- `feat(<scope>): <short>` for new behavior
|
|
26
|
+
- `fix(<scope>): <short>` for bug fixes
|
|
27
|
+
- `refactor(<scope>): <short>` for reshape
|
|
28
|
+
- `docs(...)`, `chore(...)`, `test(...)` as appropriate
|
|
29
|
+
|
|
30
|
+
The body explains *why* when the why is non-obvious. The diff already says *what*.
|
|
31
|
+
|
|
32
|
+
**Signing:** never an unsigned commit. Commit through the signing gate in [`../_shared/vcs/common/push-to-branch.md`](../_shared/vcs/common/push-to-branch.md) — it routes a broken local signing setup to the provider's signed-commits recipe. Never `--no-gpg-sign` or disable signing to work around the failure.
|
|
33
|
+
|
|
34
|
+
## Delegation
|
|
35
|
+
|
|
36
|
+
For a non-trivial change — multiple files, real design surface, or anything you would otherwise brainstorm before coding — run the implementation through superpowers' design → plan → subagent-driven build, then return to this stage's Output. That is a runtime hand-off (an action), not a doc dependency; do not encode superpowers' internals here. Routing a build request into this pipeline (the `autoRouteBuildToMuggleDo` front-door guardrail) exists to combine superpowers' design rigor with this pipeline's impact analysis, E2E, PR, and watcher — neither delivers both alone.
|
|
37
|
+
|
|
38
|
+
## Output
|
|
39
|
+
|
|
40
|
+
Per repo:
|
|
41
|
+
|
|
42
|
+
**Repo:** name
|
|
43
|
+
- Files edited / created: list
|
|
44
|
+
- Commit subject: `<conventional commit>`
|
|
45
|
+
- Notable choices: anything that needed a judgment call (briefly)
|
|
46
|
+
|
|
47
|
+
**Overall:** READY for impact analysis | BLOCKED — reason
|
|
48
|
+
|
|
49
|
+
If a requirement is fundamentally unimplementable as written, halt and escalate with the specific blocker — do not ship a half-finished implementation.
|
|
50
|
+
|
|
51
|
+
## Re-entry from the address-reviews flow
|
|
52
|
+
|
|
53
|
+
The address-reviews orchestrator ([`address-reviews.md`](address-reviews.md)) invokes this stage when reviewers submit comments that require code/design changes. When re-entered:
|
|
54
|
+
|
|
55
|
+
- The orchestrator passes the actionable reviews' bodies + line comments as the requirements amendment for this iteration. Treat them as additions to the goal/AC.
|
|
56
|
+
- Continue on the existing branch — do not re-create the worktree.
|
|
57
|
+
- After this stage, the orchestrator runs unit-tests → ONE E2E pass → create-or-update PR (push to the existing branch; refresh title/desc if state changed) → per-comment inline replies → resolve-reminder → respawn the watcher.
|
|
58
|
+
- If the requested work cannot be implemented without rethinking design (e.g. a load-bearing invariant must change), return `failed: design-adjustment` and let the orchestrator escalate via the design-adjustment terminal message. Do not partially implement.
|