@muggleai/works 5.0.2 → 5.0.4
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 +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/skills/_shared/debug-failed-run.md +53 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +14 -2
- package/dist/plugin/skills/_shared/github-cli-recipes.md +1 -1
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +4 -2
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +5 -1
- package/dist/plugin/skills/do/e2e-acceptance.md +2 -0
- package/dist/plugin/skills/do/input-routing.md +1 -1
- package/dist/plugin/skills/do/resolve-conflicts.md +21 -19
- package/dist/plugin/skills/muggle-do/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/contract.md +20 -12
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +2 -2
- package/dist/plugin/skills/muggle-test/SKILL.md +10 -20
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +4 -13
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +3 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +13 -0
- package/dist/release-manifest.json +4 -4
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/skills/_shared/debug-failed-run.md +53 -0
- package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +14 -2
- package/plugin/skills/_shared/github-cli-recipes.md +1 -1
- package/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +4 -2
- package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +5 -1
- package/plugin/skills/do/e2e-acceptance.md +2 -0
- package/plugin/skills/do/input-routing.md +1 -1
- package/plugin/skills/do/resolve-conflicts.md +21 -19
- package/plugin/skills/muggle-do/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -1
- package/plugin/skills/muggle-pr-followup/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/contract.md +20 -12
- package/plugin/skills/muggle-pr-followup/state-schemas.md +2 -2
- package/plugin/skills/muggle-test/SKILL.md +10 -20
- package/plugin/skills/muggle-test-feature-local/SKILL.md +4 -13
- package/plugin/skills/muggle-test-prepare/SKILL.md +3 -0
- package/plugin/skills/muggle-test-prepare/steps/check-running.md +13 -0
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Your AI assistant writes code fast. But does the feature actually work? Does the
|
|
|
19
19
|
|
|
20
20
|
muggle-ai-works closes the gap between "code complete" and "actually works."
|
|
21
21
|
|
|
22
|
-
- **
|
|
22
|
+
- **You create, we verify** — `/muggle:muggle-test` AI drives a real browser against your localhost across desktop and mobile resolutions, clicks through flows like a user would, and reports failures with step-by-step screenshots. No Playwright scripts to maintain.
|
|
23
23
|
- **Go from requirement to merged PR in one command** — `/muggle:muggle-do` handles the full cycle: code the feature, run unit tests, run E2E acceptance tests against the app in a real browser at multiple viewports, triage failures, and open a PR with evidence attached.
|
|
24
24
|
- **70+ MCP tools for custom workflows** — manage projects, generate test cases from plain English, replay test scripts, batch-run regressions, and publish results to your team. Works in Claude Code, Cursor, and any MCP client.
|
|
25
25
|
|
|
@@ -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.0.4",
|
|
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.0.4",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
# PR metadata snapshot
|
|
2
2
|
|
|
3
|
-
Fetch the fields the watcher and bootstrap need
|
|
3
|
+
Fetch the fields the watcher and bootstrap need.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
gh pr view <pr-number> --repo <owner>/<repo> \
|
|
@@ -10,4 +10,16 @@ gh pr view <pr-number> --repo <owner>/<repo> \
|
|
|
10
10
|
- `state` is one of `OPEN`, `MERGED`, `CLOSED`.
|
|
11
11
|
- `headRefOid` is the current head SHA — store as `head_sha` in `prs.json`.
|
|
12
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).
|
|
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`.
|
|
@@ -8,7 +8,7 @@ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`
|
|
|
8
8
|
|
|
9
9
|
| Recipe | Use case |
|
|
10
10
|
| :----- | :------- |
|
|
11
|
-
| [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch — watcher + bootstrap. |
|
|
11
|
+
| [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch, conflict + `behind_by` out-of-date detection — watcher + bootstrap. |
|
|
12
12
|
| [`submitted-reviews`](github-cli-recipes/submitted-reviews.md) | Fetch a review by id / watcher's body-only-review check. |
|
|
13
13
|
| [`pr-checks`](github-cli-recipes/pr-checks.md) | Check-run rollup for the head SHA — watcher's CI poll. |
|
|
14
14
|
| [`line-comments-for-review`](github-cli-recipes/line-comments-for-review.md) | Pull a review's line comments — `/muggle-do` per-comment routing. |
|
|
@@ -17,7 +17,7 @@ One per address-reviews invocation, regardless of outcome.
|
|
|
17
17
|
"ci_checks_escalated": ["<check-name>", ...],
|
|
18
18
|
"head_sha_before": "<sha-or-null>",
|
|
19
19
|
"head_sha_after": "<sha-or-null>",
|
|
20
|
-
"outcome": "pushed" | "escalated" | "mixed" | "no-op" | "self-loop-skip" | "ci-fixed" | "ci-escalated"
|
|
20
|
+
"outcome": "pushed" | "escalated" | "mixed" | "no-op" | "self-loop-skip" | "ci-fixed" | "ci-escalated" | "rebased" | "rebase-escalated"
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -29,5 +29,7 @@ One per address-reviews invocation, regardless of outcome.
|
|
|
29
29
|
- `"self-loop-skip"` — review was a synthetic wrapper around the agent's own reply (every line comment is a reply carrying the loop marker `<!-- muggle-do:bot -->`). Cursor advanced silently; no work, no escalation.
|
|
30
30
|
- `"ci-fixed"` — a watcher-dispatched fix-ci cycle pushed a fix for one or more red checks.
|
|
31
31
|
- `"ci-escalated"` — fix-ci exhausted its 3 attempts for the SHA or the failing checks were out of scope; the SHA was added to `ci_escalated_shas`. No further auto-fix on it.
|
|
32
|
+
- `"rebased"` — a watcher-dispatched rebase cycle rebased the branch onto its base (behind-only or conflicts resolved), verified, and force-pushed.
|
|
33
|
+
- `"rebase-escalated"` — the rebase couldn't be completed (a conflict under `autoResolveConflicts=never`, verification failed, or the 2-attempt budget for the SHA was spent); the SHA was added to `conflict_escalated_shas`. No further auto-rebase on it.
|
|
32
34
|
|
|
33
|
-
For fix-ci cycles (`ci-fixed` / `ci-escalated`) the `review_ids_*` arrays are empty and the `ci_checks_*` arrays carry the data: `ci_checks_in` (red checks dispatched), `ci_checks_fixed` (made green and pushed), `ci_checks_escalated` (out-of-scope or unresolved).
|
|
35
|
+
For fix-ci cycles (`ci-fixed` / `ci-escalated`) the `review_ids_*` arrays are empty and the `ci_checks_*` arrays carry the data: `ci_checks_in` (red checks dispatched), `ci_checks_fixed` (made green and pushed), `ci_checks_escalated` (out-of-scope or unresolved). For rebase cycles (`rebased` / `rebase-escalated`) all the `review_ids_*` and `ci_checks_*` arrays are empty; the SHA fields carry the before/after of the rebase.
|
|
@@ -11,6 +11,8 @@ One per watcher iteration (idle or not).
|
|
|
11
11
|
"pr_number": <int>,
|
|
12
12
|
"actionable_threads": <int>,
|
|
13
13
|
"dispatched_review_ids": [<int>, ...],
|
|
14
|
+
"rebase_needed": true | false,
|
|
15
|
+
"dispatched_rebase": true | false,
|
|
14
16
|
"checks_red": <int>,
|
|
15
17
|
"dispatched_ci_fix": true | false,
|
|
16
18
|
"terminal": true | false,
|
|
@@ -21,7 +23,9 @@ One per watcher iteration (idle or not).
|
|
|
21
23
|
|
|
22
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.
|
|
23
25
|
- `dispatched_review_ids`: owning review ids handed to `/muggle-do`. Empty when idle.
|
|
24
|
-
- `
|
|
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
|
+
- `dispatched_rebase`: true when this tick dispatched `/muggle-do` with a rebase directive.
|
|
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.
|
|
25
29
|
- `dispatched_ci_fix`: true when this tick dispatched `/muggle-do` with a fix-ci directive.
|
|
26
30
|
- `terminal`: true when this tick observed the PR merged or closed and wrote `result.md`.
|
|
27
31
|
- `idle`: true when nothing was dispatched this tick.
|
|
@@ -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.
|
|
@@ -4,7 +4,7 @@ How `/muggle-do` resolves `$ARGUMENTS` to a mode. Modes 1–4 are programmatic
|
|
|
4
4
|
|
|
5
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
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. **
|
|
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.
|
|
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,16 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Rebase (watcher-dispatched)
|
|
2
2
|
|
|
3
|
-
Rebase a PR
|
|
3
|
+
Rebase a PR's branch onto its base — whether it's merely **behind** (out of date, no conflict) or actually **conflicting** — behind a verify-or-rollback gate, then force-push, so a PR doesn't sit stale or un-mergeable forever. A dumb-pipe dispatch like fix-ci: the watcher detects the branch is behind or conflicting and hands off; the executor owns the rebase (and any conflict resolution), never the decision to dispatch.
|
|
4
4
|
|
|
5
5
|
## Turn preamble
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
**/muggle-do
|
|
8
|
+
**/muggle-do rebase** — rebasing <owner>/<repo>#<n> onto <base> to bring the branch up to date.
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Input
|
|
12
12
|
|
|
13
|
-
`$ARGUMENTS` carries a `github.com/.../pull/<n>` URL, `slug=<slug>`, and a `
|
|
13
|
+
`$ARGUMENTS` carries a `github.com/.../pull/<n>` URL, `slug=<slug>`, and a `rebase` directive (no review ids, no failing check names). Parse all three.
|
|
14
14
|
|
|
15
15
|
## Inputs from disk
|
|
16
16
|
|
|
@@ -20,43 +20,45 @@ 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 `
|
|
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)).
|
|
24
24
|
|
|
25
|
-
### Step 2 — Rebase onto base
|
|
25
|
+
### Step 2 — Rebase onto base (resolve conflicts if any)
|
|
26
26
|
|
|
27
|
-
Run [`../_shared/rebase-before-e2e.md`](../_shared/rebase-before-e2e.md) against the base branch
|
|
27
|
+
Run the rebase from [`../_shared/rebase-before-e2e.md`](../_shared/rebase-before-e2e.md) against the base branch, taking its `always` path unconditionally — this programmatic mode never asks, so skip the `autoRebase` prompt (the watcher already decided a rebase is due).
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
29
|
+
- **Clean replay** — a behind-only branch (and any rebase that hits no conflicts) replays without intervention. Proceed to Step 3.
|
|
30
|
+
- **Conflicts** — handle per [`autoResolveConflicts`](../muggle-preferences/preference-gates/autoResolveConflicts.md):
|
|
31
|
+
- default `never` → abort and escalate per Step 5 (`kind: "rebase-conflict"`). The watcher keeps polling; the user resolves on GitHub, or opts into `autoResolveConflicts=always`.
|
|
32
|
+
- `always` → resolve behind the verify-or-rollback gate in [`../_shared/resolve-rebase-conflicts.md`](../_shared/resolve-rebase-conflicts.md).
|
|
31
33
|
|
|
32
34
|
### Step 3 — Verify the resolution
|
|
33
35
|
|
|
34
|
-
Build (typecheck + lint on the changed surface) + unit suite must pass. Run E2E per [`e2e-acceptance.md`](e2e-acceptance.md) when app logic changed and the session carries validation context. A
|
|
36
|
+
Build (typecheck + lint on the changed surface) + unit suite must pass. Run E2E per [`e2e-acceptance.md`](e2e-acceptance.md) when app logic changed and the session carries validation context. A rebase that does not verify is rolled back → escalate per Step 5. **Never push an unverified rebase.**
|
|
35
37
|
|
|
36
38
|
### Step 4 — Force-push + respawn
|
|
37
39
|
|
|
38
|
-
Push with `--force-with-lease` (the rebase rewrote history). Append the new SHA to `last_seen.pushed_shas`; increment `last_seen.conflict_resolve_attempts[
|
|
40
|
+
Push with `--force-with-lease` (the rebase rewrote history). Append the new SHA to `last_seen.pushed_shas`; increment `last_seen.conflict_resolve_attempts[rebase_sha]`. Respawn the watcher as the last action:
|
|
39
41
|
|
|
40
42
|
```
|
|
41
43
|
/loop 1m /muggle:muggle-pr-followup <slug> <n>
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
The watcher cancelled its own cron when it dispatched this cycle ([`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md) Step 5b), so this restart is the single live watcher. Its next tick re-checks
|
|
46
|
+
The watcher cancelled its own cron when it dispatched this cycle ([`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md) Step 5b), so this restart is the single live watcher. Its next tick re-checks the branch against its base on the new head — the rebase is its own verify loop, bounded by the per-SHA attempt budget.
|
|
45
47
|
|
|
46
48
|
### Step 5 — Escalate (can't resolve / budget spent)
|
|
47
49
|
|
|
48
|
-
When `autoResolveConflicts=never`, the resolution failed verification, or `conflict_resolve_attempts[
|
|
50
|
+
When `autoResolveConflicts=never`, the resolution failed verification, or `conflict_resolve_attempts[rebase_sha]` has reached 2:
|
|
49
51
|
|
|
50
|
-
1. Add `
|
|
51
|
-
2. Emit one terminal escalation naming the PR and the conflicting files.
|
|
52
|
+
1. Add `rebase_sha` to `last_seen.conflict_escalated_shas` so the watcher does not re-dispatch this SHA.
|
|
53
|
+
2. Emit one terminal escalation naming the PR and the conflicting files (or the failing verification, for a behind-only rebase that didn't verify).
|
|
52
54
|
3. Respawn the watcher (last action) — it keeps polling for the user's manual resolution or any new reviews.
|
|
53
55
|
|
|
54
56
|
### Step 6 — Telemetry
|
|
55
57
|
|
|
56
|
-
Emit one `muggle-do:cycle` event ([`../_shared/telemetry-events/muggle-do-cycle.md`](../_shared/telemetry-events/muggle-do-cycle.md)) with `outcome: "
|
|
58
|
+
Emit one `muggle-do:cycle` event ([`../_shared/telemetry-events/muggle-do-cycle.md`](../_shared/telemetry-events/muggle-do-cycle.md)) with `outcome: "rebased"` (a verified rebase pushed — behind-only or conflicts resolved) or `"rebase-escalated"`.
|
|
57
59
|
|
|
58
60
|
## Guardrails
|
|
59
61
|
|
|
60
|
-
- Max 2
|
|
61
|
-
- Never push an unverified
|
|
62
|
-
- The default `autoResolveConflicts=never` escalates to the user rather than guessing a
|
|
62
|
+
- Max 2 rebase attempts per SHA; then escalate rather than churn.
|
|
63
|
+
- Never push an unverified rebase — verify-or-rollback always.
|
|
64
|
+
- The default `autoResolveConflicts=never` escalates to the user rather than guessing a conflict resolution. Auto-resolution of conflicts is strictly opt-in; a clean behind-only rebase needs no opt-in.
|
|
@@ -55,7 +55,7 @@ When invoked with the directive (PR URL + slug + review ids), routes to [`../do/
|
|
|
55
55
|
|
|
56
56
|
## Input routing
|
|
57
57
|
|
|
58
|
-
`/muggle-do` serves one interactive mode (the forward pipeline, from a fresh task) and four programmatic modes the watcher dispatches (address-reviews, fix-ci,
|
|
58
|
+
`/muggle-do` serves one interactive mode (the forward pipeline, from a fresh task) and four programmatic modes the watcher dispatches (address-reviews, fix-ci, rebase, post-merge cleanup). Resolve `$ARGUMENTS` to a mode per [`../do/input-routing.md`](../do/input-routing.md) before doing anything else.
|
|
59
59
|
|
|
60
60
|
## Preferences
|
|
61
61
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# muggle-pr-followup — folder TOC
|
|
2
2
|
|
|
3
|
-
This folder holds the watcher loop
|
|
3
|
+
This folder holds the watcher loop that drives one PR toward merge-ready. The watcher is a **dumb pipe**: it polls for actionable review threads, CI checks, and the branch's standing against its base, and dispatches `/muggle-do` when there's review feedback, fixable red CI, or a branch behind or conflicting with its base. Cycle execution, classification, replies, rebases, and escalation all live in `/muggle-do` — see [stage-8 design](../../../../muggle-ai-brain/architecture/2026-05-08-muggle-do-pr-comment-loop-design.md) for the architectural rationale.
|
|
4
4
|
|
|
5
5
|
## Files in this folder
|
|
6
6
|
|
|
@@ -7,7 +7,7 @@ description: "Use when the user wants a pull request's incoming review feedback
|
|
|
7
7
|
|
|
8
8
|
> Telemetry first step: see [`../_shared/telemetry-emit.md`](../_shared/telemetry-emit.md). Use `skillName: "muggle-pr-followup"`.
|
|
9
9
|
|
|
10
|
-
A watcher that babysits one open PR
|
|
10
|
+
A watcher that babysits one open PR toward **merge-ready** — review threads addressed, CI green, and the branch rebased on its base. Polls for actionable feedback, check-run state, and the branch's standing against its base; when feedback lands, CI goes red, or the branch falls behind or conflicts with its base, hands the work to `/muggle-do` and exits. On merge or close, it hands the terminal wrap-up to `/muggle-do` the same way — teardown when merged, then a next-step suggestion. `/muggle-do` is the executor — it classifies the reviews, fixes the failing checks, or rebases onto the base (resolving any conflicts), pushes, replies per comment, and respawns the watcher.
|
|
11
11
|
|
|
12
12
|
**The watcher is a dumb pipe.** It does not classify reviews, iterate cycles, post replies, or escalate. All of that lives in `/muggle-do`. See [stage-8 design](../../../../muggle-ai-brain/architecture/2026-05-08-muggle-do-pr-comment-loop-design.md) for the rationale.
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Watcher Per-Tick Contract
|
|
2
2
|
|
|
3
|
-
The procedure for the **tick mode** of `muggle-pr-followup` — one polling iteration scoped to one PR. The watcher is a dumb pipe: it polls for actionable review threads, CI checks, and
|
|
3
|
+
The procedure for the **tick mode** of `muggle-pr-followup` — one polling iteration scoped to one PR. The watcher is a dumb pipe: it polls for actionable review threads, CI checks, and the branch's standing against its base, dispatches `/muggle-do` if there's unaddressed review feedback, fixable red CI, or a branch that's behind or conflicting with its base, and exits. It does not classify, fix, resolve, rebase, amend requirements, post replies, run cycles, or escalate.
|
|
4
4
|
|
|
5
5
|
Routing into this mode is documented in [`SKILL.md`](SKILL.md#routing). The architectural rationale lives in the brain docs `architecture/2026-05-08-muggle-do-pr-comment-loop-design.md` (the overall loop) and `architecture/2026-06-06-pr-followup-thread-state-baseline-design.md` (the thread-state dispatch trigger).
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ If `prs.json[0].state` on disk is already `merged` or `closed`, this slot was fi
|
|
|
31
31
|
|
|
32
32
|
### Step 1 — Refresh PR state
|
|
33
33
|
|
|
34
|
-
Per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). Update `prs.json[0].head_sha` and `prs.json[0].state` from the response; keep `mergeable`
|
|
34
|
+
Per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). Update `prs.json[0].head_sha` and `prs.json[0].state` from the response; keep `mergeable` (conflict signal) for Step 5, and run the recipe's `compare` call to capture `behind_by` (out-of-date signal) for Step 5.
|
|
35
35
|
|
|
36
36
|
### Step 2 — Termination check
|
|
37
37
|
|
|
@@ -77,22 +77,30 @@ The watcher does **not** classify. Classification, batching, replying, escalatio
|
|
|
77
77
|
5. Emit a `tick` event with `actionable_threads: <count>`, `dispatched_review_ids: [<id>, ...]`.
|
|
78
78
|
6. Exit. **Reviews preempt CI** — when there is actionable feedback, this tick dispatches address-reviews and never polls CI. The watcher is now stopped; the dev cycle owns the PR and restarts the watcher when it finishes. (The watcher also self-unschedules in Step 2, terminal.)
|
|
79
79
|
|
|
80
|
-
### Step 5 — No actionable feedback →
|
|
80
|
+
### Step 5 — No actionable feedback → keep the branch rebased on its base
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
A merge-ready branch is **current with its base** — neither conflicting nor behind. From the Step 1 metadata, the branch needs a rebase when either:
|
|
83
|
+
|
|
84
|
+
- `mergeable == CONFLICTING` (corroborated by `mergeStateStatus == DIRTY`) — conflicts with the base, **or**
|
|
85
|
+
- `behind_by > 0` — out of date with the base. Read this from the `compare` call (commit ancestry), **never** from `mergeStateStatus == BEHIND`: GitHub masks `BEHIND` behind `DIRTY`/`BLOCKED` and only surfaces it under "require branches up to date" protection, so a stale PR that is also awaiting review or has a red required check reports `BLOCKED` — and its staleness would go unseen. See [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md#behind-by-out-of-date-detection).
|
|
86
|
+
|
|
87
|
+
This trigger is **independent of approval and CI state**: an out-of-date branch is rebased whether or not it has been reviewed, approved, or has green checks. The watcher acts on staleness directly — it never waits for an approval to surface it.
|
|
88
|
+
|
|
89
|
+
If a rebase is due **and** `conflict_resolve_attempts[head_sha] < 2` **and** `head_sha` ∉ `conflict_escalated_shas` → dispatch and exit:
|
|
83
90
|
|
|
84
91
|
1. Reset `last_seen.idle_tick_count` to 0.
|
|
85
|
-
2. **Stop this watcher (single-thread):** cancel its cron exactly as in Step 4 — `/muggle-do`'s
|
|
86
|
-
3. Dispatch `/muggle-do` with a *
|
|
92
|
+
2. **Stop this watcher (single-thread):** cancel its cron exactly as in Step 4 — `/muggle-do`'s rebase respawns it when the cycle is done.
|
|
93
|
+
3. Dispatch `/muggle-do` with a *rebase* directive (PR URL + slug; no review ids, no check names):
|
|
87
94
|
```
|
|
88
|
-
/muggle-do
|
|
95
|
+
/muggle-do rebase on <pr-url> slug=<slug>
|
|
89
96
|
```
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
The executor rebases onto the base: a behind-only branch replays cleanly and force-pushes; a conflicting branch resolves behind the `autoResolveConflicts` gate. Both paths are `/muggle-do`'s — the watcher only decides *that* a rebase is due, never how.
|
|
98
|
+
4. Append a dispatching line to `followup.log`; emit a `tick` event with `rebase_needed: true`, `dispatched_rebase: true`.
|
|
99
|
+
5. Exit. The dev cycle owns the PR; its respawn restarts the watcher, whose next tick re-checks the branch against its base on the new head — the rebase is its own verify loop, bounded by the per-SHA attempt budget.
|
|
92
100
|
|
|
93
|
-
`
|
|
101
|
+
Otherwise — `behind_by == 0` and not conflicting (`mergeable == UNKNOWN` is fine here: `behind_by` is exact while GitHub is still computing conflict state, so a stale branch still triggers), or budget spent (`conflict_resolve_attempts[head_sha] >= 2` or `head_sha` ∈ `conflict_escalated_shas`) → fall through to CI.
|
|
94
102
|
|
|
95
|
-
### Step 6 — No actionable feedback,
|
|
103
|
+
### Step 6 — No actionable feedback, branch current → poll CI for the head SHA
|
|
96
104
|
|
|
97
105
|
Fetch the check-run rollup for `prs.json[0].head_sha` per [`../_shared/github-cli-recipes/pr-checks.md`](../_shared/github-cli-recipes/pr-checks.md), then:
|
|
98
106
|
|
|
@@ -111,7 +119,7 @@ Fetch the check-run rollup for `prs.json[0].head_sha` per [`../_shared/github-cl
|
|
|
111
119
|
|
|
112
120
|
### Step 7 — Idle
|
|
113
121
|
|
|
114
|
-
Any idle branch (Steps 4–6 that did not dispatch): increment `last_seen.idle_tick_count`, append an idle line to `followup.log` per [`output-templates/watcher-log.md`](output-templates/watcher-log.md), emit a `tick` event with `idle: true`, `actionable_threads: 0`, `dispatched_review_ids: []`, `
|
|
122
|
+
Any idle branch (Steps 4–6 that did not dispatch): increment `last_seen.idle_tick_count`, append an idle line to `followup.log` per [`output-templates/watcher-log.md`](output-templates/watcher-log.md), emit a `tick` event with `idle: true`, `actionable_threads: 0`, `dispatched_review_ids: []`, `rebase_needed: <bool>`, `dispatched_rebase: false`, `checks_red: <count or 0>`, `dispatched_ci_fix: false`. Exit. The next tick fires in 1 min via `/loop`.
|
|
115
123
|
|
|
116
124
|
## Output
|
|
117
125
|
|
|
@@ -56,8 +56,8 @@ Keyed by `"<owner>/<repo>#<n>"`. One key per PR in the slot.
|
|
|
56
56
|
- `pushed_shas`: every SHA `/muggle-do` has pushed for this PR. Append-only. Used by the resolve-reminder stage to recognize threads addressed by the loop.
|
|
57
57
|
- `ci_fix_attempts`: per-SHA count of fix-ci cycles `/muggle-do` has run. The watcher stops dispatching fix-ci for a SHA once its count reaches 3. Keyed by head SHA.
|
|
58
58
|
- `ci_escalated_shas`: head SHAs whose CI the fix-ci stage gave up on (attempts exhausted or only out-of-scope checks). The watcher excludes these from CI dispatch so a hopeless SHA is never re-fixed.
|
|
59
|
-
- `conflict_resolve_attempts`: per-SHA count of
|
|
60
|
-
- `conflict_escalated_shas`: head SHAs whose
|
|
59
|
+
- `conflict_resolve_attempts`: per-SHA count of rebase cycles `/muggle-do` has run for this SHA (behind-only or conflicting — both rebase onto the base). The watcher stops dispatching a rebase for a SHA once its count reaches 2. Keyed by head SHA. A clean behind-only rebase produces a new SHA, so the cap only bites a SHA that keeps failing to rebase-and-verify.
|
|
60
|
+
- `conflict_escalated_shas`: head SHAs whose rebase `/muggle-do` gave up on (attempts exhausted, or a conflict under `autoResolveConflicts=never`). The watcher excludes these from rebase dispatch so a hopeless SHA is never re-attempted.
|
|
61
61
|
|
|
62
62
|
## `state.md`
|
|
63
63
|
|
|
@@ -297,7 +297,7 @@ Checkout flow FAILED 15.7s 12 https://www.muggle-ai.co
|
|
|
297
297
|
Total: 3 tests | 2 passed | 1 failed | 37.1s
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
For failures
|
|
300
|
+
For failures, don't hand-write a verdict in the summary — route each through the debug path (Step 7C).
|
|
301
301
|
|
|
302
302
|
## Step 7B: Execute — Remote Mode
|
|
303
303
|
|
|
@@ -341,20 +341,11 @@ Login with invalid creds COMPLETED rt-def456
|
|
|
341
341
|
Checkout flow QUEUED rt-ghi789
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
## Step 7C: Route
|
|
344
|
+
## Step 7C: Route every failed run through the debug path
|
|
345
345
|
|
|
346
|
-
For every run with `status: "failed"` (or any non-passing terminal state) from 7A or 7B,
|
|
346
|
+
For every run with `status: "failed"` (or any non-passing terminal state) from 7A or 7B, route through [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md). This is **mandatory** — a failure is never reported without it. The debug path gathers evidence (attempted steps + reasoning + screenshot), diagnoses via [`_shared/failure-mode-handling.md`](../_shared/failure-mode-handling.md) (§B replay / §C regen), shows the debug card, and presents the guaranteed selection in which **"give feedback & rerun"** is always an option and "skip" is never the default.
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
- **Regen-mode failures** — section C (buckets: `transient` / `infra` / `agent-course` / `product-uxux`).
|
|
350
|
-
|
|
351
|
-
For each failed run:
|
|
352
|
-
1. Read the run with `muggle-local-run-result-get` (local) or `muggle-remote-wf-get-ts-gen-latest-run` / `muggle-remote-wf-get-ts-replay-latest-run` (remote) and extract signals per the heuristics in the shared doc.
|
|
353
|
-
2. Emit `replay-failure-classified` or `regen-failure-classified` via `muggle-local-telemetry-event-emit` **before** asking the user.
|
|
354
|
-
3. Present the recommended action via `AskUserQuestion` along with the alternatives the shared doc lists for that bucket.
|
|
355
|
-
4. After the user picks, emit the matching `*-resolved` event with `userAction` set to what they chose.
|
|
356
|
-
|
|
357
|
-
Process failures one at a time so the user isn't drowning in pickers — but emit telemetry per failure regardless.
|
|
348
|
+
Pass it per failed run: the `runId` (local) or workflow runtime id (remote), the `mode` that failed, `testCaseId`, `projectId`, and the execution handle (local: the dev loop from "Run the dev loop"; remote: 7B's workflow-start) so a rerun re-enters the same path. Process failures one at a time so the user isn't drowning in pickers.
|
|
358
349
|
|
|
359
350
|
## Step 8: Open Results in Browser
|
|
360
351
|
|
|
@@ -403,23 +394,22 @@ Once a PR exists for this work, offer to keep watching its review thread.
|
|
|
403
394
|
|
|
404
395
|
The `/mprfollowup` shortcut starts the same watcher manually at any time.
|
|
405
396
|
|
|
406
|
-
## Step 10: Offer feedback on
|
|
407
|
-
|
|
408
|
-
After the report is complete, if **any** test in the run had a `failed` or unexpected status (or the user verbally flags something looked off), suggest the feedback skill:
|
|
397
|
+
## Step 10: Offer feedback on a clean pass
|
|
409
398
|
|
|
410
|
-
|
|
399
|
+
Failures already got a guaranteed feedback-&-rerun offer in Step 7C's debug path — don't re-ask for them here.
|
|
411
400
|
|
|
412
|
-
|
|
413
|
-
- **Yes — give feedback** → invoke the `muggle-feedback` skill via the `Skill` tool
|
|
401
|
+
This step is only for a run that **passed** but the user flags as off (a misclick, wrong element, a summary that doesn't match intent). When that happens, use `AskUserQuestion`:
|
|
402
|
+
- **Yes — give feedback** → invoke the `muggle-feedback` skill via the `Skill` tool, passing the run's `runId` (local) or `testScriptId` (remote) as anchor context so the submit flow opens with the correct script already loaded.
|
|
414
403
|
- **No — skip**
|
|
415
404
|
|
|
416
|
-
|
|
405
|
+
Skip silently if nothing looked off.
|
|
417
406
|
|
|
418
407
|
## Non-negotiables
|
|
419
408
|
|
|
420
409
|
Each rule below is covered in-step above; these are the ones this skill most often violates, kept here as reinforcement:
|
|
421
410
|
|
|
422
411
|
- **Test-case shape** — never skip the generate→review cycle, never consolidate the generator's micro-tests, one atomic behavior per test case. Creating test cases directly or merging the generator's output is the single most frequent mistake.
|
|
412
|
+
- **Every failure routes through the debug path** — no failed run is summarized-and-dropped. Step 7C → [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md) is mandatory, "give feedback & rerun" is always offered, and "skip" is never the default.
|
|
423
413
|
- **Confirm intent before acting** — local vs remote; never guess the localhost/preview URL.
|
|
424
414
|
- **PR URLs run in a dedicated worktree** — never switch the user's main checkout; pass that worktree as `cwd`.
|
|
425
415
|
- **Every selection uses `AskUserQuestion`** — never ask the user to type a number; the user picks the project (never auto-select).
|
|
@@ -174,22 +174,13 @@ Read the run record per [`../_shared/dev-loop/failures.md`](../_shared/dev-loop/
|
|
|
174
174
|
|
|
175
175
|
- Include in the report: status, duration, pass/fail summary, per-step summary (passed runs), artifact paths, errors if failed, and script view URL when publishing ran.
|
|
176
176
|
|
|
177
|
-
### 9a. Route
|
|
177
|
+
### 9a. Route a failed run through the debug path
|
|
178
178
|
|
|
179
|
-
If the run's status is `failed` or any non-passing terminal state,
|
|
179
|
+
If the run's status is `failed` or any non-passing terminal state, route through [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md) — **mandatory**; a failure is never reported without it. It gathers evidence (attempted steps + reasoning + screenshot), diagnoses via [`_shared/failure-mode-handling.md`](../_shared/failure-mode-handling.md) (§B replay / §C regen), shows the debug card, and presents the guaranteed selection in which **"give feedback & rerun"** is always offered and "skip" is never the default.
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
- **Regen-mode run failed** (the user picked "Generate new script" or no script existed) → section C (buckets: `transient` / `infra` / `agent-course` / `product-uxux`).
|
|
181
|
+
Pass it: the `runId`, the `mode` that failed (replay if the user picked an existing script in Step 5, else regen), `testCaseId`, `projectId`, and the local execution handle so a rerun re-enters the same path.
|
|
183
182
|
|
|
184
|
-
|
|
185
|
-
1. Read the run via `muggle-local-run-result-get` and extract signals per the heuristics in the shared doc.
|
|
186
|
-
2. Emit `replay-failure-classified` or `regen-failure-classified` via `muggle-local-telemetry-event-emit` **before** asking the user.
|
|
187
|
-
3. Present the recommended action via `AskUserQuestion` with the alternatives the shared doc lists for that bucket.
|
|
188
|
-
4. After the user picks, emit the matching `*-resolved` event with `userAction`.
|
|
189
|
-
|
|
190
|
-
If the user picks `muggle-feedback` from any bucket's options, invoke the `muggle-feedback` skill via the `Skill` tool, passing the just-finished `runId` so the submit flow opens with this run preloaded.
|
|
191
|
-
|
|
192
|
-
Skip silently when the run passed cleanly — failure-mode events are by definition about failures.
|
|
183
|
+
Skip only when the run passed cleanly — the debug path is by definition about failures.
|
|
193
184
|
|
|
194
185
|
### 9b. Remind the user to guide the agent (every Electron invocation)
|
|
195
186
|
|
|
@@ -62,6 +62,7 @@ Gates run per [`preference-gates/README.md`](../muggle-preferences/preference-ga
|
|
|
62
62
|
|------------|-------|
|
|
63
63
|
| `autoRebase` | [rebase-check](./steps/rebase-check.md) — rebase onto `origin/<default>` before starting dev servers |
|
|
64
64
|
| `reusePreparePlan` | [reuse-plan](./steps/reuse-plan.md) — reuse the saved prepare plan for this stack, or rediscover |
|
|
65
|
+
| `autoSelectLocalHost` | [check-running](./steps/check-running.md) — reuse the recorded dev-server URL silently, or confirm it each run |
|
|
65
66
|
|
|
66
67
|
## Workflow
|
|
67
68
|
|
|
@@ -111,6 +112,8 @@ After a test run, the caller can re-invoke for cleanup or leave services running
|
|
|
111
112
|
|
|
112
113
|
## Guardrails
|
|
113
114
|
|
|
115
|
+
- **Never invent or default a host/port** — the dev-server URL is a recorded value, not a guess. Resolve it from `<repo>/.muggle-ai/last-host.json` (the [`autoSelectLocalHost`](../muggle-preferences/preference-gates/autoSelectLocalHost.md) cache) before probing ports; a framework default like `:3000` is never a fallback. See [check-running](./steps/check-running.md).
|
|
116
|
+
- **No silent auto-selection without a gate** — when no preference authorizes a silent choice (host, restart, kill), confirm with the user. A gate set to `always` is the only license to skip the question; absent that, ask.
|
|
114
117
|
- **Verify first, offer to start second** — check what's already running before proposing to start anything.
|
|
115
118
|
- **The user may prefer to start services themselves** — always offer that option.
|
|
116
119
|
- **Never start a process the user didn't approve.**
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Check what's already running
|
|
2
2
|
|
|
3
|
+
## Resolve the target host first
|
|
4
|
+
|
|
5
|
+
The dev-server URL the tests will hit is a **recorded value, not a guess** — resolve it before probing anything.
|
|
6
|
+
|
|
7
|
+
1. Read the cached host with `muggle-local-last-host-get`. It reads `<cwd>/.muggle-ai/last-host.json`; a worktree usually has **no cache of its own**, so when the worktree returns nothing, pass the **main** working-tree root as `cwd` — `git rev-parse --git-common-dir`, then its parent directory.
|
|
8
|
+
2. Apply the [`autoSelectLocalHost`](../../muggle-preferences/preference-gates/autoSelectLocalHost.md) gate (read its value from the `Muggle Test Preferences` session-context line; absent → `ask`):
|
|
9
|
+
- `always` **and** a cache exists → use it silently: `Using saved local URL {lastHost}`.
|
|
10
|
+
- otherwise (`ask` / `never`, or no cache) → **confirm before using any host.** Run the gate's Picker 1 with `{lastHost}` (cached URL, omitted when absent) and `{suggestedHost}` (a port you actually detect listening). Never auto-pick, and never fall back to a framework default like `:3000`; if nothing is cached or detected, ask the user to type the URL.
|
|
11
|
+
|
|
12
|
+
The resolved host fixes the **expected** dev-server port for the detection below — probe that port; don't infer the target from whichever port happens to be listening.
|
|
13
|
+
|
|
14
|
+
## Detect listening services
|
|
15
|
+
|
|
3
16
|
Run port detection and (when an app declares a backend URL) backend-health probe per [`../../_shared/dev-server-readiness.md`](../../_shared/dev-server-readiness.md). Cross-reference hits against selected service directories.
|
|
4
17
|
|
|
5
18
|
> "**backend-api** is already listening on port 3001 (PID 54321) — looks good."
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release": "5.0.
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-06-
|
|
2
|
+
"release": "5.0.4",
|
|
3
|
+
"buildId": "run-52-1",
|
|
4
|
+
"commitSha": "d27f03da1295aa4e58c8e2ba6a26a0e76c7700c9",
|
|
5
|
+
"buildTime": "2026-06-09T21:46:24Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muggleai/works",
|
|
3
3
|
"mcpName": "io.github.multiplex-ai/muggle",
|
|
4
|
-
"version": "5.0.
|
|
4
|
+
"version": "5.0.4",
|
|
5
5
|
"description": "Ship quality products with AI-powered E2E acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"test:watch": "vitest"
|
|
42
42
|
},
|
|
43
43
|
"muggleConfig": {
|
|
44
|
-
"electronAppVersion": "1.
|
|
44
|
+
"electronAppVersion": "1.2.3",
|
|
45
45
|
"downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
|
|
46
46
|
"runtimeTargetDefault": "production",
|
|
47
47
|
"checksums": {
|
|
48
|
-
"darwin-arm64": "
|
|
49
|
-
"darwin-x64": "
|
|
50
|
-
"linux-x64": "
|
|
51
|
-
"win32-x64": "
|
|
48
|
+
"darwin-arm64": "f46cfc5f052021f13de7d7721276d58863b958ec717631e98b97b7cd3d0fe4fa",
|
|
49
|
+
"darwin-x64": "e2254373a0bb3fffa46b236629c5d54a611ab05e138104a98f6f60a5d1280805",
|
|
50
|
+
"linux-x64": "ff5698fdbc1d65f436b75d860ebe50ee7171c7726565fa0ab2149a6f1cde4d13",
|
|
51
|
+
"win32-x64": "35ffa7af1767e802c7670c85205ea11961cb5583aa5dc30947d142eb7c734ff5"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
@@ -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.0.4",
|
|
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.0.4",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
# PR metadata snapshot
|
|
2
2
|
|
|
3
|
-
Fetch the fields the watcher and bootstrap need
|
|
3
|
+
Fetch the fields the watcher and bootstrap need.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
gh pr view <pr-number> --repo <owner>/<repo> \
|
|
@@ -10,4 +10,16 @@ gh pr view <pr-number> --repo <owner>/<repo> \
|
|
|
10
10
|
- `state` is one of `OPEN`, `MERGED`, `CLOSED`.
|
|
11
11
|
- `headRefOid` is the current head SHA — store as `head_sha` in `prs.json`.
|
|
12
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).
|
|
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`.
|
|
@@ -8,7 +8,7 @@ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`
|
|
|
8
8
|
|
|
9
9
|
| Recipe | Use case |
|
|
10
10
|
| :----- | :------- |
|
|
11
|
-
| [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch — watcher + bootstrap. |
|
|
11
|
+
| [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch, conflict + `behind_by` out-of-date detection — watcher + bootstrap. |
|
|
12
12
|
| [`submitted-reviews`](github-cli-recipes/submitted-reviews.md) | Fetch a review by id / watcher's body-only-review check. |
|
|
13
13
|
| [`pr-checks`](github-cli-recipes/pr-checks.md) | Check-run rollup for the head SHA — watcher's CI poll. |
|
|
14
14
|
| [`line-comments-for-review`](github-cli-recipes/line-comments-for-review.md) | Pull a review's line comments — `/muggle-do` per-comment routing. |
|
|
@@ -17,7 +17,7 @@ One per address-reviews invocation, regardless of outcome.
|
|
|
17
17
|
"ci_checks_escalated": ["<check-name>", ...],
|
|
18
18
|
"head_sha_before": "<sha-or-null>",
|
|
19
19
|
"head_sha_after": "<sha-or-null>",
|
|
20
|
-
"outcome": "pushed" | "escalated" | "mixed" | "no-op" | "self-loop-skip" | "ci-fixed" | "ci-escalated"
|
|
20
|
+
"outcome": "pushed" | "escalated" | "mixed" | "no-op" | "self-loop-skip" | "ci-fixed" | "ci-escalated" | "rebased" | "rebase-escalated"
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -29,5 +29,7 @@ One per address-reviews invocation, regardless of outcome.
|
|
|
29
29
|
- `"self-loop-skip"` — review was a synthetic wrapper around the agent's own reply (every line comment is a reply carrying the loop marker `<!-- muggle-do:bot -->`). Cursor advanced silently; no work, no escalation.
|
|
30
30
|
- `"ci-fixed"` — a watcher-dispatched fix-ci cycle pushed a fix for one or more red checks.
|
|
31
31
|
- `"ci-escalated"` — fix-ci exhausted its 3 attempts for the SHA or the failing checks were out of scope; the SHA was added to `ci_escalated_shas`. No further auto-fix on it.
|
|
32
|
+
- `"rebased"` — a watcher-dispatched rebase cycle rebased the branch onto its base (behind-only or conflicts resolved), verified, and force-pushed.
|
|
33
|
+
- `"rebase-escalated"` — the rebase couldn't be completed (a conflict under `autoResolveConflicts=never`, verification failed, or the 2-attempt budget for the SHA was spent); the SHA was added to `conflict_escalated_shas`. No further auto-rebase on it.
|
|
32
34
|
|
|
33
|
-
For fix-ci cycles (`ci-fixed` / `ci-escalated`) the `review_ids_*` arrays are empty and the `ci_checks_*` arrays carry the data: `ci_checks_in` (red checks dispatched), `ci_checks_fixed` (made green and pushed), `ci_checks_escalated` (out-of-scope or unresolved).
|
|
35
|
+
For fix-ci cycles (`ci-fixed` / `ci-escalated`) the `review_ids_*` arrays are empty and the `ci_checks_*` arrays carry the data: `ci_checks_in` (red checks dispatched), `ci_checks_fixed` (made green and pushed), `ci_checks_escalated` (out-of-scope or unresolved). For rebase cycles (`rebased` / `rebase-escalated`) all the `review_ids_*` and `ci_checks_*` arrays are empty; the SHA fields carry the before/after of the rebase.
|
|
@@ -11,6 +11,8 @@ One per watcher iteration (idle or not).
|
|
|
11
11
|
"pr_number": <int>,
|
|
12
12
|
"actionable_threads": <int>,
|
|
13
13
|
"dispatched_review_ids": [<int>, ...],
|
|
14
|
+
"rebase_needed": true | false,
|
|
15
|
+
"dispatched_rebase": true | false,
|
|
14
16
|
"checks_red": <int>,
|
|
15
17
|
"dispatched_ci_fix": true | false,
|
|
16
18
|
"terminal": true | false,
|
|
@@ -21,7 +23,9 @@ One per watcher iteration (idle or not).
|
|
|
21
23
|
|
|
22
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.
|
|
23
25
|
- `dispatched_review_ids`: owning review ids handed to `/muggle-do`. Empty when idle.
|
|
24
|
-
- `
|
|
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
|
+
- `dispatched_rebase`: true when this tick dispatched `/muggle-do` with a rebase directive.
|
|
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.
|
|
25
29
|
- `dispatched_ci_fix`: true when this tick dispatched `/muggle-do` with a fix-ci directive.
|
|
26
30
|
- `terminal`: true when this tick observed the PR merged or closed and wrote `result.md`.
|
|
27
31
|
- `idle`: true when nothing was dispatched this tick.
|
|
@@ -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.
|
|
@@ -4,7 +4,7 @@ How `/muggle-do` resolves `$ARGUMENTS` to a mode. Modes 1–4 are programmatic
|
|
|
4
4
|
|
|
5
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
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. **
|
|
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.
|
|
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,16 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Rebase (watcher-dispatched)
|
|
2
2
|
|
|
3
|
-
Rebase a PR
|
|
3
|
+
Rebase a PR's branch onto its base — whether it's merely **behind** (out of date, no conflict) or actually **conflicting** — behind a verify-or-rollback gate, then force-push, so a PR doesn't sit stale or un-mergeable forever. A dumb-pipe dispatch like fix-ci: the watcher detects the branch is behind or conflicting and hands off; the executor owns the rebase (and any conflict resolution), never the decision to dispatch.
|
|
4
4
|
|
|
5
5
|
## Turn preamble
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
**/muggle-do
|
|
8
|
+
**/muggle-do rebase** — rebasing <owner>/<repo>#<n> onto <base> to bring the branch up to date.
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Input
|
|
12
12
|
|
|
13
|
-
`$ARGUMENTS` carries a `github.com/.../pull/<n>` URL, `slug=<slug>`, and a `
|
|
13
|
+
`$ARGUMENTS` carries a `github.com/.../pull/<n>` URL, `slug=<slug>`, and a `rebase` directive (no review ids, no failing check names). Parse all three.
|
|
14
14
|
|
|
15
15
|
## Inputs from disk
|
|
16
16
|
|
|
@@ -20,43 +20,45 @@ 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 `
|
|
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)).
|
|
24
24
|
|
|
25
|
-
### Step 2 — Rebase onto base
|
|
25
|
+
### Step 2 — Rebase onto base (resolve conflicts if any)
|
|
26
26
|
|
|
27
|
-
Run [`../_shared/rebase-before-e2e.md`](../_shared/rebase-before-e2e.md) against the base branch
|
|
27
|
+
Run the rebase from [`../_shared/rebase-before-e2e.md`](../_shared/rebase-before-e2e.md) against the base branch, taking its `always` path unconditionally — this programmatic mode never asks, so skip the `autoRebase` prompt (the watcher already decided a rebase is due).
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
29
|
+
- **Clean replay** — a behind-only branch (and any rebase that hits no conflicts) replays without intervention. Proceed to Step 3.
|
|
30
|
+
- **Conflicts** — handle per [`autoResolveConflicts`](../muggle-preferences/preference-gates/autoResolveConflicts.md):
|
|
31
|
+
- default `never` → abort and escalate per Step 5 (`kind: "rebase-conflict"`). The watcher keeps polling; the user resolves on GitHub, or opts into `autoResolveConflicts=always`.
|
|
32
|
+
- `always` → resolve behind the verify-or-rollback gate in [`../_shared/resolve-rebase-conflicts.md`](../_shared/resolve-rebase-conflicts.md).
|
|
31
33
|
|
|
32
34
|
### Step 3 — Verify the resolution
|
|
33
35
|
|
|
34
|
-
Build (typecheck + lint on the changed surface) + unit suite must pass. Run E2E per [`e2e-acceptance.md`](e2e-acceptance.md) when app logic changed and the session carries validation context. A
|
|
36
|
+
Build (typecheck + lint on the changed surface) + unit suite must pass. Run E2E per [`e2e-acceptance.md`](e2e-acceptance.md) when app logic changed and the session carries validation context. A rebase that does not verify is rolled back → escalate per Step 5. **Never push an unverified rebase.**
|
|
35
37
|
|
|
36
38
|
### Step 4 — Force-push + respawn
|
|
37
39
|
|
|
38
|
-
Push with `--force-with-lease` (the rebase rewrote history). Append the new SHA to `last_seen.pushed_shas`; increment `last_seen.conflict_resolve_attempts[
|
|
40
|
+
Push with `--force-with-lease` (the rebase rewrote history). Append the new SHA to `last_seen.pushed_shas`; increment `last_seen.conflict_resolve_attempts[rebase_sha]`. Respawn the watcher as the last action:
|
|
39
41
|
|
|
40
42
|
```
|
|
41
43
|
/loop 1m /muggle:muggle-pr-followup <slug> <n>
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
The watcher cancelled its own cron when it dispatched this cycle ([`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md) Step 5b), so this restart is the single live watcher. Its next tick re-checks
|
|
46
|
+
The watcher cancelled its own cron when it dispatched this cycle ([`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md) Step 5b), so this restart is the single live watcher. Its next tick re-checks the branch against its base on the new head — the rebase is its own verify loop, bounded by the per-SHA attempt budget.
|
|
45
47
|
|
|
46
48
|
### Step 5 — Escalate (can't resolve / budget spent)
|
|
47
49
|
|
|
48
|
-
When `autoResolveConflicts=never`, the resolution failed verification, or `conflict_resolve_attempts[
|
|
50
|
+
When `autoResolveConflicts=never`, the resolution failed verification, or `conflict_resolve_attempts[rebase_sha]` has reached 2:
|
|
49
51
|
|
|
50
|
-
1. Add `
|
|
51
|
-
2. Emit one terminal escalation naming the PR and the conflicting files.
|
|
52
|
+
1. Add `rebase_sha` to `last_seen.conflict_escalated_shas` so the watcher does not re-dispatch this SHA.
|
|
53
|
+
2. Emit one terminal escalation naming the PR and the conflicting files (or the failing verification, for a behind-only rebase that didn't verify).
|
|
52
54
|
3. Respawn the watcher (last action) — it keeps polling for the user's manual resolution or any new reviews.
|
|
53
55
|
|
|
54
56
|
### Step 6 — Telemetry
|
|
55
57
|
|
|
56
|
-
Emit one `muggle-do:cycle` event ([`../_shared/telemetry-events/muggle-do-cycle.md`](../_shared/telemetry-events/muggle-do-cycle.md)) with `outcome: "
|
|
58
|
+
Emit one `muggle-do:cycle` event ([`../_shared/telemetry-events/muggle-do-cycle.md`](../_shared/telemetry-events/muggle-do-cycle.md)) with `outcome: "rebased"` (a verified rebase pushed — behind-only or conflicts resolved) or `"rebase-escalated"`.
|
|
57
59
|
|
|
58
60
|
## Guardrails
|
|
59
61
|
|
|
60
|
-
- Max 2
|
|
61
|
-
- Never push an unverified
|
|
62
|
-
- The default `autoResolveConflicts=never` escalates to the user rather than guessing a
|
|
62
|
+
- Max 2 rebase attempts per SHA; then escalate rather than churn.
|
|
63
|
+
- Never push an unverified rebase — verify-or-rollback always.
|
|
64
|
+
- The default `autoResolveConflicts=never` escalates to the user rather than guessing a conflict resolution. Auto-resolution of conflicts is strictly opt-in; a clean behind-only rebase needs no opt-in.
|
|
@@ -55,7 +55,7 @@ When invoked with the directive (PR URL + slug + review ids), routes to [`../do/
|
|
|
55
55
|
|
|
56
56
|
## Input routing
|
|
57
57
|
|
|
58
|
-
`/muggle-do` serves one interactive mode (the forward pipeline, from a fresh task) and four programmatic modes the watcher dispatches (address-reviews, fix-ci,
|
|
58
|
+
`/muggle-do` serves one interactive mode (the forward pipeline, from a fresh task) and four programmatic modes the watcher dispatches (address-reviews, fix-ci, rebase, post-merge cleanup). Resolve `$ARGUMENTS` to a mode per [`../do/input-routing.md`](../do/input-routing.md) before doing anything else.
|
|
59
59
|
|
|
60
60
|
## Preferences
|
|
61
61
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# muggle-pr-followup — folder TOC
|
|
2
2
|
|
|
3
|
-
This folder holds the watcher loop
|
|
3
|
+
This folder holds the watcher loop that drives one PR toward merge-ready. The watcher is a **dumb pipe**: it polls for actionable review threads, CI checks, and the branch's standing against its base, and dispatches `/muggle-do` when there's review feedback, fixable red CI, or a branch behind or conflicting with its base. Cycle execution, classification, replies, rebases, and escalation all live in `/muggle-do` — see [stage-8 design](../../../../muggle-ai-brain/architecture/2026-05-08-muggle-do-pr-comment-loop-design.md) for the architectural rationale.
|
|
4
4
|
|
|
5
5
|
## Files in this folder
|
|
6
6
|
|
|
@@ -7,7 +7,7 @@ description: "Use when the user wants a pull request's incoming review feedback
|
|
|
7
7
|
|
|
8
8
|
> Telemetry first step: see [`../_shared/telemetry-emit.md`](../_shared/telemetry-emit.md). Use `skillName: "muggle-pr-followup"`.
|
|
9
9
|
|
|
10
|
-
A watcher that babysits one open PR
|
|
10
|
+
A watcher that babysits one open PR toward **merge-ready** — review threads addressed, CI green, and the branch rebased on its base. Polls for actionable feedback, check-run state, and the branch's standing against its base; when feedback lands, CI goes red, or the branch falls behind or conflicts with its base, hands the work to `/muggle-do` and exits. On merge or close, it hands the terminal wrap-up to `/muggle-do` the same way — teardown when merged, then a next-step suggestion. `/muggle-do` is the executor — it classifies the reviews, fixes the failing checks, or rebases onto the base (resolving any conflicts), pushes, replies per comment, and respawns the watcher.
|
|
11
11
|
|
|
12
12
|
**The watcher is a dumb pipe.** It does not classify reviews, iterate cycles, post replies, or escalate. All of that lives in `/muggle-do`. See [stage-8 design](../../../../muggle-ai-brain/architecture/2026-05-08-muggle-do-pr-comment-loop-design.md) for the rationale.
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Watcher Per-Tick Contract
|
|
2
2
|
|
|
3
|
-
The procedure for the **tick mode** of `muggle-pr-followup` — one polling iteration scoped to one PR. The watcher is a dumb pipe: it polls for actionable review threads, CI checks, and
|
|
3
|
+
The procedure for the **tick mode** of `muggle-pr-followup` — one polling iteration scoped to one PR. The watcher is a dumb pipe: it polls for actionable review threads, CI checks, and the branch's standing against its base, dispatches `/muggle-do` if there's unaddressed review feedback, fixable red CI, or a branch that's behind or conflicting with its base, and exits. It does not classify, fix, resolve, rebase, amend requirements, post replies, run cycles, or escalate.
|
|
4
4
|
|
|
5
5
|
Routing into this mode is documented in [`SKILL.md`](SKILL.md#routing). The architectural rationale lives in the brain docs `architecture/2026-05-08-muggle-do-pr-comment-loop-design.md` (the overall loop) and `architecture/2026-06-06-pr-followup-thread-state-baseline-design.md` (the thread-state dispatch trigger).
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ If `prs.json[0].state` on disk is already `merged` or `closed`, this slot was fi
|
|
|
31
31
|
|
|
32
32
|
### Step 1 — Refresh PR state
|
|
33
33
|
|
|
34
|
-
Per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). Update `prs.json[0].head_sha` and `prs.json[0].state` from the response; keep `mergeable`
|
|
34
|
+
Per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). Update `prs.json[0].head_sha` and `prs.json[0].state` from the response; keep `mergeable` (conflict signal) for Step 5, and run the recipe's `compare` call to capture `behind_by` (out-of-date signal) for Step 5.
|
|
35
35
|
|
|
36
36
|
### Step 2 — Termination check
|
|
37
37
|
|
|
@@ -77,22 +77,30 @@ The watcher does **not** classify. Classification, batching, replying, escalatio
|
|
|
77
77
|
5. Emit a `tick` event with `actionable_threads: <count>`, `dispatched_review_ids: [<id>, ...]`.
|
|
78
78
|
6. Exit. **Reviews preempt CI** — when there is actionable feedback, this tick dispatches address-reviews and never polls CI. The watcher is now stopped; the dev cycle owns the PR and restarts the watcher when it finishes. (The watcher also self-unschedules in Step 2, terminal.)
|
|
79
79
|
|
|
80
|
-
### Step 5 — No actionable feedback →
|
|
80
|
+
### Step 5 — No actionable feedback → keep the branch rebased on its base
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
A merge-ready branch is **current with its base** — neither conflicting nor behind. From the Step 1 metadata, the branch needs a rebase when either:
|
|
83
|
+
|
|
84
|
+
- `mergeable == CONFLICTING` (corroborated by `mergeStateStatus == DIRTY`) — conflicts with the base, **or**
|
|
85
|
+
- `behind_by > 0` — out of date with the base. Read this from the `compare` call (commit ancestry), **never** from `mergeStateStatus == BEHIND`: GitHub masks `BEHIND` behind `DIRTY`/`BLOCKED` and only surfaces it under "require branches up to date" protection, so a stale PR that is also awaiting review or has a red required check reports `BLOCKED` — and its staleness would go unseen. See [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md#behind-by-out-of-date-detection).
|
|
86
|
+
|
|
87
|
+
This trigger is **independent of approval and CI state**: an out-of-date branch is rebased whether or not it has been reviewed, approved, or has green checks. The watcher acts on staleness directly — it never waits for an approval to surface it.
|
|
88
|
+
|
|
89
|
+
If a rebase is due **and** `conflict_resolve_attempts[head_sha] < 2` **and** `head_sha` ∉ `conflict_escalated_shas` → dispatch and exit:
|
|
83
90
|
|
|
84
91
|
1. Reset `last_seen.idle_tick_count` to 0.
|
|
85
|
-
2. **Stop this watcher (single-thread):** cancel its cron exactly as in Step 4 — `/muggle-do`'s
|
|
86
|
-
3. Dispatch `/muggle-do` with a *
|
|
92
|
+
2. **Stop this watcher (single-thread):** cancel its cron exactly as in Step 4 — `/muggle-do`'s rebase respawns it when the cycle is done.
|
|
93
|
+
3. Dispatch `/muggle-do` with a *rebase* directive (PR URL + slug; no review ids, no check names):
|
|
87
94
|
```
|
|
88
|
-
/muggle-do
|
|
95
|
+
/muggle-do rebase on <pr-url> slug=<slug>
|
|
89
96
|
```
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
The executor rebases onto the base: a behind-only branch replays cleanly and force-pushes; a conflicting branch resolves behind the `autoResolveConflicts` gate. Both paths are `/muggle-do`'s — the watcher only decides *that* a rebase is due, never how.
|
|
98
|
+
4. Append a dispatching line to `followup.log`; emit a `tick` event with `rebase_needed: true`, `dispatched_rebase: true`.
|
|
99
|
+
5. Exit. The dev cycle owns the PR; its respawn restarts the watcher, whose next tick re-checks the branch against its base on the new head — the rebase is its own verify loop, bounded by the per-SHA attempt budget.
|
|
92
100
|
|
|
93
|
-
`
|
|
101
|
+
Otherwise — `behind_by == 0` and not conflicting (`mergeable == UNKNOWN` is fine here: `behind_by` is exact while GitHub is still computing conflict state, so a stale branch still triggers), or budget spent (`conflict_resolve_attempts[head_sha] >= 2` or `head_sha` ∈ `conflict_escalated_shas`) → fall through to CI.
|
|
94
102
|
|
|
95
|
-
### Step 6 — No actionable feedback,
|
|
103
|
+
### Step 6 — No actionable feedback, branch current → poll CI for the head SHA
|
|
96
104
|
|
|
97
105
|
Fetch the check-run rollup for `prs.json[0].head_sha` per [`../_shared/github-cli-recipes/pr-checks.md`](../_shared/github-cli-recipes/pr-checks.md), then:
|
|
98
106
|
|
|
@@ -111,7 +119,7 @@ Fetch the check-run rollup for `prs.json[0].head_sha` per [`../_shared/github-cl
|
|
|
111
119
|
|
|
112
120
|
### Step 7 — Idle
|
|
113
121
|
|
|
114
|
-
Any idle branch (Steps 4–6 that did not dispatch): increment `last_seen.idle_tick_count`, append an idle line to `followup.log` per [`output-templates/watcher-log.md`](output-templates/watcher-log.md), emit a `tick` event with `idle: true`, `actionable_threads: 0`, `dispatched_review_ids: []`, `
|
|
122
|
+
Any idle branch (Steps 4–6 that did not dispatch): increment `last_seen.idle_tick_count`, append an idle line to `followup.log` per [`output-templates/watcher-log.md`](output-templates/watcher-log.md), emit a `tick` event with `idle: true`, `actionable_threads: 0`, `dispatched_review_ids: []`, `rebase_needed: <bool>`, `dispatched_rebase: false`, `checks_red: <count or 0>`, `dispatched_ci_fix: false`. Exit. The next tick fires in 1 min via `/loop`.
|
|
115
123
|
|
|
116
124
|
## Output
|
|
117
125
|
|
|
@@ -56,8 +56,8 @@ Keyed by `"<owner>/<repo>#<n>"`. One key per PR in the slot.
|
|
|
56
56
|
- `pushed_shas`: every SHA `/muggle-do` has pushed for this PR. Append-only. Used by the resolve-reminder stage to recognize threads addressed by the loop.
|
|
57
57
|
- `ci_fix_attempts`: per-SHA count of fix-ci cycles `/muggle-do` has run. The watcher stops dispatching fix-ci for a SHA once its count reaches 3. Keyed by head SHA.
|
|
58
58
|
- `ci_escalated_shas`: head SHAs whose CI the fix-ci stage gave up on (attempts exhausted or only out-of-scope checks). The watcher excludes these from CI dispatch so a hopeless SHA is never re-fixed.
|
|
59
|
-
- `conflict_resolve_attempts`: per-SHA count of
|
|
60
|
-
- `conflict_escalated_shas`: head SHAs whose
|
|
59
|
+
- `conflict_resolve_attempts`: per-SHA count of rebase cycles `/muggle-do` has run for this SHA (behind-only or conflicting — both rebase onto the base). The watcher stops dispatching a rebase for a SHA once its count reaches 2. Keyed by head SHA. A clean behind-only rebase produces a new SHA, so the cap only bites a SHA that keeps failing to rebase-and-verify.
|
|
60
|
+
- `conflict_escalated_shas`: head SHAs whose rebase `/muggle-do` gave up on (attempts exhausted, or a conflict under `autoResolveConflicts=never`). The watcher excludes these from rebase dispatch so a hopeless SHA is never re-attempted.
|
|
61
61
|
|
|
62
62
|
## `state.md`
|
|
63
63
|
|
|
@@ -297,7 +297,7 @@ Checkout flow FAILED 15.7s 12 https://www.muggle-ai.co
|
|
|
297
297
|
Total: 3 tests | 2 passed | 1 failed | 37.1s
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
For failures
|
|
300
|
+
For failures, don't hand-write a verdict in the summary — route each through the debug path (Step 7C).
|
|
301
301
|
|
|
302
302
|
## Step 7B: Execute — Remote Mode
|
|
303
303
|
|
|
@@ -341,20 +341,11 @@ Login with invalid creds COMPLETED rt-def456
|
|
|
341
341
|
Checkout flow QUEUED rt-ghi789
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
## Step 7C: Route
|
|
344
|
+
## Step 7C: Route every failed run through the debug path
|
|
345
345
|
|
|
346
|
-
For every run with `status: "failed"` (or any non-passing terminal state) from 7A or 7B,
|
|
346
|
+
For every run with `status: "failed"` (or any non-passing terminal state) from 7A or 7B, route through [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md). This is **mandatory** — a failure is never reported without it. The debug path gathers evidence (attempted steps + reasoning + screenshot), diagnoses via [`_shared/failure-mode-handling.md`](../_shared/failure-mode-handling.md) (§B replay / §C regen), shows the debug card, and presents the guaranteed selection in which **"give feedback & rerun"** is always an option and "skip" is never the default.
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
- **Regen-mode failures** — section C (buckets: `transient` / `infra` / `agent-course` / `product-uxux`).
|
|
350
|
-
|
|
351
|
-
For each failed run:
|
|
352
|
-
1. Read the run with `muggle-local-run-result-get` (local) or `muggle-remote-wf-get-ts-gen-latest-run` / `muggle-remote-wf-get-ts-replay-latest-run` (remote) and extract signals per the heuristics in the shared doc.
|
|
353
|
-
2. Emit `replay-failure-classified` or `regen-failure-classified` via `muggle-local-telemetry-event-emit` **before** asking the user.
|
|
354
|
-
3. Present the recommended action via `AskUserQuestion` along with the alternatives the shared doc lists for that bucket.
|
|
355
|
-
4. After the user picks, emit the matching `*-resolved` event with `userAction` set to what they chose.
|
|
356
|
-
|
|
357
|
-
Process failures one at a time so the user isn't drowning in pickers — but emit telemetry per failure regardless.
|
|
348
|
+
Pass it per failed run: the `runId` (local) or workflow runtime id (remote), the `mode` that failed, `testCaseId`, `projectId`, and the execution handle (local: the dev loop from "Run the dev loop"; remote: 7B's workflow-start) so a rerun re-enters the same path. Process failures one at a time so the user isn't drowning in pickers.
|
|
358
349
|
|
|
359
350
|
## Step 8: Open Results in Browser
|
|
360
351
|
|
|
@@ -403,23 +394,22 @@ Once a PR exists for this work, offer to keep watching its review thread.
|
|
|
403
394
|
|
|
404
395
|
The `/mprfollowup` shortcut starts the same watcher manually at any time.
|
|
405
396
|
|
|
406
|
-
## Step 10: Offer feedback on
|
|
407
|
-
|
|
408
|
-
After the report is complete, if **any** test in the run had a `failed` or unexpected status (or the user verbally flags something looked off), suggest the feedback skill:
|
|
397
|
+
## Step 10: Offer feedback on a clean pass
|
|
409
398
|
|
|
410
|
-
|
|
399
|
+
Failures already got a guaranteed feedback-&-rerun offer in Step 7C's debug path — don't re-ask for them here.
|
|
411
400
|
|
|
412
|
-
|
|
413
|
-
- **Yes — give feedback** → invoke the `muggle-feedback` skill via the `Skill` tool
|
|
401
|
+
This step is only for a run that **passed** but the user flags as off (a misclick, wrong element, a summary that doesn't match intent). When that happens, use `AskUserQuestion`:
|
|
402
|
+
- **Yes — give feedback** → invoke the `muggle-feedback` skill via the `Skill` tool, passing the run's `runId` (local) or `testScriptId` (remote) as anchor context so the submit flow opens with the correct script already loaded.
|
|
414
403
|
- **No — skip**
|
|
415
404
|
|
|
416
|
-
|
|
405
|
+
Skip silently if nothing looked off.
|
|
417
406
|
|
|
418
407
|
## Non-negotiables
|
|
419
408
|
|
|
420
409
|
Each rule below is covered in-step above; these are the ones this skill most often violates, kept here as reinforcement:
|
|
421
410
|
|
|
422
411
|
- **Test-case shape** — never skip the generate→review cycle, never consolidate the generator's micro-tests, one atomic behavior per test case. Creating test cases directly or merging the generator's output is the single most frequent mistake.
|
|
412
|
+
- **Every failure routes through the debug path** — no failed run is summarized-and-dropped. Step 7C → [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md) is mandatory, "give feedback & rerun" is always offered, and "skip" is never the default.
|
|
423
413
|
- **Confirm intent before acting** — local vs remote; never guess the localhost/preview URL.
|
|
424
414
|
- **PR URLs run in a dedicated worktree** — never switch the user's main checkout; pass that worktree as `cwd`.
|
|
425
415
|
- **Every selection uses `AskUserQuestion`** — never ask the user to type a number; the user picks the project (never auto-select).
|
|
@@ -174,22 +174,13 @@ Read the run record per [`../_shared/dev-loop/failures.md`](../_shared/dev-loop/
|
|
|
174
174
|
|
|
175
175
|
- Include in the report: status, duration, pass/fail summary, per-step summary (passed runs), artifact paths, errors if failed, and script view URL when publishing ran.
|
|
176
176
|
|
|
177
|
-
### 9a. Route
|
|
177
|
+
### 9a. Route a failed run through the debug path
|
|
178
178
|
|
|
179
|
-
If the run's status is `failed` or any non-passing terminal state,
|
|
179
|
+
If the run's status is `failed` or any non-passing terminal state, route through [`_shared/debug-failed-run.md`](../_shared/debug-failed-run.md) — **mandatory**; a failure is never reported without it. It gathers evidence (attempted steps + reasoning + screenshot), diagnoses via [`_shared/failure-mode-handling.md`](../_shared/failure-mode-handling.md) (§B replay / §C regen), shows the debug card, and presents the guaranteed selection in which **"give feedback & rerun"** is always offered and "skip" is never the default.
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
- **Regen-mode run failed** (the user picked "Generate new script" or no script existed) → section C (buckets: `transient` / `infra` / `agent-course` / `product-uxux`).
|
|
181
|
+
Pass it: the `runId`, the `mode` that failed (replay if the user picked an existing script in Step 5, else regen), `testCaseId`, `projectId`, and the local execution handle so a rerun re-enters the same path.
|
|
183
182
|
|
|
184
|
-
|
|
185
|
-
1. Read the run via `muggle-local-run-result-get` and extract signals per the heuristics in the shared doc.
|
|
186
|
-
2. Emit `replay-failure-classified` or `regen-failure-classified` via `muggle-local-telemetry-event-emit` **before** asking the user.
|
|
187
|
-
3. Present the recommended action via `AskUserQuestion` with the alternatives the shared doc lists for that bucket.
|
|
188
|
-
4. After the user picks, emit the matching `*-resolved` event with `userAction`.
|
|
189
|
-
|
|
190
|
-
If the user picks `muggle-feedback` from any bucket's options, invoke the `muggle-feedback` skill via the `Skill` tool, passing the just-finished `runId` so the submit flow opens with this run preloaded.
|
|
191
|
-
|
|
192
|
-
Skip silently when the run passed cleanly — failure-mode events are by definition about failures.
|
|
183
|
+
Skip only when the run passed cleanly — the debug path is by definition about failures.
|
|
193
184
|
|
|
194
185
|
### 9b. Remind the user to guide the agent (every Electron invocation)
|
|
195
186
|
|
|
@@ -62,6 +62,7 @@ Gates run per [`preference-gates/README.md`](../muggle-preferences/preference-ga
|
|
|
62
62
|
|------------|-------|
|
|
63
63
|
| `autoRebase` | [rebase-check](./steps/rebase-check.md) — rebase onto `origin/<default>` before starting dev servers |
|
|
64
64
|
| `reusePreparePlan` | [reuse-plan](./steps/reuse-plan.md) — reuse the saved prepare plan for this stack, or rediscover |
|
|
65
|
+
| `autoSelectLocalHost` | [check-running](./steps/check-running.md) — reuse the recorded dev-server URL silently, or confirm it each run |
|
|
65
66
|
|
|
66
67
|
## Workflow
|
|
67
68
|
|
|
@@ -111,6 +112,8 @@ After a test run, the caller can re-invoke for cleanup or leave services running
|
|
|
111
112
|
|
|
112
113
|
## Guardrails
|
|
113
114
|
|
|
115
|
+
- **Never invent or default a host/port** — the dev-server URL is a recorded value, not a guess. Resolve it from `<repo>/.muggle-ai/last-host.json` (the [`autoSelectLocalHost`](../muggle-preferences/preference-gates/autoSelectLocalHost.md) cache) before probing ports; a framework default like `:3000` is never a fallback. See [check-running](./steps/check-running.md).
|
|
116
|
+
- **No silent auto-selection without a gate** — when no preference authorizes a silent choice (host, restart, kill), confirm with the user. A gate set to `always` is the only license to skip the question; absent that, ask.
|
|
114
117
|
- **Verify first, offer to start second** — check what's already running before proposing to start anything.
|
|
115
118
|
- **The user may prefer to start services themselves** — always offer that option.
|
|
116
119
|
- **Never start a process the user didn't approve.**
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Check what's already running
|
|
2
2
|
|
|
3
|
+
## Resolve the target host first
|
|
4
|
+
|
|
5
|
+
The dev-server URL the tests will hit is a **recorded value, not a guess** — resolve it before probing anything.
|
|
6
|
+
|
|
7
|
+
1. Read the cached host with `muggle-local-last-host-get`. It reads `<cwd>/.muggle-ai/last-host.json`; a worktree usually has **no cache of its own**, so when the worktree returns nothing, pass the **main** working-tree root as `cwd` — `git rev-parse --git-common-dir`, then its parent directory.
|
|
8
|
+
2. Apply the [`autoSelectLocalHost`](../../muggle-preferences/preference-gates/autoSelectLocalHost.md) gate (read its value from the `Muggle Test Preferences` session-context line; absent → `ask`):
|
|
9
|
+
- `always` **and** a cache exists → use it silently: `Using saved local URL {lastHost}`.
|
|
10
|
+
- otherwise (`ask` / `never`, or no cache) → **confirm before using any host.** Run the gate's Picker 1 with `{lastHost}` (cached URL, omitted when absent) and `{suggestedHost}` (a port you actually detect listening). Never auto-pick, and never fall back to a framework default like `:3000`; if nothing is cached or detected, ask the user to type the URL.
|
|
11
|
+
|
|
12
|
+
The resolved host fixes the **expected** dev-server port for the detection below — probe that port; don't infer the target from whichever port happens to be listening.
|
|
13
|
+
|
|
14
|
+
## Detect listening services
|
|
15
|
+
|
|
3
16
|
Run port detection and (when an app declares a backend URL) backend-health probe per [`../../_shared/dev-server-readiness.md`](../../_shared/dev-server-readiness.md). Cross-reference hits against selected service directories.
|
|
4
17
|
|
|
5
18
|
> "**backend-api** is already listening on port 3001 (PID 54321) — looks good."
|