@muggleai/works 5.0.4 → 5.2.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.
Files changed (132) hide show
  1. package/README.md +2 -3
  2. package/dist/{chunk-KDRSEHK5.js → chunk-3HSV6EXR.js} +245 -309
  3. package/dist/{chunk-XO5BQZM3.js → chunk-IIQZLG34.js} +3 -4
  4. package/dist/cli.js +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  7. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  8. package/dist/plugin/scripts/ensure-electron-app.sh +1 -1
  9. package/dist/plugin/scripts/guardrails.mjs +4 -2
  10. package/dist/plugin/skills/_shared/dev-loop/publish.md +4 -8
  11. package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +1 -1
  12. package/dist/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +1 -1
  13. package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -1
  14. package/dist/plugin/skills/_shared/test-case-chain-readiness.md +1 -2
  15. package/dist/plugin/skills/_shared/vcs/CLAUDE.md +9 -0
  16. package/dist/plugin/skills/_shared/vcs/detect-vcs.md +28 -0
  17. package/{plugin/skills/_shared/github-cli-recipes → dist/plugin/skills/_shared/vcs/github}/unresolved-threads.md +1 -1
  18. package/{plugin/skills/_shared/github-cli-recipes → dist/plugin/skills/_shared/vcs/github}/verify-working-tree.md +1 -1
  19. package/dist/plugin/skills/_shared/vcs/github.md +21 -0
  20. package/dist/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +9 -0
  21. package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +11 -0
  22. package/dist/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +18 -0
  23. package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +8 -0
  24. package/dist/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +22 -0
  25. package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +7 -0
  26. package/dist/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +27 -0
  27. package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +9 -0
  28. package/dist/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +10 -0
  29. package/dist/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +18 -0
  30. package/dist/plugin/skills/_shared/vcs/gitlab.md +24 -0
  31. package/dist/plugin/skills/do/address-reviews.md +10 -6
  32. package/dist/plugin/skills/do/e2e-acceptance.md +4 -4
  33. package/dist/plugin/skills/do/fix-ci.md +1 -1
  34. package/dist/plugin/skills/do/input-routing.md +4 -4
  35. package/dist/plugin/skills/do/open-prs/forward.md +6 -4
  36. package/dist/plugin/skills/do/open-prs/update.md +8 -6
  37. package/dist/plugin/skills/do/per-comment-replies.md +22 -13
  38. package/dist/plugin/skills/do/resolve-conflicts.md +1 -1
  39. package/dist/plugin/skills/do/resolve-reminder.md +8 -6
  40. package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  41. package/dist/plugin/skills/muggle-do/SKILL.md +2 -2
  42. package/dist/plugin/skills/muggle-feedback/ops/submit.md +4 -4
  43. package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -2
  44. package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +13 -11
  45. package/dist/plugin/skills/muggle-pr-followup/contract.md +13 -9
  46. package/dist/plugin/skills/muggle-pr-followup/finalize.md +1 -1
  47. package/dist/plugin/skills/muggle-pr-followup/reconcile.md +1 -1
  48. package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +4 -1
  49. package/dist/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +13 -13
  50. package/dist/plugin/skills/muggle-preferences/ops/configure.md +2 -2
  51. package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +8 -5
  52. package/dist/plugin/skills/muggle-test/SKILL.md +23 -126
  53. package/dist/plugin/skills/muggle-test/execute-local.md +60 -0
  54. package/dist/plugin/skills/muggle-test/execute-remote.md +49 -0
  55. package/dist/plugin/skills/muggle-test/reference.md +1 -2
  56. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +5 -5
  57. package/dist/release-manifest.json +4 -4
  58. package/dist/{src-2O5UH4FA.js → src-KHZUV253.js} +1 -1
  59. package/package.json +8 -7
  60. package/plugin/.claude-plugin/plugin.json +1 -1
  61. package/plugin/.cursor-plugin/plugin.json +1 -1
  62. package/plugin/scripts/ensure-electron-app.sh +1 -1
  63. package/plugin/scripts/guardrails.mjs +4 -2
  64. package/plugin/skills/_shared/dev-loop/publish.md +4 -8
  65. package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +1 -1
  66. package/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +1 -1
  67. package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -1
  68. package/plugin/skills/_shared/test-case-chain-readiness.md +1 -2
  69. package/plugin/skills/_shared/vcs/CLAUDE.md +9 -0
  70. package/plugin/skills/_shared/vcs/detect-vcs.md +28 -0
  71. package/{dist/plugin/skills/_shared/github-cli-recipes → plugin/skills/_shared/vcs/github}/unresolved-threads.md +1 -1
  72. package/{dist/plugin/skills/_shared/github-cli-recipes → plugin/skills/_shared/vcs/github}/verify-working-tree.md +1 -1
  73. package/plugin/skills/_shared/vcs/github.md +21 -0
  74. package/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +9 -0
  75. package/plugin/skills/_shared/vcs/gitlab/mr-create.md +11 -0
  76. package/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +18 -0
  77. package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +8 -0
  78. package/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +22 -0
  79. package/plugin/skills/_shared/vcs/gitlab/mr-note.md +7 -0
  80. package/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +27 -0
  81. package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +9 -0
  82. package/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +10 -0
  83. package/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +18 -0
  84. package/plugin/skills/_shared/vcs/gitlab.md +24 -0
  85. package/plugin/skills/do/address-reviews.md +10 -6
  86. package/plugin/skills/do/e2e-acceptance.md +4 -4
  87. package/plugin/skills/do/fix-ci.md +1 -1
  88. package/plugin/skills/do/input-routing.md +4 -4
  89. package/plugin/skills/do/open-prs/forward.md +6 -4
  90. package/plugin/skills/do/open-prs/update.md +8 -6
  91. package/plugin/skills/do/per-comment-replies.md +22 -13
  92. package/plugin/skills/do/resolve-conflicts.md +1 -1
  93. package/plugin/skills/do/resolve-reminder.md +8 -6
  94. package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  95. package/plugin/skills/muggle-do/SKILL.md +2 -2
  96. package/plugin/skills/muggle-feedback/ops/submit.md +4 -4
  97. package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -2
  98. package/plugin/skills/muggle-pr-followup/bootstrap.md +13 -11
  99. package/plugin/skills/muggle-pr-followup/contract.md +13 -9
  100. package/plugin/skills/muggle-pr-followup/finalize.md +1 -1
  101. package/plugin/skills/muggle-pr-followup/reconcile.md +1 -1
  102. package/plugin/skills/muggle-pr-followup/state-schemas.md +4 -1
  103. package/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +13 -13
  104. package/plugin/skills/muggle-preferences/ops/configure.md +2 -2
  105. package/plugin/skills/muggle-preferences/preference-gates/README.md +8 -5
  106. package/plugin/skills/muggle-test/SKILL.md +23 -126
  107. package/plugin/skills/muggle-test/execute-local.md +60 -0
  108. package/plugin/skills/muggle-test/execute-remote.md +49 -0
  109. package/plugin/skills/muggle-test/reference.md +1 -2
  110. package/plugin/skills/muggle-test-feature-local/SKILL.md +5 -5
  111. package/dist/plugin/skills/_shared/github-cli-recipes.md +0 -21
  112. package/dist/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +0 -11
  113. package/plugin/skills/_shared/github-cli-recipes.md +0 -21
  114. package/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +0 -11
  115. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/line-comments-for-review.md +0 -0
  116. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/loop-user-identity.md +0 -0
  117. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-checks.md +0 -0
  118. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-edit.md +0 -0
  119. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-metadata.md +0 -0
  120. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/push-to-branch.md +0 -0
  121. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/reply-line-comment.md +0 -0
  122. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/submitted-reviews.md +0 -0
  123. /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/top-level-comment.md +0 -0
  124. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/line-comments-for-review.md +0 -0
  125. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/loop-user-identity.md +0 -0
  126. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-checks.md +0 -0
  127. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-edit.md +0 -0
  128. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-metadata.md +0 -0
  129. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/push-to-branch.md +0 -0
  130. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/reply-line-comment.md +0 -0
  131. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/submitted-reviews.md +0 -0
  132. /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/top-level-comment.md +0 -0
