@muggleai/works 5.0.1 → 5.0.3
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/scripts/guardrails.mjs +2 -1
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +1 -1
- package/dist/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +14 -6
- package/dist/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +6 -2
- package/dist/plugin/skills/_shared/github-cli-recipes.md +2 -2
- package/dist/plugin/skills/_shared/pr-followup-helpers/classify.md +2 -2
- package/dist/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +6 -12
- package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.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 +9 -5
- package/dist/plugin/skills/do/address-reviews.md +7 -7
- package/dist/plugin/skills/do/input-routing.md +1 -1
- package/dist/plugin/skills/do/open-prs/forward.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-feedback/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +3 -3
- package/dist/plugin/skills/muggle-pr-followup/auto-track.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +8 -6
- package/dist/plugin/skills/muggle-pr-followup/contract.md +30 -21
- package/dist/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +2 -2
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +9 -9
- package/dist/plugin/skills/muggle-status/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-test/SKILL.md +13 -51
- package/dist/plugin/skills/muggle-test/reference.md +35 -0
- package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -10
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +4 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +13 -0
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- 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/scripts/guardrails.mjs +2 -1
- package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +1 -1
- package/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +14 -6
- package/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +6 -2
- package/plugin/skills/_shared/github-cli-recipes.md +2 -2
- package/plugin/skills/_shared/pr-followup-helpers/classify.md +2 -2
- package/plugin/skills/_shared/pr-followup-helpers/echo-skip.md +6 -12
- package/plugin/skills/_shared/pr-followup-helpers/reply-routing.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 +9 -5
- package/plugin/skills/do/address-reviews.md +7 -7
- package/plugin/skills/do/input-routing.md +1 -1
- package/plugin/skills/do/open-prs/forward.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-feedback/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +1 -1
- package/plugin/skills/muggle-pr-followup/SKILL.md +3 -3
- package/plugin/skills/muggle-pr-followup/auto-track.md +1 -1
- package/plugin/skills/muggle-pr-followup/bootstrap.md +8 -6
- package/plugin/skills/muggle-pr-followup/contract.md +30 -21
- package/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +1 -1
- package/plugin/skills/muggle-pr-followup/output-templates/help.md +1 -1
- package/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +2 -2
- package/plugin/skills/muggle-pr-followup/state-schemas.md +9 -9
- package/plugin/skills/muggle-status/SKILL.md +1 -1
- package/plugin/skills/muggle-test/SKILL.md +13 -51
- package/plugin/skills/muggle-test/reference.md +35 -0
- package/plugin/skills/muggle-test-import/SKILL.md +1 -10
- package/plugin/skills/muggle-test-prepare/SKILL.md +4 -1
- package/plugin/skills/muggle-test-prepare/steps/check-running.md +13 -0
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
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.3",
|
|
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.3",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
|
@@ -64,12 +64,13 @@ function shouldRunE2E(state) {
|
|
|
64
64
|
|
|
65
65
|
// src/guardrails/detectBuildIntent.ts
|
|
66
66
|
var BUILD = /\b(implement|build|add|create|write|fix|refactor|wire up|hook up|make (a|the|it)|change the)\b/i;
|
|
67
|
+
var DEVCYCLE = /\bresolve\b[^.?!]{0,40}\bconflicts?\b|\bget\b[^.?!]{0,40}\bpr\b[^.?!]{0,40}\b(green|merged?|passing)\b/i;
|
|
67
68
|
var QUESTION = /^\s*(why|what|how|when|where|who|is|are|does|do|can you (explain|tell)|explain)\b/i;
|
|
68
69
|
function detectBuildIntent(prompt) {
|
|
69
70
|
const p = (prompt ?? "").trim();
|
|
70
71
|
if (!p || p.startsWith("/")) return false;
|
|
71
72
|
if (QUESTION.test(p)) return false;
|
|
72
|
-
return BUILD.test(p);
|
|
73
|
+
return BUILD.test(p) || DEVCYCLE.test(p);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
// src/guardrails/emit.ts
|
|
@@ -10,4 +10,4 @@ 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
|
|
13
|
+
- `mergeable` is `MERGEABLE`, `CONFLICTING`, or `UNKNOWN` (GitHub still computing — treat as current this tick). `mergeStateStatus` carries the finer state: `DIRTY` = conflicts with base, `BEHIND` = out of date with base (no conflict), `CLEAN`/`BLOCKED`/`UNSTABLE`/`HAS_HOOKS` = current. The watcher dispatches a rebase on `CONFLICTING`/`DIRTY` **or** `BEHIND` — keeping the branch current with its base, a merge-ready gap no review or CI signal would surface.
|
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
# Submitted reviews
|
|
1
|
+
# Submitted reviews
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Two consumers (this recipe links to neither — it is a shared primitive):
|
|
4
|
+
|
|
5
|
+
- the watcher's **body-only-review** check — a submitted review carrying a summary body but **no** line comments. Line-comment threads are dispatched from live thread state, not here.
|
|
6
|
+
- the address-reviews fetch of a **specific** review id.
|
|
4
7
|
|
|
5
8
|
```bash
|
|
6
9
|
gh api repos/<owner>/<repo>/pulls/<n>/reviews --paginate
|
|
7
10
|
```
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
Common filter:
|
|
10
13
|
|
|
11
14
|
- `submitted_at != null` (skip PENDING drafts)
|
|
12
|
-
- `id > last_seen.reviewId`
|
|
13
|
-
- `id` not in `last_seen.escalated_review_ids`
|
|
14
15
|
- `user.login` in the resolved allow-list
|
|
15
16
|
- `state` in `{CHANGES_REQUESTED, COMMENTED}`, OR `APPROVED` with a non-empty body or at least one line comment
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
The **watcher's body-only check** adds:
|
|
19
|
+
|
|
20
|
+
- the review has **no line comments** — `gh api repos/<owner>/<repo>/pulls/<n>/reviews/<id>/comments` returns `[]`. A review with line comments is dispatched from thread state, not here.
|
|
21
|
+
- `id > last_seen.lastBodyReviewId`
|
|
22
|
+
- `id` not in `last_seen.escalated_review_ids`
|
|
23
|
+
|
|
24
|
+
A reply posted by the loop surfaces as an implicit review, but it always carries the reply as a line comment, so it can never be body-only — the body-only filter excludes it structurally, no marker check needed. Thread-level echo protection is intrinsic to the marker rule in [`unresolved-threads.md`](unresolved-threads.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Unresolved comment threads
|
|
2
2
|
|
|
3
|
-
For the resolve-reminder stage. GraphQL only — REST does not expose `isResolved`.
|
|
3
|
+
For the watcher's dispatch trigger and the resolve-reminder stage. GraphQL only — REST does not expose `isResolved`/`isOutdated`.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
gh api graphql -F owner=<owner> -F name=<repo> -F number=<n> -f query='
|
|
@@ -11,9 +11,11 @@ query($owner: String!, $name: String!, $number: Int!) {
|
|
|
11
11
|
nodes {
|
|
12
12
|
id
|
|
13
13
|
isResolved
|
|
14
|
+
isOutdated
|
|
14
15
|
comments(first: 100) {
|
|
15
16
|
nodes {
|
|
16
17
|
databaseId
|
|
18
|
+
pullRequestReview { databaseId }
|
|
17
19
|
author { login }
|
|
18
20
|
body
|
|
19
21
|
createdAt
|
|
@@ -29,7 +31,9 @@ query($owner: String!, $name: String!, $number: Int!) {
|
|
|
29
31
|
Filter client-side to `isResolved == false`. Walk each thread's comments in `createdAt` order and classify by the loop marker (see [`../pr-followup-helpers/loop-signature.md`](../pr-followup-helpers/loop-signature.md)), not by `author.login` — the login is ambiguous under a shared account:
|
|
30
32
|
|
|
31
33
|
- **Addressed, awaiting resolve** — the **newest** comment carries the loop marker `<!-- muggle-do:bot -->`. The loop has replied and nothing newer is waiting. → resolve-reminder.
|
|
32
|
-
- **Unaddressed human comment** — the newest comment lacks the marker and is newer than the thread's newest loop-marked comment (or the thread has no loop comment yet). → actionable: the round should address it.
|
|
34
|
+
- **Unaddressed human comment** — the newest comment lacks the marker and is newer than the thread's newest loop-marked comment (or the thread has no loop comment yet). → actionable: the round should address it. The **watcher's dispatch trigger** additionally requires `isOutdated == false` — a thread whose anchored line has since changed is skipped, since re-surfacing a stale anchor risks answering a concern the change already mooted. The resolve-reminder stage ignores `isOutdated`.
|
|
33
35
|
- **Not addressed** — indeterminate (e.g. no comments).
|
|
34
36
|
|
|
37
|
+
Each comment exposes its owning review as `pullRequestReview.databaseId` — the watcher collects this from an actionable thread's newest comment to build its dispatch list.
|
|
38
|
+
|
|
35
39
|
A loop comment also cites a `<short-sha>` from `last_seen.pushed_shas[]` in its body, which tells *which* push addressed the thread.
|
|
@@ -9,10 +9,10 @@ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`
|
|
|
9
9
|
| Recipe | Use case |
|
|
10
10
|
| :----- | :------- |
|
|
11
11
|
| [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch — watcher + bootstrap. |
|
|
12
|
-
| [`submitted-reviews`](github-cli-recipes/submitted-reviews.md) | Fetch
|
|
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. |
|
|
15
|
-
| [`unresolved-threads`](github-cli-recipes/unresolved-threads.md) | GraphQL
|
|
15
|
+
| [`unresolved-threads`](github-cli-recipes/unresolved-threads.md) | GraphQL unresolved-thread state — watcher's dispatch trigger + resolve-reminder. |
|
|
16
16
|
| [`reply-line-comment`](github-cli-recipes/reply-line-comment.md) | POST a threaded reply on a line comment. |
|
|
17
17
|
| [`top-level-comment`](github-cli-recipes/top-level-comment.md) | POST a top-level PR comment — resolve-reminder + overflow. |
|
|
18
18
|
| [`push-to-branch`](github-cli-recipes/push-to-branch.md) | Push + capture new SHA after address-reviews work. |
|
|
@@ -10,7 +10,7 @@ A review is a **self-loop** iff **every** line comment under it is a reply (`in_
|
|
|
10
10
|
|
|
11
11
|
If any comment in a reply-only wrapper **lacks** the marker, it is a **human follow-up** on an existing thread — not a self-loop. It carries reviewer intent; treat it as actionable and address it in this round (the caller's unresolved-thread sweep picks up the thread context).
|
|
12
12
|
|
|
13
|
-
Self-loops bypass the actionable/ambiguous decision entirely. Action:
|
|
13
|
+
Self-loops bypass the actionable/ambiguous decision entirely. Action: skip silently — a body-only echo folds its id into `lastBodyReviewId`; a line-comment echo already drops out of the actionable set via the marker. No push, no reply, no resolve-reminder, no escalation, no entry in `escalated_review_ids`. Telemetry: emit one `cycle` event with `outcome: "self-loop-skip"`.
|
|
14
14
|
|
|
15
15
|
Only reviews that survive the self-loop check proceed to classify below.
|
|
16
16
|
|
|
@@ -51,7 +51,7 @@ Reply shape (all replies for one review reference the same SHA):
|
|
|
51
51
|
| 1 comment: "won't this break the prod migration we did last week?" | Implicit change request gated on knowledge the loop can't access |
|
|
52
52
|
| Mixed: 2 concrete directives + 1 "rethink the whole approach" | The "rethink" subverts the others; escalate to confirm scope |
|
|
53
53
|
|
|
54
|
-
Escalate per the caller's procedure (add the review id to
|
|
54
|
+
Escalate per the caller's procedure (add the review id to `escalated_review_ids`, emit one terminal message, pause the PR).
|
|
55
55
|
|
|
56
56
|
## Borderline rule
|
|
57
57
|
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Echo protection (intrinsic under thread-state)
|
|
2
2
|
|
|
3
|
-
When `/muggle-do` posts a threaded reply to a review comment, GitHub surfaces that reply as a **new submitted review** under the same account
|
|
3
|
+
When `/muggle-do` posts a threaded reply to a review comment, GitHub surfaces that reply as a **new submitted review** under the same account, and the reply becomes the newest comment in its thread. The watcher must never read that as fresh feedback, or it replies to itself forever.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Under the thread-state dispatch trigger this is **intrinsic** — there is no "advance past the echo" step to get wrong:
|
|
6
6
|
|
|
7
|
-
A
|
|
7
|
+
- **Line-comment threads.** A thread is actionable only when its newest comment lacks the loop marker `<!-- muggle-do:bot -->` (see [`loop-signature.md`](loop-signature.md)). After the loop replies, the newest comment is the loop's own and carries the marker, so the thread drops out of the actionable set on its own.
|
|
8
|
+
- **Body-only reviews.** A loop reply always carries a line comment, so it is never body-only; the body-only check (no line comments, `id > lastBodyReviewId` — see [`../github-cli-recipes/submitted-reviews.md`](../github-cli-recipes/submitted-reviews.md)) excludes echoes structurally.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
1. Advance `last_seen.reviewId` past the echo's id (so it is not seen again), and
|
|
12
|
-
2. **Skip it** — never dispatch `/muggle-do` for it.
|
|
13
|
-
|
|
14
|
-
## Detection
|
|
15
|
-
|
|
16
|
-
Classify by the marker, never by `author.login` — under a shared account the loop posts as the PR author, so the login cannot tell echo from human. Fetch the review's comments; if the set is non-empty and every comment body contains `<!-- muggle-do:bot -->`, it is an echo. A review with at least one marker-less comment is human feedback and must be processed normally.
|
|
10
|
+
Classify by the marker, never by `author.login` — under a shared account the loop posts as the PR author, so the login cannot tell echo from human.
|
|
@@ -37,4 +37,4 @@ fix(ci): lint — remove unused import
|
|
|
37
37
|
|
|
38
38
|
- Never post a top-level comment in reply to a line-level comment. It loses thread context.
|
|
39
39
|
- Never `gh pr review --comment` for replies — that endpoint is for *new* reviews.
|
|
40
|
-
- Never reply twice to the same comment. The
|
|
40
|
+
- Never reply twice to the same comment. The loop marker on each posted reply is the re-entry guard — a thread whose newest comment is loop-marked is no longer actionable, so the next round won't re-reply.
|
|
@@ -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.
|
|
@@ -9,8 +9,10 @@ One per watcher iteration (idle or not).
|
|
|
9
9
|
"session_slug": "<slug>",
|
|
10
10
|
"repo": "<owner>/<repo>",
|
|
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,
|
|
@@ -19,9 +21,11 @@ One per watcher iteration (idle or not).
|
|
|
19
21
|
}
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
- `
|
|
23
|
-
- `dispatched_review_ids`: review ids handed to `/muggle-do`. Empty when idle.
|
|
24
|
-
- `
|
|
24
|
+
- `actionable_threads`: count of actionable items this tick — unresolved, non-outdated threads whose newest comment is unmarked, plus body-only reviews past `lastBodyReviewId` — **after** filtering by the escalated set.
|
|
25
|
+
- `dispatched_review_ids`: owning review ids handed to `/muggle-do`. Empty when idle.
|
|
26
|
+
- `rebase_needed`: true when the branch is behind (`BEHIND`) or conflicting (`DIRTY`/`CONFLICTING`) with its base. `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
|
-
- `idle`: true when
|
|
31
|
+
- `idle`: true when nothing was dispatched this tick.
|
|
@@ -13,16 +13,16 @@ The entry procedure for `/muggle-do`'s **address-reviews** mode — invoked by t
|
|
|
13
13
|
`$ARGUMENTS` carries:
|
|
14
14
|
- PR URL: `<owner>/<repo>#<n>` derivable from the URL.
|
|
15
15
|
- Session slug: `<slug>`.
|
|
16
|
-
-
|
|
16
|
+
- Owning review ids: one or more integers (the reviews whose actionable threads or body-only feedback the watcher flagged).
|
|
17
17
|
|
|
18
|
-
Exact phrasing comes from the watcher's dispatch (see [`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md
|
|
18
|
+
Exact phrasing comes from the watcher's dispatch (see [`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md)). Parse all three out of the directive text.
|
|
19
19
|
|
|
20
20
|
## Inputs from disk
|
|
21
21
|
|
|
22
22
|
Read from `~/.muggle-ai/muggle-do/sessions/<slug>/`:
|
|
23
23
|
|
|
24
24
|
- `prs.json` — to locate the PR's local checkout path (the `repo` field maps to a configured local repo) and capture `head_sha_before`.
|
|
25
|
-
- `last_seen.json` — for `pushed_shas[]` (used by the resolve-reminder stage) and to update
|
|
25
|
+
- `last_seen.json` — for `pushed_shas[]` (used by the resolve-reminder stage) and to update `lastBodyReviewId`.
|
|
26
26
|
- `state.md` — for the cached `loop_user` login (used by resolve-reminder thread classification).
|
|
27
27
|
|
|
28
28
|
## Procedure
|
|
@@ -37,10 +37,10 @@ Two sources, combined into one batch (dedupe by comment id):
|
|
|
37
37
|
|
|
38
38
|
**(a) The dispatched reviews.** For each review id in the input:
|
|
39
39
|
|
|
40
|
-
- Fetch reviews per [`../_shared/github-cli-recipes/submitted-reviews.md`](../_shared/github-cli-recipes/submitted-reviews.md) (
|
|
40
|
+
- Fetch reviews per [`../_shared/github-cli-recipes/submitted-reviews.md`](../_shared/github-cli-recipes/submitted-reviews.md) (no watermark; filter to the specific id).
|
|
41
41
|
- Fetch its line comments per [`../_shared/github-cli-recipes/line-comments-for-review.md`](../_shared/github-cli-recipes/line-comments-for-review.md).
|
|
42
42
|
|
|
43
|
-
**(b) Unaddressed comments on every unresolved thread.** Fetch unresolved threads per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md). For each thread classified **unaddressed human comment** — newest comment lacks the loop marker `<!-- muggle-do:bot -->` ([`loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)) and post-dates the loop's last marked reply — add it to the batch,
|
|
43
|
+
**(b) Unaddressed comments on every unresolved thread.** Fetch unresolved threads per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md). For each thread classified **unaddressed human comment** — newest comment lacks the loop marker `<!-- muggle-do:bot -->` ([`loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)) and post-dates the loop's last marked reply — add it to the batch — unresolved thread state, not any review-id watermark, is the authority here. This is how a human thread follow-up (a marker-less reply) gets addressed. **Exclude** comments whose review id is in `last_seen.escalated_review_ids` — paused awaiting the user, not re-work.
|
|
44
44
|
|
|
45
45
|
Group (a) and (b) into one combined batch.
|
|
46
46
|
|
|
@@ -101,7 +101,7 @@ Invoke [`per-comment-replies.md`](per-comment-replies.md) with the actionable re
|
|
|
101
101
|
|
|
102
102
|
- `last_seen.cycles_completed` += 1
|
|
103
103
|
- `last_seen.last_pushed_sha` = the new head SHA (update.md already wrote this; verify)
|
|
104
|
-
- `last_seen.
|
|
104
|
+
- `last_seen.lastBodyReviewId` = max(body-only input review ids ∪ last_seen.lastBodyReviewId) — line-comment threads need no watermark; they fall out of the actionable set once the per-comment reply carries the loop marker.
|
|
105
105
|
|
|
106
106
|
### Step 5.5 — Resolve-reminder (runs every round)
|
|
107
107
|
|
|
@@ -145,5 +145,5 @@ Do **not** push, do **not** post replies, do **not** run resolve-reminder. The c
|
|
|
145
145
|
## Invariants
|
|
146
146
|
|
|
147
147
|
- One `/muggle-do` invocation = at most one push and one resolve-reminder, regardless of how many reviews are in the batch.
|
|
148
|
-
- Every input review id ends up
|
|
148
|
+
- Every input review id ends up either handled (its thread answered with a loop-marked reply, or — for a body-only review — folded into `lastBodyReviewId`) or in `escalated_review_ids` (skipped) — never both, never neither.
|
|
149
149
|
- The watcher is respawned exactly when the PR is still open at the end of the cycle.
|
|
@@ -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`.
|
|
@@ -46,7 +46,7 @@ Write `~/.muggle-ai/muggle-do/sessions/<slug>/prs.json` per [`../../muggle-pr-fo
|
|
|
46
46
|
[{ "repo": "owner/repo", "number": 142, "url": "...", "head_sha": "...", "state": "open" }]
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Seed `~/.muggle-ai/muggle-do/sessions/<slug>/last_seen.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#last_seenjson) — empty
|
|
49
|
+
Seed `~/.muggle-ai/muggle-do/sessions/<slug>/last_seen.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#last_seenjson) — empty-watermark shape with `pushed_shas: []`. Forward mode never has prior reviews to skip, so `lastBodyReviewId: 0`.
|
|
50
50
|
|
|
51
51
|
**Do not** seed `cycle.json` or `requirements.md`. The watcher is a dumb pipe; `/muggle-do` reads reviews off GitHub.
|
|
52
52
|
|
|
@@ -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
|
---
|
|
2
2
|
name: muggle-feedback
|
|
3
|
-
description: Use when the user wants to flag that a generated Muggle Test action script — or one specific step in it — did the wrong thing,
|
|
3
|
+
description: "Use when the user wants to flag that a generated Muggle Test action script — or one specific step in it — did the wrong thing, so Muggle can analyze and regenerate affected scripts. Covers any report that a script or step is wrong, broken, or misbehaving (clicked the wrong element, step N did not work, broken at the submit step), whether from a recent local/remote run or a pasted Muggle dashboard run/script URL. Also lists, views, or deletes previously submitted feedback. Triggers on /muggle-feedback, give feedback on this run, the test was wrong, flag this run."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Muggle Test Feedback
|
|
@@ -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
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: muggle-pr-followup
|
|
3
|
-
description: Use
|
|
3
|
+
description: "Use when the user wants a pull request's incoming review feedback handled for them — it watches one PR's review thread and, each time a reviewer submits new comments, dispatches the work to address them. Engage on PR-review-follow-up intent: \"watch my PR and address review comments as they come in\", \"keep an eye on PR #123 and respond to reviewer feedback\", \"babysit my PR's review thread\", \"I'm stepping away — handle my PR's reviews while I'm gone\". This is PR-review-specific automation — prefer it over the generic loop skill. It only watches and dispatches; the actual edits and replies are muggle-do. Not for posting test results to a PR (use muggle-pr-visual-walkthrough)."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# muggle-pr-followup
|
|
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
|
|
|
@@ -34,7 +34,7 @@ Bootstrap accepts three optional trailing flags:
|
|
|
34
34
|
|
|
35
35
|
- `--slug=<name>` — override the default `<repo>-pr<n>` slug
|
|
36
36
|
- `--resume` — opt in to reusing an existing session slot (default is refuse on conflict)
|
|
37
|
-
- `--forward-only` — pin
|
|
37
|
+
- `--forward-only` — pin `lastBodyReviewId` past existing **body-only** reviews (skip history on those). Line-comment threads are always picked up from live thread state, regardless of this flag.
|
|
38
38
|
|
|
39
39
|
## Preferences
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ For each PR URL in the track list, run the [`bootstrap.md`](bootstrap.md) proced
|
|
|
51
51
|
- **Existing slot → skip silently** (never the slot-conflict abort); add it to the *skipped* list.
|
|
52
52
|
- **`caller = "auto-track"`** in the bootstrap telemetry event.
|
|
53
53
|
|
|
54
|
-
Everything else is unchanged: URL parse, metadata + terminal-PR abort, slug,
|
|
54
|
+
Everything else is unchanged: URL parse, metadata + terminal-PR abort, slug, `lastBodyReviewId` 0 (line-comment threads are picked up live from thread state; body-only reviews from id 0), and the `prs.json`/`last_seen.json`/`state.md` writes minus the pre-flight block.
|
|
55
55
|
|
|
56
56
|
### Step 5 — Print the summary
|
|
57
57
|
|
|
@@ -17,7 +17,7 @@ Bootstrap asks **one** questionnaire — the E2E validation context the loop wil
|
|
|
17
17
|
- `<pr-url>` matches `https?://github\.com/[^/]+/[^/]+/pull/\d+` — required.
|
|
18
18
|
- `--slug=<name>` overrides the default `<repo>-pr<n>` slug.
|
|
19
19
|
- `--resume` opts into refreshing an existing slot instead of refusing on conflict.
|
|
20
|
-
- `--forward-only` pins
|
|
20
|
+
- `--forward-only` pins `lastBodyReviewId` past existing **body-only** reviews (skip history on those). It does **not** affect line-comment threads — those are always picked up from live thread state. Default is `0`.
|
|
21
21
|
|
|
22
22
|
## Procedure
|
|
23
23
|
|
|
@@ -46,12 +46,14 @@ Default: `<repo>-pr<n>` (e.g. `muggle-ai-works-pr154`). Override: `--slug=<name>
|
|
|
46
46
|
If `~/.muggle-ai/muggle-do/sessions/<slug>/` exists (including a slot just migrated above):
|
|
47
47
|
|
|
48
48
|
- Without `--resume` → exit with the slot-conflict abort. Both remedies (delete + re-run, or pass `--resume`) are spelled out in the message.
|
|
49
|
-
- With `--resume` → refresh `prs.json[0].head_sha` to the current `headRefOid` from Step 2; leave `last_seen.json`
|
|
49
|
+
- With `--resume` → refresh `prs.json[0].head_sha` to the current `headRefOid` from Step 2; leave `last_seen.json` untouched. If `state.md` already has a `## Pre-flight answers` block, skip to Step 8; if not (older session), run Step 6.5 to backfill it, then skip to Step 8.
|
|
50
50
|
|
|
51
|
-
### Step 6 — Resolve the
|
|
51
|
+
### Step 6 — Resolve the body-only watermark
|
|
52
52
|
|
|
53
|
-
-
|
|
54
|
-
|
|
53
|
+
Line-comment threads need no seeding — the watcher derives them from live thread state on every tick, so existing unresolved threads are picked up on the first tick regardless of this step. This step only sets `lastBodyReviewId`, the narrow watermark for body-only reviews (a submitted review with no line comments).
|
|
54
|
+
|
|
55
|
+
- **Default (no `--forward-only`):** `lastBodyReviewId = 0`. The watcher picks up every existing body-only review on its first tick. Matches the common case — the user opened the PR, left feedback they want addressed, and is now bootstrapping.
|
|
56
|
+
- **With `--forward-only`:** fetch reviews per [`../_shared/github-cli-recipes/submitted-reviews.md`](../_shared/github-cli-recipes/submitted-reviews.md), then take `max(id)`. Body-only reviews at or below that id are treated as already-handled. This no longer hides existing line-comment threads — those are always picked up from thread state.
|
|
55
57
|
|
|
56
58
|
### Step 6.5 — Resolve E2E validation context
|
|
57
59
|
|
|
@@ -67,7 +69,7 @@ Write under `~/.muggle-ai/muggle-do/sessions/<slug>/`:
|
|
|
67
69
|
|
|
68
70
|
**`prs.json`** — see [`state-schemas.md`](state-schemas.md#prsjson). One entry, `state` = `"open"`, `head_sha` from Step 2's `headRefOid`.
|
|
69
71
|
|
|
70
|
-
**`last_seen.json`** — see [`state-schemas.md`](state-schemas.md#last_seenjson). One key (`"<owner>/<repo>#<n>"`), `
|
|
72
|
+
**`last_seen.json`** — see [`state-schemas.md`](state-schemas.md#last_seenjson). One key (`"<owner>/<repo>#<n>"`), `lastBodyReviewId` from Step 6, `last_pushed_sha: null`, `idle_tick_count: 0`, `cycles_completed: 0`, `escalated_review_ids: []`, `pushed_shas: []`.
|
|
71
73
|
|
|
72
74
|
**`state.md`** — see [`state-schemas.md`](state-schemas.md#statemd). `Bootstrapped from URL: yes`. Cache the loop-user login. Append the `## Pre-flight answers` block with the fields resolved in Step 6.5, per [`../_shared/resolve-e2e-validation-context.md`](../_shared/resolve-e2e-validation-context.md#persisted-fields).
|
|
73
75
|
|