@muggleai/works 5.0.3 → 5.1.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/README.md +2 -3
- package/dist/{chunk-XO5BQZM3.js → chunk-2BHC6TQR.js} +3 -4
- package/dist/{chunk-KDRSEHK5.js → chunk-MOHR7QUN.js} +197 -291
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/scripts/ensure-electron-app.sh +1 -1
- package/dist/plugin/scripts/guardrails.mjs +4 -2
- package/dist/plugin/skills/_shared/debug-failed-run.md +53 -0
- package/dist/plugin/skills/_shared/dev-loop/publish.md +4 -8
- package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +1 -1
- package/dist/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +1 -1
- package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -1
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +1 -1
- package/dist/plugin/skills/_shared/test-case-chain-readiness.md +1 -2
- package/dist/plugin/skills/_shared/vcs/CLAUDE.md +9 -0
- package/dist/plugin/skills/_shared/vcs/detect-vcs.md +28 -0
- package/dist/plugin/skills/_shared/vcs/github/pr-metadata.md +25 -0
- package/{plugin/skills/_shared/github-cli-recipes → dist/plugin/skills/_shared/vcs/github}/unresolved-threads.md +1 -1
- package/{plugin/skills/_shared/github-cli-recipes → dist/plugin/skills/_shared/vcs/github}/verify-working-tree.md +1 -1
- package/dist/plugin/skills/_shared/vcs/github.md +21 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +9 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +11 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +18 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +8 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +22 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +7 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +27 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +9 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +10 -0
- package/dist/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +18 -0
- package/dist/plugin/skills/_shared/vcs/gitlab.md +24 -0
- package/dist/plugin/skills/do/address-reviews.md +10 -6
- package/dist/plugin/skills/do/e2e-acceptance.md +6 -4
- package/dist/plugin/skills/do/fix-ci.md +1 -1
- package/dist/plugin/skills/do/input-routing.md +4 -4
- package/dist/plugin/skills/do/open-prs/forward.md +6 -4
- package/dist/plugin/skills/do/open-prs/update.md +8 -6
- package/dist/plugin/skills/do/per-comment-replies.md +22 -13
- package/dist/plugin/skills/do/resolve-conflicts.md +1 -1
- package/dist/plugin/skills/do/resolve-reminder.md +8 -6
- package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-do/SKILL.md +2 -2
- package/dist/plugin/skills/muggle-feedback/ops/submit.md +4 -4
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -2
- package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +13 -11
- package/dist/plugin/skills/muggle-pr-followup/contract.md +18 -12
- package/dist/plugin/skills/muggle-pr-followup/finalize.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/reconcile.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +4 -1
- package/dist/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +13 -13
- package/dist/plugin/skills/muggle-preferences/ops/configure.md +2 -2
- package/dist/plugin/skills/muggle-test/SKILL.md +30 -143
- package/dist/plugin/skills/muggle-test/execute-local.md +60 -0
- package/dist/plugin/skills/muggle-test/execute-remote.md +49 -0
- package/dist/plugin/skills/muggle-test/reference.md +1 -2
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +9 -18
- package/dist/release-manifest.json +4 -4
- package/dist/{src-2O5UH4FA.js → src-SS2AL3O7.js} +1 -1
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/scripts/ensure-electron-app.sh +1 -1
- package/plugin/scripts/guardrails.mjs +4 -2
- package/plugin/skills/_shared/debug-failed-run.md +53 -0
- package/plugin/skills/_shared/dev-loop/publish.md +4 -8
- package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +1 -1
- package/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +1 -1
- package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -1
- package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +1 -1
- package/plugin/skills/_shared/test-case-chain-readiness.md +1 -2
- package/plugin/skills/_shared/vcs/CLAUDE.md +9 -0
- package/plugin/skills/_shared/vcs/detect-vcs.md +28 -0
- package/plugin/skills/_shared/vcs/github/pr-metadata.md +25 -0
- package/{dist/plugin/skills/_shared/github-cli-recipes → plugin/skills/_shared/vcs/github}/unresolved-threads.md +1 -1
- package/{dist/plugin/skills/_shared/github-cli-recipes → plugin/skills/_shared/vcs/github}/verify-working-tree.md +1 -1
- package/plugin/skills/_shared/vcs/github.md +21 -0
- package/plugin/skills/_shared/vcs/gitlab/loop-user-identity.md +9 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-create.md +11 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-discussions.md +18 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +8 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-metadata.md +22 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-note.md +7 -0
- package/plugin/skills/_shared/vcs/gitlab/mr-pipeline.md +27 -0
- package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +9 -0
- package/plugin/skills/_shared/vcs/gitlab/resolve-discussion.md +10 -0
- package/plugin/skills/_shared/vcs/gitlab/unresolved-discussions.md +18 -0
- package/plugin/skills/_shared/vcs/gitlab.md +24 -0
- package/plugin/skills/do/address-reviews.md +10 -6
- package/plugin/skills/do/e2e-acceptance.md +6 -4
- package/plugin/skills/do/fix-ci.md +1 -1
- package/plugin/skills/do/input-routing.md +4 -4
- package/plugin/skills/do/open-prs/forward.md +6 -4
- package/plugin/skills/do/open-prs/update.md +8 -6
- package/plugin/skills/do/per-comment-replies.md +22 -13
- package/plugin/skills/do/resolve-conflicts.md +1 -1
- package/plugin/skills/do/resolve-reminder.md +8 -6
- package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/plugin/skills/muggle-do/SKILL.md +2 -2
- package/plugin/skills/muggle-feedback/ops/submit.md +4 -4
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -2
- package/plugin/skills/muggle-pr-followup/bootstrap.md +13 -11
- package/plugin/skills/muggle-pr-followup/contract.md +18 -12
- package/plugin/skills/muggle-pr-followup/finalize.md +1 -1
- package/plugin/skills/muggle-pr-followup/reconcile.md +1 -1
- package/plugin/skills/muggle-pr-followup/state-schemas.md +4 -1
- package/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +13 -13
- package/plugin/skills/muggle-preferences/ops/configure.md +2 -2
- package/plugin/skills/muggle-test/SKILL.md +30 -143
- package/plugin/skills/muggle-test/execute-local.md +60 -0
- package/plugin/skills/muggle-test/execute-remote.md +49 -0
- package/plugin/skills/muggle-test/reference.md +1 -2
- package/plugin/skills/muggle-test-feature-local/SKILL.md +9 -18
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +0 -13
- package/dist/plugin/skills/_shared/github-cli-recipes.md +0 -21
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +0 -11
- package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +0 -13
- package/plugin/skills/_shared/github-cli-recipes.md +0 -21
- package/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +0 -11
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/line-comments-for-review.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/loop-user-identity.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-checks.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-edit.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/push-to-branch.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/reply-line-comment.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/submitted-reviews.md +0 -0
- /package/dist/plugin/skills/_shared/{github-cli-recipes → vcs/github}/top-level-comment.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/line-comments-for-review.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/loop-user-identity.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-checks.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/pr-edit.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/push-to-branch.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/reply-line-comment.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/submitted-reviews.md +0 -0
- /package/plugin/skills/_shared/{github-cli-recipes → vcs/github}/top-level-comment.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Run real-browser end-to-end (E2E) acceptance tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
|
|
4
|
-
"version": "5.0
|
|
4
|
+
"version": "5.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Ship quality products with AI-powered end-to-end (E2E) acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
5
|
-
"version": "5.0
|
|
5
|
+
"version": "5.1.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
|
@@ -104,7 +104,7 @@ if [ -f "$prefs_global_file" ]; then
|
|
|
104
104
|
const defaults = {
|
|
105
105
|
autoLogin:'ask', autoSelectProject:'ask', autoSelectLocalHost:'ask',
|
|
106
106
|
showElectronBrowser:'ask', openTestResultsAfterRun:'ask',
|
|
107
|
-
defaultExecutionMode:'ask',
|
|
107
|
+
defaultExecutionMode:'ask',
|
|
108
108
|
suggestRelatedUseCases:'ask', suggestRelatedTestCases:'ask', autoDetectChanges:'ask',
|
|
109
109
|
postPRVisualWalkthrough:'ask', autoCreatePR:'ask',
|
|
110
110
|
checkForUpdates:'ask', verboseOutput:'ask',
|
|
@@ -27,14 +27,16 @@ function markPrHandled(sessionId2, prUrl, dirOverride) {
|
|
|
27
27
|
|
|
28
28
|
// src/guardrails/prOpened.ts
|
|
29
29
|
var PR_URL = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+/;
|
|
30
|
+
var MR_URL = /https?:\/\/[^/\s]+\/[^\s]+\/-\/merge_requests\/\d+/;
|
|
30
31
|
var CREATE_CMD = /\bgh\s+pr\s+(create|ready)\b/;
|
|
32
|
+
var MR_CREATE_CMD = /\bglab\s+mr\s+create\b|\bglab\s+mr\s+update\b.*--ready\b/;
|
|
31
33
|
function detectPrOpened(input2) {
|
|
32
34
|
if (input2.tool_name !== "Bash") return null;
|
|
33
35
|
const cmd = input2.tool_input?.command ?? "";
|
|
34
|
-
if (!CREATE_CMD.test(cmd)) return null;
|
|
36
|
+
if (!CREATE_CMD.test(cmd) && !MR_CREATE_CMD.test(cmd)) return null;
|
|
35
37
|
const out = `${input2.tool_response?.stdout ?? ""}
|
|
36
38
|
${input2.tool_response?.output ?? ""}`;
|
|
37
|
-
const m = out.match(PR_URL);
|
|
39
|
+
const m = out.match(PR_URL) ?? out.match(MR_URL);
|
|
38
40
|
return m ? m[0] : null;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Debug a Failed Run — Shared Reference
|
|
2
|
+
|
|
3
|
+
> Presentation layer for a single failed run. Turns one non-passing terminal run — local or remote — into a debug path: **evidence → diagnosis → a guaranteed user choice that always offers "give feedback & rerun".** Used by `muggle-test` (Step 7C), `muggle-test-feature-local`, and the `muggle-do` e2e stage. Depends on [`failure-mode-handling.md`](./failure-mode-handling.md) for the bucket taxonomy and telemetry schema — never the reverse. Don't restate its tables; read them.
|
|
4
|
+
|
|
5
|
+
## The guarantee
|
|
6
|
+
|
|
7
|
+
Every non-passing terminal run MUST route through this doc before it is reported. A failure is never summarized-and-dropped: the user is always shown *what happened* and is always presented a selection in which **"give feedback & rerun" is a first-class option**. "Skip — just report" stays selectable but is never the default.
|
|
8
|
+
|
|
9
|
+
The guarantee binds **interactive** callers. An autonomous caller with no user to prompt (the `acceptance-tester` agent) runs Steps 1–2 (evidence + diagnosis into its structured report) and skips Step 3's interactive offer.
|
|
10
|
+
|
|
11
|
+
## Inputs (the caller passes these in)
|
|
12
|
+
|
|
13
|
+
- `runId` (local) or workflow runtime id (remote).
|
|
14
|
+
- `mode` — `replay` or `regen`, the mode that failed.
|
|
15
|
+
- `testCaseId`, `projectId`.
|
|
16
|
+
- A re-execute handle — the loop/tool the caller used to run this case, so a rerun re-enters the same execution path.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Gather evidence
|
|
19
|
+
|
|
20
|
+
Read **structured run fields**, never the `execute` stdout tail (see [`dev-loop/failures.md`](./dev-loop/failures.md)). Assemble:
|
|
21
|
+
|
|
22
|
+
- **Attempted steps + reasoning** — local: the attempted steps + `summaryStep` halt reason from `action-script.json` in `artifactsDir`; remote: the per-step list + `summaryStep` from `muggle-remote-wf-get-ts-gen-latest-run` / `muggle-remote-wf-get-ts-replay-latest-run`.
|
|
23
|
+
- **Visual evidence** — a failed run already preserves the full step-by-step on disk: every per-step frame under `<artifactsDir>/electron-runtime/screenshot/` (with per-step label data under `.../dataset/`), alongside the step script at `<artifactsDir>/action-script.json`. `run-result-get` returns `artifactsDir` — read the whole set there; don't trust a step's `screenshotLocalPath`, which points at the original runtime dir. Once published — failed runs are published too, see [`dev-loop/publish.md`](./dev-loop/publish.md) — the same frames are cloud-hosted per step as `screenshotUrl`, the form remote runs expose directly.
|
|
24
|
+
- **Verdict** — `Status` + `Error`.
|
|
25
|
+
|
|
26
|
+
## Step 2 — Diagnose
|
|
27
|
+
|
|
28
|
+
Classify into the failure bucket per [`failure-mode-handling.md`](./failure-mode-handling.md) — §B for a replay failure, §C for a regen failure. That bucket **is** the initial diagnosis. Phrase it for a human ("Looks like a **stale script** — the selectors moved; the product itself probably still works"), not as a telemetry label. Emit the `replay-failure-classified` / `regen-failure-classified` event now, before presenting anything.
|
|
29
|
+
|
|
30
|
+
## Step 3 — Present the debug card, then the guaranteed offer
|
|
31
|
+
|
|
32
|
+
Show the **debug card** first: attempted steps + reasoning, the failing step's screenshot (or a one-line note if a path is genuinely absent), and the one-line diagnosis.
|
|
33
|
+
|
|
34
|
+
Then present one `AskUserQuestion` whose options are:
|
|
35
|
+
|
|
36
|
+
1. **Give feedback & rerun** — always present. Invoke the `muggle-feedback` skill with this run's anchor (`runId` local / `testScriptId` remote) so the user says what should have happened, then re-execute per Step 4.
|
|
37
|
+
2. **The bucket's recommended action** from §B/§C (regenerate, report bug, share defect, retry…). Label it `(Recommended)` — it is the classifier's pick.
|
|
38
|
+
3. **Retry as-is**.
|
|
39
|
+
4. **Skip — just report** — last, never the default.
|
|
40
|
+
|
|
41
|
+
The bucket's recommended action and its alternatives live in `failure-mode-handling.md` §B/§C — read them there, don't restate them.
|
|
42
|
+
|
|
43
|
+
**Feedback anchor by lane.** Feedback attaches to a cloud action-script id. `muggle-feedback` owns resolving it: for a **local** run it publishes/uploads first when the run isn't already published; for a **remote** run it uses the existing script. Pass `runId` (local) or `testScriptId` (remote) and let it resolve the anchor — don't resolve or upload here.
|
|
44
|
+
|
|
45
|
+
## Step 4 — Rerun (always regen)
|
|
46
|
+
|
|
47
|
+
On "give feedback & rerun": once `muggle-feedback` returns, re-execute the test case in **regen** mode — feedback adjusts the agent's course, so the prior script is discarded; replay is never the rerun path. Local: the regen branch of [`dev-loop/run.md`](./dev-loop/run.md). Remote: `muggle-remote-workflow-start-test-script-generation`. Read the new run's result back through this doc (Step 1) so a second failure is debugged too.
|
|
48
|
+
|
|
49
|
+
## Step 5 — Resolve
|
|
50
|
+
|
|
51
|
+
Emit the `replay-failure-resolved` / `regen-failure-resolved` event with `userAction` set to the pick (`feedback-rerun`, the bucket action, `retry`, or `skip`) — one `*-classified` and one `*-resolved` per failure, per [`failure-mode-handling.md`](./failure-mode-handling.md) §D.
|
|
52
|
+
|
|
53
|
+
Process failures one at a time so the user isn't drowning in pickers.
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
# Dev Loop —
|
|
1
|
+
# Dev Loop — Cloud Refs & Screenshots
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Cloud refs
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
`muggle-local-publish-test-script` with `runId` and `cloudTestCaseId`. Retain `testScriptId`, `actionScriptId`, `viewUrl`. Batch publishes in parallel.
|
|
8
|
-
|
|
9
|
-
If publish rejects with `has no generated actionScript steps to publish` (a true zero-step run), fall back to `muggle-remote-local-run-upload` with whatever exists (`summaryStep`, `errorMessage`, empty `actionScript`); capture `actionScriptId` and `viewUrl` the same way.
|
|
5
|
+
The studio publishes every completed run — pass or fail — to the cloud during execution. There is no publish step to run. The run result already carries the cloud identifiers: read `viewUrl`, `cloudTestScriptId`, and `cloudActionScriptId` from `muggle-local-run-result-get` for the run. (`cloudTestScriptId` is absent for a failed generation — the backend records the action script and `viewUrl` but no test script.)
|
|
10
6
|
|
|
11
7
|
## Screenshots
|
|
12
8
|
|
|
13
|
-
Per
|
|
9
|
+
Per run, `muggle-remote-test-script-get` with the `cloudTestScriptId` from the run result → per step `operation.screenshotUrl` and `operation.action`. Retain `{ stepIndex, action, screenshotUrl }`. Batch in parallel. These feed the `E2eReport` consumed by [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) — the dev loop does not post to the PR.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The address-reviews flow only acts on reviews submitted by users in the **allow-list** = (requested reviewers ∪ CODEOWNERS ∪ {PR author}) − bots. Re-resolve every invocation — never cache across cycles.
|
|
4
4
|
|
|
5
|
-
The PR author is implicitly a valid reviewer: in single-account workflows, the human running the agent and the PR's author are the same identity, and the agent must honor their reviews. Self-loop is prevented at the watcher's filter layer rather than here: `POST /pulls/<n>/comments/<id>/replies` does create an implicit review under the loop user's identity, and the watcher drops it via the loop-echo clause in [`../github
|
|
5
|
+
The PR author is implicitly a valid reviewer: in single-account workflows, the human running the agent and the PR's author are the same identity, and the agent must honor their reviews. Self-loop is prevented at the watcher's filter layer rather than here: `POST /pulls/<n>/comments/<id>/replies` does create an implicit review under the loop user's identity, and the watcher drops it via the loop-echo clause in [`../vcs/github/submitted-reviews.md`](../vcs/github/submitted-reviews.md) — matched by the loop signature ([`loop-signature.md`](loop-signature.md)), so the author's *genuine* reviews and thread replies still get through. Including the PR author in this allow-list is therefore safe.
|
|
6
6
|
|
|
7
7
|
## Step 1: requested reviewers
|
|
8
8
|
|
|
@@ -5,6 +5,6 @@ When `/muggle-do` posts a threaded reply to a review comment, GitHub surfaces th
|
|
|
5
5
|
Under the thread-state dispatch trigger this is **intrinsic** — there is no "advance past the echo" step to get wrong:
|
|
6
6
|
|
|
7
7
|
- **Line-comment threads.** A thread is actionable only when its newest comment lacks the loop marker `<!-- muggle-do:bot -->` (see [`loop-signature.md`](loop-signature.md)). After the loop replies, the newest comment is the loop's own and carries the marker, so the thread drops out of the actionable set on its own.
|
|
8
|
-
- **Body-only reviews.** A loop reply always carries a line comment, so it is never body-only; the body-only check (no line comments, `id > lastBodyReviewId` — see [`../github
|
|
8
|
+
- **Body-only reviews.** A loop reply always carries a line comment, so it is never body-only; the body-only check (no line comments, `id > lastBodyReviewId` — see [`../vcs/github/submitted-reviews.md`](../vcs/github/submitted-reviews.md)) excludes echoes structurally.
|
|
9
9
|
|
|
10
10
|
Classify by the marker, never by `author.login` — under a shared account the loop posts as the PR author, so the login cannot tell echo from human.
|
|
@@ -53,4 +53,4 @@ Write to `state.md` under a `## Pre-flight answers` block:
|
|
|
53
53
|
- `Auth status: <ok | re-authed | N/A>`
|
|
54
54
|
- `Working tree: <path>` — the verified checkout the cycle runs against
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Once the strategy is `local-e2e`, missing any of its required fields (`Local URL`, `Muggle Test project`) is a seeding bug — escalate with the session path and halt; don't silently downgrade a chosen `local-e2e` to a skip. But `unit-only` and `skip` are valid first-class strategies for a PR with no testable surface — they need none of those fields, and a session seeded with no block at all is itself a clean skip.
|
|
@@ -23,7 +23,7 @@ One per watcher iteration (idle or not).
|
|
|
23
23
|
|
|
24
24
|
- `actionable_threads`: count of actionable items this tick — unresolved, non-outdated threads whose newest comment is unmarked, plus body-only reviews past `lastBodyReviewId` — **after** filtering by the escalated set.
|
|
25
25
|
- `dispatched_review_ids`: owning review ids handed to `/muggle-do`. Empty when idle.
|
|
26
|
-
- `rebase_needed`: true when the branch is behind (`
|
|
26
|
+
- `rebase_needed`: true when the branch is behind its base (`behind_by > 0`) or conflicting (`mergeable == CONFLICTING`). `false` when reviews were dispatched (reviews preempt the mergeability check).
|
|
27
27
|
- `dispatched_rebase`: true when this tick dispatched `/muggle-do` with a rebase directive.
|
|
28
28
|
- `checks_red`: count of failing checks on the head SHA. `0` when reviews or a rebase were dispatched (both preempt the CI poll) or CI was green/pending.
|
|
29
29
|
- `dispatched_ci_fix`: true when this tick dispatched `/muggle-do` with a fix-ci directive.
|
|
@@ -19,8 +19,7 @@ Run once the target `testCaseId` is chosen and the local URL + services are conf
|
|
|
19
19
|
- Not ready → **generate its script only (never replay):**
|
|
20
20
|
1. `muggle-remote-test-case-get` for the ancestor.
|
|
21
21
|
2. Determine `freshSession` for that ancestor from its own content — same rules as Step 6.
|
|
22
|
-
3. `muggle-local-execute-test-generation` with the ancestor test case, `localUrl`, `cwd`, and a long `timeoutMs` (see Step 6's timeout guidance). Do **not** call `muggle-local-execute-replay`.
|
|
23
|
-
4. `muggle-local-publish-test-script` (`runId`, `cloudTestCaseId` = ancestor) so the generated script is promoted as that ancestor's canonical replay script — it now reads as ready for any case downstream.
|
|
22
|
+
3. `muggle-local-execute-test-generation` with the ancestor test case, `localUrl`, `cwd`, and a long `timeoutMs` (see Step 6's timeout guidance). Do **not** call `muggle-local-execute-replay`. The studio publishes the generated run during execution, promoting its script as that ancestor's canonical replay script — it now reads as ready for any case downstream (confirm via `muggle-local-run-result-get` carrying a `cloudActionScriptId`).
|
|
24
23
|
|
|
25
24
|
3. **All ancestors ready** → continue to Step 5 for the target test case.
|
|
26
25
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# VCS provider recipes
|
|
2
|
+
|
|
3
|
+
Provider-agnostic seam for the watcher and dev-cycle skills. A caller resolves one provider token via [`detect-vcs.md`](detect-vcs.md) — `github` (`gh`) or `gitlab` (`glab`) — then loads the matching provider's recipe for each agnostic step. The two recipe sets mirror each other call-for-call; callers branch on the token, never fork procedure.
|
|
4
|
+
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
- [`detect-vcs.md`](detect-vcs.md) — resolve `github` | `gitlab` from a URL argument or the origin remote.
|
|
8
|
+
- [`github.md`](github.md) — `gh` / `git` recipe TOC; per-recipe files in [`github/`](github/).
|
|
9
|
+
- [`gitlab.md`](gitlab.md) — `glab` / `git` recipe TOC; per-recipe files in [`gitlab/`](gitlab/).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Detect VCS provider
|
|
2
|
+
|
|
3
|
+
Resolve a single provider token — `github` or `gitlab` — before a caller picks its recipe set. GitHub uses `gh`; GitLab uses `glab`. Auth errors from either surface verbatim.
|
|
4
|
+
|
|
5
|
+
## Resolution order
|
|
6
|
+
|
|
7
|
+
1. **From a URL argument**, if one was passed:
|
|
8
|
+
- `github.com/<owner>/<repo>/pull/<n>` → `github`.
|
|
9
|
+
- `<host>/<group>/<project>/-/merge_requests/<iid>` → `gitlab`. The `/-/merge_requests/` segment is the tell, on any host.
|
|
10
|
+
|
|
11
|
+
2. **From the repo**, when no URL — parse the origin remote:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git remote get-url origin
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- Host `github.com` → `github`.
|
|
18
|
+
- Host `gitlab.com`, **or** any other host where `glab auth status` succeeds → `gitlab`.
|
|
19
|
+
|
|
20
|
+
## GitLab wrinkles
|
|
21
|
+
|
|
22
|
+
- **Nested namespaces.** The path between host and `/-/` can be `group/subgroup/project`, any depth. Do not assume two segments — split on `/-/`, the project path is everything before it.
|
|
23
|
+
- **The `/-/` segment** separates the project path from the resource (`/-/merge_requests/<iid>`). GitHub has no equivalent; its `/pull/<n>` sits directly under `<owner>/<repo>`.
|
|
24
|
+
- **Self-hosted hosts.** GitLab is not just `gitlab.com` — `git.acme.com`, `gitlab.internal`, etc. Resolve the host from the remote and confirm it against `glab`'s configured host (`GITLAB_HOST`, or `glab auth status`'s active host). A remote host that matches `glab`'s host → `gitlab`, even when unknown to this doc.
|
|
25
|
+
|
|
26
|
+
## Output
|
|
27
|
+
|
|
28
|
+
The bare token `github` or `gitlab` — nothing else.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# PR metadata snapshot
|
|
2
|
+
|
|
3
|
+
Fetch the fields the watcher and bootstrap need.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
gh pr view <pr-number> --repo <owner>/<repo> \
|
|
7
|
+
--json url,number,headRefOid,headRefName,baseRefName,state,mergeable,mergeStateStatus,mergedAt,closedAt,body,title,author
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- `state` is one of `OPEN`, `MERGED`, `CLOSED`.
|
|
11
|
+
- `headRefOid` is the current head SHA — store as `head_sha` in `prs.json`.
|
|
12
|
+
- `headRefName` is the branch — must match the working tree's branch in bootstrap.
|
|
13
|
+
- `mergeable` is `MERGEABLE`, `CONFLICTING`, or `UNKNOWN` (GitHub still computing — treat as not-conflicting this tick). The watcher's **conflict** signal is `mergeable == CONFLICTING` (corroborated by `mergeStateStatus == DIRTY`).
|
|
14
|
+
|
|
15
|
+
## Behind-by (out-of-date detection)
|
|
16
|
+
|
|
17
|
+
`mergeStateStatus == BEHIND` is **not** a reliable out-of-date signal. GitHub collapses merge state into one value with precedence — `DIRTY` (conflict) and `BLOCKED` (missing required review, pending/failing required check) outrank `BEHIND` and mask it, and `BEHIND` surfaces *at all* only when the base enforces "require branches up to date." So a PR that is genuinely behind **and** awaiting review reports `BLOCKED`; `BEHIND` never shows, and its staleness goes unseen.
|
|
18
|
+
|
|
19
|
+
Detect out-of-date straight from commit ancestry instead — independent of merge-state precedence, review state, and branch protection:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gh api repos/<owner>/<repo>/compare/<baseRefName>...<head_sha> --jq '.behind_by'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`behind_by > 0` ⇒ the head is missing that many base commits ⇒ out of date. `0` ⇒ current with base. (`ahead_by` counts the head's own commits — ignore it.) This is the watcher's out-of-date trigger; it is exact even while `mergeable == UNKNOWN`.
|
|
@@ -28,7 +28,7 @@ query($owner: String!, $name: String!, $number: Int!) {
|
|
|
28
28
|
}'
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Filter client-side to `isResolved == false`. Walk each thread's comments in `createdAt` order and classify by the loop marker (see [
|
|
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
32
|
|
|
33
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
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. The **watcher's dispatch trigger** additionally requires `isOutdated == false` — a thread whose anchored line has since changed is skipped, since re-surfacing a stale anchor risks answering a concern the change already mooted. The resolve-reminder stage ignores `isOutdated`.
|
|
@@ -14,4 +14,4 @@ Accept any remote URL form for `<owner>/<repo>` (with or without trailing `.git`
|
|
|
14
14
|
- `git@github.com:<owner>/<repo>`
|
|
15
15
|
- `ssh://git@github.com/<owner>/<repo>`
|
|
16
16
|
|
|
17
|
-
Any mismatch → bootstrap aborts using the wrong-checkout template in [
|
|
17
|
+
Any mismatch → bootstrap aborts using the wrong-checkout template in [`../../../muggle-pr-followup/output-templates/bootstrap.md`](../../../muggle-pr-followup/output-templates/bootstrap.md).
|
|
@@ -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,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
|
-
|
|
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
|
|
41
|
-
- Fetch its line comments per [`../_shared/github
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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),
|
|
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
|
|
|
@@ -145,6 +145,8 @@ For each test case:
|
|
|
145
145
|
|
|
146
146
|
**Overall:** PASS | FAIL | PARTIAL | INCONCLUSIVE | BLOCKED | SKIPPED — see [`../_shared/failure-mode-handling.md`](../_shared/failure-mode-handling.md) section F for the canonical taxonomy.
|
|
147
147
|
|
|
148
|
+
Failed runs use the same evidence + diagnosis assembly as the interactive debug path ([`../_shared/debug-failed-run.md`](../_shared/debug-failed-run.md) Steps 1–2) — write that evidence into the **Failed** block above. This stage is autonomous, so it skips Step 3's interactive offer.
|
|
149
|
+
|
|
148
150
|
## Hard constraints
|
|
149
151
|
|
|
150
152
|
- **Do NOT shut down the dev server.** The caller manages dev-server lifecycle.
|
|
@@ -159,5 +161,5 @@ For each test case:
|
|
|
159
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.
|
|
160
162
|
- No hiding failures: surface errors, exit codes, and artifact paths.
|
|
161
163
|
- In `local-e2e` mode, every relevant test case must be executed — generate a new script if none exists (no skips).
|
|
162
|
-
-
|
|
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.
|
|
163
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>`
|
|
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
|
|
6
|
-
2. **Fix-CI** — a
|
|
7
|
-
3. **Rebase** — a
|
|
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`.
|