@@ -0,0 +1,21 @@
1
+ # GitHub CLI Recipes
2
+
3
+ Reusable `gh` / `git` snippets used by `muggle-pr-followup` (watcher + bootstrap) and `/muggle-do` (address-reviews + open-prs). Each recipe is one file — load only what you need.
4
+
5
+ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`.
6
+
7
+ ## Index
8
+
9
+ | Recipe | Use case |
10
+ | :----- | :------- |
11
+ | [`pr-metadata`](github/pr-metadata.md) | Snapshot PR state, head SHA, branch, conflict + `behind_by` out-of-date detection — watcher + bootstrap. |
12
+ | [`submitted-reviews`](github/submitted-reviews.md) | Fetch a review by id / watcher's body-only-review check. |
13
+ | [`pr-checks`](github/pr-checks.md) | Check-run rollup for the head SHA — watcher's CI poll. |
14
+ | [`line-comments-for-review`](github/line-comments-for-review.md) | Pull a review's line comments — `/muggle-do` per-comment routing. |
15
+ | [`unresolved-threads`](github/unresolved-threads.md) | GraphQL unresolved-thread state — watcher's dispatch trigger + resolve-reminder. |
16
+ | [`reply-line-comment`](github/reply-line-comment.md) | POST a threaded reply on a line comment. |
17
+ | [`top-level-comment`](github/top-level-comment.md) | POST a top-level PR comment — resolve-reminder + overflow. |
18
+ | [`push-to-branch`](github/push-to-branch.md) | Push + capture new SHA after address-reviews work. |
19
+ | [`verify-working-tree`](github/verify-working-tree.md) | Three checks bootstrap runs before seeding state. |
20
+ | [`pr-edit`](github/pr-edit.md) | Refresh title or body when address-reviews mode flips state. |
21
+ | [`loop-user-identity`](github/loop-user-identity.md) | Resolve the GitHub login of the loop user. |
@@ -0,0 +1,9 @@
1
+ # Identify the loop user
2
+
3
+ The GitLab identity that owns the authenticated `glab` token. Resolve-reminder thread classification and reply attribution need this.
4
+
5
+ ```bash
6
+ glab api user --jq '.username'
7
+ ```
8
+
9
+ Cache in `state.md` under `Loop user:`; re-resolve only when missing.
@@ -0,0 +1,11 @@
1
+ # Open a merge request
2
+
3
+ For `open-prs`. Push the branch first (see [`../github/push-to-branch.md`](../github/push-to-branch.md)), then open the MR.
4
+
5
+ ```bash
6
+ glab mr create -R <group>/<project> \
7
+ --source-branch <branch> --target-branch <base> \
8
+ --title "<title>" --description "$(cat <file>)"
9
+ ```
10
+
11
+ `glab` prints the created MR's URL on success — capture stdout and store the URL for handoff (the watcher seeds from it, the user gets the link).
@@ -0,0 +1,18 @@
1
+ # MR discussions
2
+
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
+
5
+ ```bash
6
+ glab api projects/:id/merge_requests/:iid/discussions --paginate
7
+ ```
8
+
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).
10
+
11
+ Common filter:
12
+
13
+ - `system == false` (skip "added 2 commits", "changed the description", etc.)
14
+ - `author.username` in the resolved allow-list
15
+
16
+ Cursor: track the highest note/discussion `id` seen in `last_seen`; a note whose `id` exceeds it is new this tick. (GitLab ids are monotonic, so id ordering is reliable where `created_at` ties.)
17
+
18
+ Loop-echo skip: a note the loop itself posted carries the `<!-- muggle-do:bot -->` marker in its `body`. Classify by that marker per note, never by `author.username` alone — under a shared account the login is ambiguous. A marked note is the loop's own and must never re-trigger a cycle; an unmarked note from an allow-listed author is actionable.
@@ -0,0 +1,8 @@
1
+ # Refresh the MR title or description
2
+
3
+ For `open-prs/update.md` when E2E state flips (passing↔failing) or validation strategy changes. GitLab calls the body the **description**.
4
+
5
+ ```bash
6
+ glab mr update <iid> -R <group>/<project> --title "<new-title>"
7
+ glab mr update <iid> -R <group>/<project> --description "$(cat <file>)"
8
+ ```
@@ -0,0 +1,22 @@
1
+ # MR metadata snapshot
2
+
3
+ Fetch the fields the watcher and bootstrap need.
4
+
5
+ ```bash
6
+ glab mr view <iid> -R <group>/<project> -F json
7
+ ```
8
+
9
+ - `state` is one of `opened`, `merged`, `closed`, `locked` (lowercase — unlike GitHub's uppercase).
10
+ - `sha` is the current head SHA — store as `head_sha` in `prs.json`.
11
+ - `source_branch` is the branch — must match the working tree's branch in bootstrap. `target_branch` is the base.
12
+ - conflict comes from `detailed_merge_status`, not a `mergeable` + `mergeStateStatus` pair. The watcher's **conflict** signal is `detailed_merge_status == "broken_status"` or `"conflict"`. `"checking"`/`"unchecked"` means GitLab is still computing — treat as not-conflicting this tick.
13
+
14
+ ## Behind-by (out-of-date detection)
15
+
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
+
18
+ ```bash
19
+ glab api projects/:id/repository/compare?from=<target_branch>&to=<head_sha> --jq '.commits | length'
20
+ ```
21
+
22
+ GitLab's compare lists only the commits `to` is ahead by, so flip the direction: compare `from=<head_sha>&to=<target_branch>` and a non-empty `.commits` ⇒ the base has commits the head lacks ⇒ out of date. Empty ⇒ current with base.
@@ -0,0 +1,7 @@
1
+ # Top-level MR note
2
+
3
+ For the resolve-reminder stage and any non-threaded notice.
4
+
5
+ ```bash
6
+ glab mr note <iid> -R <group>/<project> -m "<text>"
7
+ ```
@@ -0,0 +1,27 @@
1
+ # MR pipeline rollup
2
+
3
+ Fetch the CI state for an MR's head — what the watcher polls to detect red CI. GitLab runs **one** pipeline of jobs per commit, not independent check-runs, so the rollup is over that pipeline's jobs.
4
+
5
+ ```bash
6
+ glab ci status -R <group>/<project> -b <source_branch>
7
+ ```
8
+
9
+ Or straight from the API for the latest pipeline and its jobs:
10
+
11
+ ```bash
12
+ glab api projects/:id/merge_requests/:iid/pipelines --jq '.[0].id'
13
+ glab api projects/:id/pipelines/<pipeline-id>/jobs --paginate
14
+ ```
15
+
16
+ Each job:
17
+
18
+ - `name` — the job's name (e.g. `lint`, `test`, `build`).
19
+ - `status` — one of `success` / `failed` / `running` / `pending` / `created` / `canceled` / `skipped` / `manual`.
20
+
21
+ Classify for the watcher:
22
+
23
+ - **red** — any job `failed`. Candidate for fix-ci.
24
+ - **pending** — no job `failed`, but any job `running` / `pending` / `created`. Pipeline hasn't settled → idle.
25
+ - **green** — every job `success` / `skipped` / `manual` / `canceled`, or no jobs at all.
26
+
27
+ The fix-ci dispatch carries the `name`s of the `failed` jobs.
@@ -0,0 +1,9 @@
1
+ # Reply to a discussion (threaded)
2
+
3
+ Used by `/muggle-do` per-comment inline replies. A threaded reply is a new note appended to an existing discussion.
4
+
5
+ ```bash
6
+ glab api --method POST \
7
+ projects/:id/merge_requests/:iid/discussions/<discussion-id>/notes \
8
+ -f body="<reply-text>"
9
+ ```
@@ -0,0 +1,10 @@
1
+ # Resolve a discussion thread
2
+
3
+ Mark a thread resolved once the loop's reply has addressed it — the resolve-reminder stage's action.
4
+
5
+ ```bash
6
+ glab api --method PUT \
7
+ "projects/:id/merge_requests/:iid/discussions/<discussion-id>?resolved=true"
8
+ ```
9
+
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,18 @@
1
+ # Unresolved discussions
2
+
3
+ For the watcher's dispatch trigger and the resolve-reminder stage. REST exposes resolution directly — no GraphQL needed, unlike GitHub.
4
+
5
+ ```bash
6
+ glab api projects/:id/merge_requests/:iid/discussions --paginate \
7
+ --jq '[.[] | select(.notes[0].resolvable == true) | select(any(.notes[]; .resolved == false))]'
8
+ ```
9
+
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`.
11
+
12
+ Walk each unresolved thread's `notes[]` in `created_at` order and classify by the loop marker (see [`../../pr-followup-helpers/loop-signature.md`](../../pr-followup-helpers/loop-signature.md)), not by `author.username` — the login is ambiguous under a shared account:
13
+
14
+ - **Addressed, awaiting resolve** — the **newest** note carries `<!-- muggle-do:bot -->`. The loop has replied and nothing newer waits. → resolve-reminder.
15
+ - **Unaddressed human comment** — the newest note lacks the marker and is newer than the thread's newest loop-marked note (or the thread has none yet). → actionable: the round should address it.
16
+ - **Not addressed** — indeterminate (e.g. no notes).
17
+
18
+ Each thread carries its `id` (the `discussion_id`) — the watcher collects this from an actionable thread to build its dispatch list and to target the resolve call later.
@@ -0,0 +1,24 @@
1
+ # GitLab CLI Recipes
2
+
3
+ Reusable `glab` / `git` snippets used by `muggle-pr-followup` (watcher + bootstrap) and `/muggle-do` (address-reviews + open-prs), mirroring the `gh` set for GitLab merge requests. Each recipe is one file — load only what you need.
4
+
5
+ Skills assume a working `glab auth status`. Auth errors surface verbatim from `glab`.
6
+
7
+ The project ref `:id` is `<group>/<project>` — URL-encode it for `glab api` (`mygroup/myproj` → `mygroup%2Fmyproj`). `:iid` is the MR's per-project internal id (the `!123` number), not the global id.
8
+
9
+ ## Index
10
+
11
+ | Recipe | Use case |
12
+ | :----- | :------- |
13
+ | [`mr-metadata`](gitlab/mr-metadata.md) | Snapshot MR state, head SHA, branch, conflict + out-of-date detection — watcher + bootstrap. |
14
+ | [`mr-pipeline`](gitlab/mr-pipeline.md) | Pipeline-job rollup for the head SHA — watcher's CI poll. |
15
+ | [`mr-discussions`](gitlab/mr-discussions.md) | Fetch incoming notes/discussions — watcher's feedback poll. |
16
+ | [`unresolved-discussions`](gitlab/unresolved-discussions.md) | Unresolved-discussion state — watcher's dispatch trigger + resolve-reminder. |
17
+ | [`reply-discussion`](gitlab/reply-discussion.md) | POST a threaded reply on a discussion. |
18
+ | [`mr-note`](gitlab/mr-note.md) | POST a top-level MR note — resolve-reminder + overflow. |
19
+ | [`resolve-discussion`](gitlab/resolve-discussion.md) | Mark a discussion thread resolved. |
20
+ | [`mr-edit`](gitlab/mr-edit.md) | Refresh title or description when address-reviews mode flips state. |
21
+ | [`mr-create`](gitlab/mr-create.md) | Open an MR + capture its URL for handoff. |
22
+ | [`loop-user-identity`](gitlab/loop-user-identity.md) | Resolve the GitLab username of the loop user. |
23
+ | [`push-to-branch`](github/push-to-branch.md) | Push + capture new SHA after address-reviews work (provider-agnostic). |
24
+ | [`verify-working-tree`](github/verify-working-tree.md) | Three checks bootstrap runs before seeding state (provider-agnostic). |
@@ -33,17 +33,21 @@ Before assembling work, rebase onto the latest default branch so the cycle addre
33
33
 
34
34
  ### Step 1 — Assemble the work set
35
35
 
36
- Two sources, combined into one batch (dedupe by comment id):
36
+ Resolve the provider per [`../_shared/vcs/detect-vcs.md`](../_shared/vcs/detect-vcs.md), then assemble.
37
+
38
+ **`github`** — two sources, combined into one batch (dedupe by comment id):
37
39
 
38
40
  **(a) The dispatched reviews.** For each review id in the input:
39
41
 
40
- - Fetch reviews per [`../_shared/github-cli-recipes/submitted-reviews.md`](../_shared/github-cli-recipes/submitted-reviews.md) (no watermark; filter to the specific id).
41
- - Fetch its line comments per [`../_shared/github-cli-recipes/line-comments-for-review.md`](../_shared/github-cli-recipes/line-comments-for-review.md).
42
+ - Fetch reviews per [`../_shared/vcs/github/submitted-reviews.md`](../_shared/vcs/github/submitted-reviews.md) (no watermark; filter to the specific id).
43
+ - Fetch its line comments per [`../_shared/vcs/github/line-comments-for-review.md`](../_shared/vcs/github/line-comments-for-review.md).
42
44
 
43
- **(b) Unaddressed comments on every unresolved thread.** Fetch unresolved threads per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md). For each thread classified **unaddressed human comment** — newest comment lacks the loop marker `<!-- muggle-do:bot -->` ([`loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)) and post-dates the loop's last marked reply — add it to the batch — unresolved thread state, not any review-id watermark, is the authority here. This is how a human thread follow-up (a marker-less reply) gets addressed. **Exclude** comments whose review id is in `last_seen.escalated_review_ids` — paused awaiting the user, not re-work.
45
+ **(b) Unaddressed comments on every unresolved thread.** Fetch unresolved threads per [`../_shared/vcs/github/unresolved-threads.md`](../_shared/vcs/github/unresolved-threads.md). For each thread classified **unaddressed human comment** — newest comment lacks the loop marker `<!-- muggle-do:bot -->` ([`loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)) and post-dates the loop's last marked reply — add it to the batch — unresolved thread state, not any review-id watermark, is the authority here. This is how a human thread follow-up (a marker-less reply) gets addressed. **Exclude** comments whose review id is in `last_seen.escalated_review_ids` — paused awaiting the user, not re-work.
44
46
 
45
47
  Group (a) and (b) into one combined batch.
46
48
 
49
+ **`gitlab`** — single source (no review-id watermark; discussion state is the sole authority). Fetch unresolved discussions per [`../_shared/vcs/gitlab/unresolved-discussions.md`](../_shared/vcs/gitlab/unresolved-discussions.md) (drop to [`../_shared/vcs/gitlab/mr-discussions.md`](../_shared/vcs/gitlab/mr-discussions.md) for raw notes where classification needs them). The input ids are discussion ids; the batch is every discussion classified **unaddressed human comment** — newest note lacks the marker and post-dates the loop's last marked note. **Exclude** discussions whose id is in `last_seen.escalated_review_ids`. A discussion is the unit of work in place of GitHub's review + line-comment pair.
50
+
47
51
  ### Step 2 — Classify each review
48
52
 
49
53
  Apply the classify rule in [`../_shared/pr-followup-helpers/classify.md`](../_shared/pr-followup-helpers/classify.md). Two outcomes per review:
@@ -93,7 +97,7 @@ Invoke [`open-prs/update.md`](open-prs/update.md) (pass the PR URL + slug + exis
93
97
 
94
98
  #### 4f. Post per-comment inline replies
95
99
 
96
- Invoke [`per-comment-replies.md`](per-comment-replies.md) with the actionable reviews and the new SHA. One reply per comment, in its own thread, describing what was done for it.
100
+ Invoke [`per-comment-replies.md`](per-comment-replies.md) with the actionable reviews (`gitlab`: discussions) and the new SHA. One reply per comment, in its own thread, describing what was done for it; on `gitlab` the same step also resolves each fully-addressed discussion.
97
101
 
98
102
  (The resolve-reminder runs once per round in Step 5.5 below — not only after a push — so a round that pushed nothing still nudges addressed-but-unresolved threads.)
99
103
 
@@ -109,7 +113,7 @@ Invoke [`resolve-reminder.md`](resolve-reminder.md) once, regardless of whether
109
113
 
110
114
  ### Step 6 — Respawn the watcher
111
115
 
112
- Refresh PR state per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). If the PR is now merged or closed:
116
+ Refresh PR state with the provider resolved in Step 1 — `github` per [`../_shared/vcs/github/pr-metadata.md`](../_shared/vcs/github/pr-metadata.md), `gitlab` per [`../_shared/vcs/gitlab/mr-metadata.md`](../_shared/vcs/gitlab/mr-metadata.md) (`state == merged`/`closed`, lowercase). If the PR is now merged or closed:
113
117
 
114
118
  1. Write `result.md` per [`../muggle-pr-followup/state-schemas.md`](../muggle-pr-followup/state-schemas.md#resultmd).
115
119
  2. Do **not** respawn the watcher.
@@ -27,7 +27,7 @@ For local runs, the tool boundaries are:
27
27
  | Scope | MCP tools |
28
28
  | :---- | :-------- |
29
29
  | Cloud (projects, cases, scripts, auth) | `muggle-remote-*` |
30
- | Local (Electron run, publish, results) | `muggle-local-*` |
30
+ | Local (Electron run, results — the studio publishes during the run) | `muggle-local-*` |
31
31
 
32
32
  This keeps execution deterministic: local runs do not depend on cloud replay execution availability.
33
33
 
@@ -96,9 +96,9 @@ Based on the changed files and the requirements goal, determine which test cases
96
96
  - Test cases that cover areas potentially affected by the changes
97
97
  - When in doubt, include the test case (better to over-test than miss a regression)
98
98
 
99
- ### Step 4: Run the dev loop, publish, gather screenshots
99
+ ### Step 4: Run the dev loop, gather screenshots
100
100
 
101
- For each relevant test case, run the shared loop in [`../_shared/dev-loop/run.md`](../_shared/dev-loop/run.md): `muggle-remote-test-script-list` by `testCaseId` to pick [replay vs regen](../_shared/dev-loop/run.md), [execute with `timeoutMs`](../_shared/dev-loop/timeouts.md), [fetch the result](../_shared/dev-loop/failures.md) and [interpret failures](../_shared/dev-loop/failures.md), [publish](../_shared/dev-loop/publish.md), and gather [per-step screenshots](../_shared/dev-loop/publish.md).
101
+ For each relevant test case, run the shared loop in [`../_shared/dev-loop/run.md`](../_shared/dev-loop/run.md): `muggle-remote-test-script-list` by `testCaseId` to pick [replay vs regen](../_shared/dev-loop/run.md), [execute with `timeoutMs`](../_shared/dev-loop/timeouts.md), [fetch the result](../_shared/dev-loop/failures.md) and [interpret failures](../_shared/dev-loop/failures.md), then read the studio-published [cloud refs and per-step screenshots](../_shared/dev-loop/publish.md) off the run result.
102
102
 
103
103
  Inputs to the loop: `mode` from the script-exists check, `localUrl`/project from Step 1.7, `cwd` = the working tree recorded in `state.md`.
104
104
 
@@ -161,5 +161,5 @@ Failed runs use the same evidence + diagnosis assembly as the interactive debug
161
161
  - Replay/timeout/result discipline per [`../_shared/dev-loop/run.md`](../_shared/dev-loop/run.md) — never hand-build `actionScript`, always pass `timeoutMs`, read structured run-result fields.
162
162
  - No hiding failures: surface errors, exit codes, and artifact paths.
163
163
  - In `local-e2e` mode, every relevant test case must be executed — generate a new script if none exists (no skips).
164
- - Always publish after execution to ensure screenshots are cloud-accessible for PR comments.
164
+ - The studio publishes every run during execution, so screenshots are cloud-accessible for PR comments — read the refs off the run result, never re-publish.
165
165
  - **Never drop a test case from the report because it "couldn't run cleanly."** A test that didn't reach its assertion is `inconclusive`, not absent. Dropping it produces misleading verdicts and pushes downstream PR-comment renderers to hand-write the comment — which is the failure mode this stage exists to prevent.
@@ -10,7 +10,7 @@ Resolve red CI on a PR's head — lint/format, typecheck, and failing unit tests
10
10
 
11
11
  ## Input
12
12
 
13
- `$ARGUMENTS` carries a `github.com/.../pull/<n>` URL, `slug=<slug>`, and the failing check names (no review ids). Parse all three.
13
+ `$ARGUMENTS` carries a PR/MR URL (`github.com/.../pull/<n>` or `<host>/<group>/<project>/-/merge_requests/<iid>`), `slug=<slug>`, and the failing check names (no review ids). Parse all three. The names are GitHub check-runs or — when the URL resolves `gitlab` per [`../_shared/vcs/detect-vcs.md`](../_shared/vcs/detect-vcs.md) — the failed pipeline-job names the watcher read off [`../_shared/vcs/gitlab/mr-pipeline.md`](../_shared/vcs/gitlab/mr-pipeline.md); the fix cycle below is identical for either.
14
14
 
15
15
  ## Inputs from disk
16
16
 
@@ -1,10 +1,10 @@
1
1
  # Input routing
2
2
 
3
- How `/muggle-do` resolves `$ARGUMENTS` to a mode. Modes 1–4 are programmatic — dispatched by the watcher — so never ask on those. Inspect in order:
3
+ How `/muggle-do` resolves `$ARGUMENTS` to a mode. Modes 1–4 are programmatic — dispatched by the watcher — so never ask on those. A change-URL in modes 1–3 is either a GitHub PR (`github.com/.../pull/<n>`) or a GitLab MR (`<host>/.../-/merge_requests/<iid>`); provider is resolved via [`../_shared/vcs/detect-vcs.md`](../_shared/vcs/detect-vcs.md). Inspect in order:
4
4
 
5
- 1. **Address-reviews** — a `github.com/.../pull/<n>` URL **and** one or more review ids (integers ≥ 100000000) → [`address-reviews.md`](address-reviews.md).
6
- 2. **Fix-CI** — a `github.com/.../pull/<n>` URL **and** a `fix ci` / `fix-ci` directive with failing check names (no review ids) → [`fix-ci.md`](fix-ci.md).
7
- 3. **Rebase** — a `github.com/.../pull/<n>` URL **and** a `rebase` directive (or legacy `resolve conflicts` / `resolve-conflicts`; no review ids, no check names) → [`resolve-conflicts.md`](resolve-conflicts.md). Rebases the branch onto its base whether it's merely behind or actually conflicting.
5
+ 1. **Address-reviews** — a PR/MR URL **and** an address-reviews directive carrying review/discussion ids → [`address-reviews.md`](address-reviews.md). On GitHub these ids are integers ≥ 100000000; that magnitude is GitHub-specific lore, **not** a portable test. Under GitLab, tell this apart from other directives by the directive keyword plus the presence of discussion ids, never by id size.
6
+ 2. **Fix-CI** — a PR/MR URL **and** a `fix ci` / `fix-ci` directive with failing check names (no review/discussion ids) → [`fix-ci.md`](fix-ci.md).
7
+ 3. **Rebase** — a PR/MR URL **and** a `rebase` directive (or legacy `resolve conflicts` / `resolve-conflicts`; no review/discussion ids, no check names) → [`resolve-conflicts.md`](resolve-conflicts.md). Rebases the branch onto its base whether it's merely behind or actually conflicting.
8
8
  4. **Post-merge cleanup** — a `cleanup` token and `slug=<slug>` (no PR URL, no review ids), optionally `state=<merged|closed>` (default `merged`) → [`cleanup.md`](cleanup.md).
9
9
  5. **Empty / `help` / `menu` / `?`** → menu + session selector.
10
10
  6. **Task automation** (perform an action on a website) → `muggle:muggle-browser-task`.
@@ -1,6 +1,6 @@
1
1
  # Open PR — forward mode
2
2
 
3
- Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fresh feature. Creates the PR via `gh pr create`, seeds session state, dispatches the first watcher.
3
+ Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fresh feature. Opens the change — a PR on GitHub (`gh pr create`) or an MR on GitLab (`glab mr create`), provider resolved in Step 4 — then seeds session state and dispatches the first watcher.
4
4
 
5
5
  ## Turn preamble
6
6
 
@@ -30,11 +30,13 @@ Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fres
30
30
  - `## Acceptance Criteria` — bulleted; omit if empty.
31
31
  - `## Changes` — summary of what changed in this repo.
32
32
  - `## Validation` — one line: link to E2E report, `unit-only`, or `skip — <reason>`.
33
- - **If an E2E report exists,** invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode B to render the walkthrough block. Embed the returned `body` verbatim. If no report, skip this block entirely.
33
+ - **Walkthrough block** — only when an E2E report exists. Fire [`postPRVisualWalkthrough`](../../muggle-preferences/preference-gates/postPRVisualWalkthrough.md); on skip, omit this block. Otherwise invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode B and embed the returned `body` verbatim. No report → skip the block.
34
34
 
35
- 4. **Create:** `gh pr create --title "..." --body "..." --head <branch>`. Capture the PR URL and number.
35
+ 4. **Create:** resolve the provider per [`../../_shared/vcs/detect-vcs.md`](../../_shared/vcs/detect-vcs.md).
36
+ - `github` → `gh pr create --title "..." --body "..." --head <branch>`. Capture the PR URL and number.
37
+ - `gitlab` → open the change via [`../../_shared/vcs/gitlab/mr-create.md`](../../_shared/vcs/gitlab/mr-create.md): `glab mr create --source-branch <branch> --target-branch <base> --title "..." --description "..."`. Capture the MR URL and iid.
36
38
 
37
- 5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once per [`../../_shared/github-cli-recipes/top-level-comment.md`](../../_shared/github-cli-recipes/top-level-comment.md). Never post when `comment` is `null`.
39
+ 5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once using the provider resolved in Step 4 — `github` per [`../../_shared/vcs/github/top-level-comment.md`](../../_shared/vcs/github/top-level-comment.md), `gitlab` per [`../../_shared/vcs/gitlab/mr-note.md`](../../_shared/vcs/gitlab/mr-note.md). Never post when `comment` is `null`.
38
40
 
39
41
  ## Stage 8 handoff
40
42
 
@@ -20,21 +20,23 @@ Does **not** create a PR, seed session state, or dispatch a watcher (`/muggle-do
20
20
 
21
21
  Skip `autoCreatePR` (it gates creation, not update). The PR's title is left intact unless state changed in Step 3.
22
22
 
23
- 1. **Push:** per [`../../_shared/github-cli-recipes/push-to-branch.md`](../../_shared/github-cli-recipes/push-to-branch.md). Capture the new SHA.
23
+ Resolve the provider once per [`../../_shared/vcs/detect-vcs.md`](../../_shared/vcs/detect-vcs.md). Wherever Steps 3–4 below edit title/description: `github` uses `gh pr edit` per [`../../_shared/vcs/github/pr-edit.md`](../../_shared/vcs/github/pr-edit.md); `gitlab` uses `glab mr update --title --description` per [`../../_shared/vcs/gitlab/mr-edit.md`](../../_shared/vcs/gitlab/mr-edit.md).
24
+
25
+ 1. **Push:** per [`../../_shared/vcs/github/push-to-branch.md`](../../_shared/vcs/github/push-to-branch.md). Capture the new SHA.
24
26
 
25
27
  2. **Append new SHA** to `last_seen.json[<key>].pushed_shas` (the resolve-reminder stage uses this to recognize threads addressed by the loop). Set `last_seen.last_pushed_sha` to the new SHA too.
26
28
 
27
29
  3. **Refresh title if state changed.** Compare the new state against the current PR title prefix:
28
- - E2E now passing, current title has `[E2E FAILING]` → strip the prefix per [`../../_shared/github-cli-recipes/pr-edit.md`](../../_shared/github-cli-recipes/pr-edit.md).
30
+ - E2E now passing, current title has `[E2E FAILING]` → strip the prefix per [`../../_shared/vcs/github/pr-edit.md`](../../_shared/vcs/github/pr-edit.md).
29
31
  - E2E now failing, current title has no `[E2E FAILING]` → add the prefix.
30
32
  - Validation now ran (was unit-only/skip, now has E2E report) → strip `[UNVERIFIED]` or `[UNIT-ONLY]`.
31
33
  - Otherwise → no title change.
32
34
 
33
- 4. **Refresh body when validation outcome changed** — only when the `## Validation` section's content differs from what's in the body. Use the `--body-file` form in [`../../_shared/github-cli-recipes/pr-edit.md`](../../_shared/github-cli-recipes/pr-edit.md). Preserve `## Goal` and `## Acceptance Criteria` verbatim.
35
+ 4. **Refresh body when validation outcome changed** — only when the `## Validation` section's content differs from what's in the body. Use the `--body-file` form in [`../../_shared/vcs/github/pr-edit.md`](../../_shared/vcs/github/pr-edit.md). Preserve `## Goal` and `## Acceptance Criteria` verbatim.
34
36
 
35
- 5. **Visual walkthrough comment** — if an E2E report exists, invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode A. Always a fresh comment per cycle; do not edit prior walkthrough comments.
37
+ 5. **Visual walkthrough comment** — only when an E2E report exists. Fire [`postPRVisualWalkthrough`](../../muggle-preferences/preference-gates/postPRVisualWalkthrough.md) (PR number from `prs.json`); on skip, record `skipped (gate)` and continue. Otherwise invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode A — a fresh comment per cycle; do not edit prior walkthrough comments.
36
38
 
37
- 6. **Overflow comment** — same rule as forward mode: post when the walkthrough skill returns non-null `comment`, via [`../../_shared/github-cli-recipes/top-level-comment.md`](../../_shared/github-cli-recipes/top-level-comment.md).
39
+ 6. **Overflow comment** — same rule as forward mode: post when the walkthrough skill returns non-null `comment`, via [`../../_shared/vcs/github/top-level-comment.md`](../../_shared/vcs/github/top-level-comment.md).
38
40
 
39
41
  ## Handoff
40
42
 
@@ -50,5 +52,5 @@ Return control to `/muggle-do`'s address-reviews orchestrator. The orchestrator
50
52
  **PR updated:** URL (new SHA: `<short-sha>`)
51
53
  **Title refreshed:** yes | no
52
54
  **Body refreshed:** yes | no
53
- **Walkthrough comment:** posted | skipped (no report)
55
+ **Walkthrough comment:** posted | skipped (no report) | skipped (gate)
54
56
  **Overflow comment:** posted | skipped
@@ -2,13 +2,15 @@
2
2
 
3
3
  A `/muggle-do` step invoked from [`address-reviews.md`](address-reviews.md) (Step 4f) after the push has succeeded. Posts one inline nested reply on each line comment from the actionable reviews, describing what was done for that comment and referencing the new SHA.
4
4
 
5
- This is **not** a top-level "summary reply on the review." Each comment thread gets its own reply, in context, via GitHub's `/comments/{id}/replies` endpoint.
5
+ This is **not** a top-level "summary reply on the review." Each comment thread gets its own reply, in context — on `github` via the `/comments/{id}/replies` endpoint, on `gitlab` via a threaded note on the discussion.
6
6
 
7
7
  ## Inputs
8
8
 
9
- - `actionable_reviews` — the list of reviews classified actionable in `address-reviews.md` Step 2.
9
+ - `actionable_reviews` — the list of reviews classified actionable in `address-reviews.md` Step 2. On `gitlab`, these are the actionable **discussions** (no review envelope) — read each as a thread of notes.
10
10
  - `new_sha` — the SHA `open-prs/update.md` just pushed.
11
- - The PR's owner, repo, number.
11
+ - The PR's owner, repo, number. On `gitlab`, the project ref and MR iid.
12
+
13
+ Resolve the provider once per [`../_shared/vcs/detect-vcs.md`](../_shared/vcs/detect-vcs.md).
12
14
 
13
15
  ## Procedure
14
16
 
@@ -23,14 +25,18 @@ If a comment has no associated change in either source (e.g. the comment was a q
23
25
 
24
26
  ### Step 2 — Post one reply per comment
25
27
 
26
- For each comment id with a description:
28
+ For each comment id with a description, post the reply with the resolved provider:
27
29
 
28
- ```bash
29
- gh api --method POST \
30
- -H "Accept: application/vnd.github+json" \
31
- repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
32
- -f body="<reply-body>"
33
- ```
30
+ - **`github`** — nested reply on the line comment:
31
+
32
+ ```bash
33
+ gh api --method POST \
34
+ -H "Accept: application/vnd.github+json" \
35
+ repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
36
+ -f body="<reply-body>"
37
+ ```
38
+
39
+ - **`gitlab`** — one threaded note per actionable discussion per [`../_shared/vcs/gitlab/reply-discussion.md`](../_shared/vcs/gitlab/reply-discussion.md) (the discussion id stands in for the comment id), then resolve each fully-addressed thread per [`../_shared/vcs/gitlab/resolve-discussion.md`](../_shared/vcs/gitlab/resolve-discussion.md). The loop-marked reply is the addressed signal; the resolve is GitLab's equivalent of a reviewer closing the thread, which the loop can do directly. Resolving folds the discussion out of the next tick's actionable set, so no separate resolve-reminder nudge is needed for it.
34
40
 
35
41
  Reply body uses the template in [`../muggle-pr-followup/output-templates/inline-reply.md`](../muggle-pr-followup/output-templates/inline-reply.md):
36
42
 
@@ -43,7 +49,10 @@ Addressed in <short-sha>: <one-line summary of the change made for THIS comment>
43
49
 
44
50
  `<short-sha>` is the first 7 chars of `new_sha`; the body must contain that substring so the resolve-reminder stage knows which push addressed the thread. The trailing signature block is mandatory — its `<!-- muggle-do:bot -->` marker is what identifies the reply as loop-authored (see [`../_shared/pr-followup-helpers/loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)).
45
51
 
46
- ### Step 3 — Handle review-body-only comments
52
+ ### Step 3 — Handle review-body-only comments (GitHub only)
53
+
54
+ GitLab has no review envelope — every note belongs to a discussion handled in Step 2 — so this step is GitHub-only.
55
+
47
56
 
48
57
  If an actionable review has a non-empty `body` and **zero** line comments, GitHub has no `/replies` endpoint for the review body itself (the API has been inconsistent on this and the only reliable path is a top-level PR comment that references the review). Use the *top-level reference* form:
49
58
 
@@ -54,7 +63,7 @@ Re: review #<review_id> — addressed in <short-sha>: <one-line summary>.
54
63
  🤖 _Automated reply from muggle-do._
55
64
  ```
56
65
 
57
- Posted per [`../_shared/github-cli-recipes/top-level-comment.md`](../_shared/github-cli-recipes/top-level-comment.md). Fires at most once per actionable review-with-no-line-comments. Does not fire if the review has line comments — Step 2 covers those.
66
+ Posted per [`../_shared/vcs/github/top-level-comment.md`](../_shared/vcs/github/top-level-comment.md). Fires at most once per actionable review-with-no-line-comments. Does not fire if the review has line comments — Step 2 covers those.
58
67
 
59
68
  ## Failure modes
60
69
 
@@ -63,5 +72,5 @@ Posted per [`../_shared/github-cli-recipes/top-level-comment.md`](../_shared/git
63
72
 
64
73
  ## Invariants
65
74
 
66
- - One reply per line comment. No per-review summary reply anywhere.
75
+ - One reply per line comment (`gitlab`: per discussion). No per-review summary reply anywhere.
67
76
  - Every reply body contains the new SHA's 7-char prefix (which push addressed it) and ends with the loop signature block — the `<!-- muggle-do:bot -->` marker, not the author login, is what identifies loop-authored comments.
@@ -20,7 +20,7 @@ From `~/.muggle-ai/muggle-do/sessions/<slug>/`: `prs.json` (PR + branch + `head_
20
20
 
21
21
  ### Step 1 — Re-attach
22
22
 
23
- Materialize the PR branch in its worktree per [`../_shared/pr-branch-worktree.md`](../_shared/pr-branch-worktree.md) (or use `state.md`'s `worktreePath`). Capture `rebase_sha = prs.json[0].head_sha` and the base branch (`baseRefName` from [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md)).
23
+ Materialize the PR branch in its worktree per [`../_shared/pr-branch-worktree.md`](../_shared/pr-branch-worktree.md) (or use `state.md`'s `worktreePath`). Capture `rebase_sha = prs.json[0].head_sha` and the base branch (`baseRefName` from [`../_shared/vcs/github/pr-metadata.md`](../_shared/vcs/github/pr-metadata.md)).
24
24
 
25
25
  ### Step 2 — Rebase onto base (resolve conflicts if any)
26
26
 
@@ -14,21 +14,23 @@ This stage does not print a turn preamble — it runs inside `/muggle-do`'s addr
14
14
 
15
15
  ## Inputs
16
16
 
17
- - The current PR (URL, owner, repo, number) from the session's `prs.json`.
17
+ - The current PR (URL, owner, repo, number) from the session's `prs.json`. On `gitlab`, the project ref and MR iid.
18
18
  - `last_seen.pushed_shas[]` from `last_seen.json` — the list of every SHA `/muggle-do` has pushed for this PR.
19
- - The loop user's GitHub login (cached in `state.md` under `Loop user:` — re-resolve per [`../_shared/github-cli-recipes/loop-user-identity.md`](../_shared/github-cli-recipes/loop-user-identity.md) if missing).
19
+ - The loop user's login (cached in `state.md` under `Loop user:`) — re-resolve when missing, `github` per [`../_shared/vcs/github/loop-user-identity.md`](../_shared/vcs/github/loop-user-identity.md), `gitlab` per [`../_shared/vcs/gitlab/loop-user-identity.md`](../_shared/vcs/gitlab/loop-user-identity.md).
20
+
21
+ Resolve the provider once per [`../_shared/vcs/detect-vcs.md`](../_shared/vcs/detect-vcs.md).
20
22
 
21
23
  ## Procedure
22
24
 
23
25
  ### Step 1 — Fetch unresolved comment threads
24
26
 
25
- Per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md). Filter client-side to `isResolved == false`. Each thread carries its line comments with `author.login`, `body`, and `databaseId`.
27
+ `github` per [`../_shared/vcs/github/unresolved-threads.md`](../_shared/vcs/github/unresolved-threads.md) (filter client-side to `isResolved == false`; each thread carries its line comments with `author.login`, `body`, `databaseId`); `gitlab` per [`../_shared/vcs/gitlab/unresolved-discussions.md`](../_shared/vcs/gitlab/unresolved-discussions.md) (each discussion carries its `notes[]` and `id`).
26
28
 
27
29
  If the API call fails, log the error to `followup.log` and skip the stage. Do not surface a user-facing error — the resolve reminder is a nice-to-have, not load-bearing. The reply summaries on the threads themselves still happen.
28
30
 
29
31
  ### Step 2 — Classify each thread
30
32
 
31
- Per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md): walk each thread's comments in `createdAt` order and classify by the loop marker `<!-- muggle-do:bot -->` ([`../_shared/pr-followup-helpers/loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)), not by `author.login`:
33
+ Walk each thread's comments (`gitlab`: notes) in chronological order and classify by the loop marker `<!-- muggle-do:bot -->` ([`../_shared/pr-followup-helpers/loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)), not by `author.login` — `github` per [`../_shared/vcs/github/unresolved-threads.md`](../_shared/vcs/github/unresolved-threads.md), `gitlab` per [`../_shared/vcs/gitlab/unresolved-discussions.md`](../_shared/vcs/gitlab/unresolved-discussions.md):
32
34
 
33
35
  - **Addressed, awaiting resolve** — the **newest** comment carries the marker. The loop replied and nothing newer is waiting. These feed the reminder.
34
36
  - **Unaddressed human comment** — the newest comment lacks the marker and post-dates the loop's last marked reply (or there is no loop reply yet). The address-reviews round handles these as work (Step 1 sweep), not the reminder.
@@ -42,7 +44,7 @@ Collect the thread `databaseId` of every thread classified **addressed, awaiting
42
44
 
43
45
  ### Step 4 — Post the top-level reminder comment
44
46
 
45
- If the resolve-reminder list is non-empty, post **one** top-level PR comment using the template in [`../muggle-pr-followup/output-templates/resolve-reminder.md`](../muggle-pr-followup/output-templates/resolve-reminder.md) per [`../_shared/github-cli-recipes/top-level-comment.md`](../_shared/github-cli-recipes/top-level-comment.md). The comment carries the loop signature, so a later round's scan won't read it back as a human comment.
47
+ If the resolve-reminder list is non-empty, post **one** top-level comment using the template in [`../muggle-pr-followup/output-templates/resolve-reminder.md`](../muggle-pr-followup/output-templates/resolve-reminder.md) — `github` per [`../_shared/vcs/github/top-level-comment.md`](../_shared/vcs/github/top-level-comment.md), `gitlab` per [`../_shared/vcs/gitlab/mr-note.md`](../_shared/vcs/gitlab/mr-note.md). The comment carries the loop signature, so a later round's scan won't read it back as a human comment.
46
48
 
47
49
  If the list is empty, post **nothing**. Still emit telemetry so the stage's run is observable.
48
50
 
@@ -59,7 +61,7 @@ Emit one event per [`../_shared/telemetry-events/muggle-do-resolve-reminder.md`]
59
61
 
60
62
  This stage is best-effort. Any failure is logged to `followup.log` and silently skipped — the reviewer still gets the per-comment inline replies (the canonical signal that work was done), and the cycle continues to respawn the watcher.
61
63
 
62
- The one exception: do not silently swallow a `gh pr comment` failure if Step 4 ran. The comment is a user-visible artifact; if it fails, surface the underlying `gh` error to the user so they know the reminder didn't post.
64
+ The one exception: do not silently swallow a Step 4 post failure (`gh pr comment` / `glab mr note`) if Step 4 ran. The comment is a user-visible artifact; if it fails, surface the underlying CLI error to the user so they know the reminder didn't post.
63
65
 
64
66
  ## Invariants
65
67
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: muggle-browser-task
3
- description: Run a browser automation task on a website using natural language. Finds or creates the Muggle Test project, use case, test case, and script, then executes locally via the electron app. Use when the user wants to perform an action on a website (post, fill a form, click through a flow) rather than implement a code change.
3
+ description: "Perform a real action on a website or web app from a plain-English instruction — log in and do the thing: submit a form, create or update a record, place an order, refund a charge, file a ticket, publish a post, click through and complete a multi-step flow (Stripe, Jira, Shopify, the AWS console, WordPress, LinkedIn, an admin dashboard). Use whenever the user wants something actually done in a web UI ('log into X and …', 'submit the form', 'create the ticket', 'refund the charge', 'place the order', 'update the listing'); Muggle drives a real browser to do it, so reach for this rather than declining or doing it by hand. Boundary: this performs the action (operating a site's own UI), not migrating personal data between consumer apps; to verify a flow works instead, use muggle-test-feature-local."
4
4
  ---
5
5
 
6
6
  # Muggle Test Task Runner
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: muggle-do
3
- description: Unified Muggle AI workflow entry point. Use when user types muggle do or asks for autonomous implementation to PR. Also handles the `address-reviews` directive (dispatched by the muggle-pr-followup watcher when new submitted reviews land on a PR).
3
+ description: Take a feature request, spec, design, bug, or ticket and carry it autonomously to an opened pull request — write the code, test it, raise the PR. Use whenever the user wants something built, implemented, added, or fixed with a PR opened, raised, or put up at the end — "implement rate limiting and raise a PR", "build the CSV export and put up a PR", "take this Jira ticket and run with it, ping me when the PR is up", "fix this bug and open a pr", or "muggle do …". Also handles the internal `address-reviews` directive the muggle-pr-followup watcher dispatches when new reviews land. Not for testing or validating existing changes (use muggle-test), nor watching a PR's incoming reviews (use muggle-pr-followup).
4
4
  ---
5
5
 
6
6
  # Muggle Test Do
@@ -65,7 +65,7 @@ When invoked with the directive (PR URL + slug + review ids), routes to [`../do/
65
65
  | `autoResolveConflicts` | On rebase conflict — resolve autonomously behind a verify-or-rollback gate (opt-in), or abort + escalate (default `never`) |
66
66
  | `autoRouteBuildToMuggleDo` | Front-door guardrail — route build/implement/fix requests through this pipeline (build delegated to superpowers); fired by the UserPromptSubmit guardrail, default `ask` |
67
67
 
68
- `autoUseWorktree`, `autoRebase`, `autoResolveConflicts`, `autoCreatePR`, `autoCleanup` fire from per-stage files.
68
+ `autoUseWorktree`, `autoRebase`, `autoResolveConflicts`, `autoCreatePR`, `autoCleanup`, `postPRVisualWalkthrough` fire from per-stage files.
69
69
 
70
70
  ## Session model
71
71
 
@@ -20,15 +20,15 @@ A Muggle dashboard URL looks like `https://www.muggle-ai.com/muggleTestV0/dashbo
20
20
  If this skill was invoked by another skill (`muggle-test`, `muggle-test-feature-local`) the caller MUST pass the just-finished `runId` as context. If you have a `runId`:
21
21
 
22
22
  1. `muggle-local-run-result-get` with the `runId`.
23
- 2. The result includes the test case context. If the run was already published (`muggle-local-publish-test-script` was called), the resulting `testScriptId` gives you the cloud `actionScriptId` via `muggle-remote-test-script-get`.
24
- 3. **If the run was NOT published**, upload it first via `muggle-remote-local-run-upload` (passing the `runId`'s test case context and `actionScript` payload). Use the returned cloud `actionScriptId`.
23
+ 2. The result includes the test case context. The studio publishes every local run during execution, so the run result carries the cloud `cloudActionScriptId` — use it directly as the `actionScriptId`.
24
+ 3. **Legacy fallback** — if `cloudActionScriptId` is absent (a run from an older Electron build that didn't publish), upload it first via `muggle-remote-local-run-upload` (passing the `runId`'s test case context and `actionScript` payload). Use the returned cloud `actionScriptId`.
25
25
 
26
26
  If no `runId` was passed but the user is plausibly continuing from a recent test:
27
27
 
28
28
  1. `muggle-local-list-sessions` and pick sessions completed in the last 10 minutes for the current project.
29
29
  2. If exactly one fresh session exists, offer it as the default via `AskUserQuestion` ("Feedback on the run from <X> minutes ago?" with **Yes / Pick a different run**).
30
30
  3. If multiple, present the top 3 via `AskUserQuestion` (most recent first).
31
- 4. Once picked, follow the publish-or-upload path above.
31
+ 4. Once picked, resolve `cloudActionScriptId` from the run result (legacy upload fallback above).
32
32
 
33
33
  If neither chained nor recent fits, fall through to 1c.
34
34
 
@@ -127,4 +127,4 @@ If any pieces failed, list them under a `Failed:` header with the error.
127
127
  - Always render the script (step 2) before collecting feedback.
128
128
  - One create call per feedback piece — never concatenate paragraphs across targets.
129
129
  - Convert step numbers from 1-based (UI) to 0-based (wire) at submit time.
130
- - If the user came from a non-uploaded local run, do the upload silently before submit; do not ask for permission for the upload itself.
130
+ - If a legacy local run carries no `cloudActionScriptId`, do the upload silently before submit; do not ask for permission for the upload itself.
@@ -6,7 +6,7 @@ This folder holds the watcher loop that drives one PR toward merge-ready. The wa
6
6
 
7
7
  - [`SKILL.md`](SKILL.md) — public entry. Routing between bootstrap (URL input), tick (slug + PR number), and auto-track (no args). Read first.
8
8
  - [`auto-track.md`](auto-track.md) — the no-args procedure: discovers PRs pushed this session (any repo) and seeds one poll-only watcher each. Seeds no E2E context — the watcher only watches.
9
- - [`bootstrap.md`](bootstrap.md) — the bootstrap procedure (asks once for the E2E validation context, seeds state, dispatches the first watcher).
9
+ - [`bootstrap.md`](bootstrap.md) — the bootstrap procedure (resolves the validation context once when the PR has a testable surface — else seeds poll-only like auto-track — then dispatches the first watcher).
10
10
  - [`contract.md`](contract.md) — the watcher per-tick procedure (poll → dispatch → exit).
11
11
  - [`finalize.md`](finalize.md) — shared termination sequence for a terminal PR (mark terminal, `result.md`, log/telemetry, unschedule cron, post-merge cleanup handoff). Called by `contract.md` and `reconcile.md`.
12
12
  - [`reconcile.md`](reconcile.md) — sweep that finalizes slots whose PR went terminal while polling lapsed; runs at the top of auto-track and on demand.
@@ -21,6 +21,6 @@ Shared with other skills, under `../_shared/`:
21
21
  - [`pr-followup-helpers.md`](../_shared/pr-followup-helpers.md) — TOC of allow-list / reply-routing / classify; per-section files in `_shared/pr-followup-helpers/`. Called by `/muggle-do`, not by this folder.
22
22
  - [`telemetry-emit.md`](../_shared/telemetry-emit.md) — how to emit a telemetry event.
23
23
  - [`telemetry-events.md`](../_shared/telemetry-events.md) — TOC of canonical event shapes; per-event files in `_shared/telemetry-events/`.
24
- - [`github-cli-recipes.md`](../_shared/github-cli-recipes.md) — TOC of reusable `gh` / `git` snippets; per-recipe files in `_shared/github-cli-recipes/`.
24
+ - [`vcs/github.md`](../_shared/vcs/github.md) — TOC of reusable `gh` / `git` snippets; per-recipe files in `_shared/vcs/github/`.
25
25
 
26
26
  Callers (e.g. `/muggle-do`) reference this folder, not the reverse — see [`../CLAUDE.md`](../CLAUDE.md) for the one-way-dependency rule.