@muggleai/works 4.11.4 → 4.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/{chunk-E52757MK.js → chunk-2DVZ2LYO.js} +5 -0
  2. package/dist/cli.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  5. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  6. package/dist/plugin/scripts/ensure-electron-app.sh +29 -4
  7. package/dist/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
  8. package/dist/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
  9. package/dist/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
  10. package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
  11. package/dist/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
  12. package/dist/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
  13. package/dist/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
  14. package/dist/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
  15. package/dist/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
  16. package/dist/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
  17. package/dist/plugin/skills/_shared/github-cli-recipes.md +20 -0
  18. package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
  19. package/dist/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
  20. package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
  21. package/dist/plugin/skills/_shared/pr-followup-helpers.md +8 -146
  22. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
  23. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
  24. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
  25. package/dist/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
  26. package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
  27. package/dist/plugin/skills/_shared/telemetry-events.md +15 -0
  28. package/dist/plugin/skills/do/address-reviews.md +137 -0
  29. package/dist/plugin/skills/do/build.md +5 -5
  30. package/dist/plugin/skills/do/open-prs/forward.md +80 -0
  31. package/dist/plugin/skills/do/open-prs/update.md +54 -0
  32. package/dist/plugin/skills/do/open-prs.md +6 -102
  33. package/dist/plugin/skills/do/per-comment-replies.md +61 -0
  34. package/dist/plugin/skills/do/resolve-reminder.md +70 -0
  35. package/dist/plugin/skills/muggle-do/SKILL.md +41 -45
  36. package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
  37. package/dist/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
  38. package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
  39. package/dist/plugin/skills/muggle-pr-followup/contract.md +44 -161
  40. package/dist/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
  41. package/dist/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
  42. package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
  43. package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
  44. package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
  45. package/dist/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
  46. package/dist/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
  47. package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
  48. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
  49. package/dist/release-manifest.json +4 -4
  50. package/package.json +106 -106
  51. package/plugin/.claude-plugin/plugin.json +1 -1
  52. package/plugin/.cursor-plugin/plugin.json +1 -1
  53. package/plugin/scripts/ensure-electron-app.sh +29 -4
  54. package/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
  55. package/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
  56. package/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
  57. package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
  58. package/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
  59. package/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
  60. package/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
  61. package/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
  62. package/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
  63. package/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
  64. package/plugin/skills/_shared/github-cli-recipes.md +20 -0
  65. package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
  66. package/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
  67. package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
  68. package/plugin/skills/_shared/pr-followup-helpers.md +8 -146
  69. package/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
  70. package/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
  71. package/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
  72. package/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
  73. package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
  74. package/plugin/skills/_shared/telemetry-events.md +15 -0
  75. package/plugin/skills/do/address-reviews.md +137 -0
  76. package/plugin/skills/do/build.md +5 -5
  77. package/plugin/skills/do/open-prs/forward.md +80 -0
  78. package/plugin/skills/do/open-prs/update.md +54 -0
  79. package/plugin/skills/do/open-prs.md +6 -102
  80. package/plugin/skills/do/per-comment-replies.md +61 -0
  81. package/plugin/skills/do/resolve-reminder.md +70 -0
  82. package/plugin/skills/muggle-do/SKILL.md +41 -45
  83. package/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
  84. package/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
  85. package/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
  86. package/plugin/skills/muggle-pr-followup/contract.md +44 -161
  87. package/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
  88. package/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
  89. package/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
  90. package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
  91. package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
  92. package/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
  93. package/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
  94. package/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
  95. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
  96. package/scripts/postinstall.mjs +36 -5
@@ -0,0 +1,20 @@
1
+ # `muggle-pr-followup:bootstrap`
2
+
3
+ One per successful bootstrap, after state seeding and before the first watcher dispatches.
4
+
5
+ ```json
6
+ {
7
+ "skill": "muggle-pr-followup",
8
+ "event": "bootstrap",
9
+ "caller": "<caller-name>",
10
+ "session_slug": "<slug>",
11
+ "repo": "<owner>/<repo>",
12
+ "pr_number": <int>,
13
+ "cursor_review_id": <int>,
14
+ "resume": true | false
15
+ }
16
+ ```
17
+
18
+ - `caller`: `"user"` for direct invocation; another skill name if relayed.
19
+ - `cursor_review_id`: `0` if no prior submitted reviews; otherwise the highest existing review id.
20
+ - `resume`: true when `--resume` was used.
@@ -0,0 +1,23 @@
1
+ # `muggle-pr-followup:tick`
2
+
3
+ One per watcher iteration (idle or not).
4
+
5
+ ```json
6
+ {
7
+ "skill": "muggle-pr-followup",
8
+ "event": "tick",
9
+ "session_slug": "<slug>",
10
+ "repo": "<owner>/<repo>",
11
+ "pr_number": <int>,
12
+ "reviews_seen": <int>,
13
+ "dispatched_review_ids": [<int>, ...],
14
+ "terminal": true | false,
15
+ "idle": true | false,
16
+ "tick_duration_ms": <int>
17
+ }
18
+ ```
19
+
20
+ - `reviews_seen`: count of new submitted reviews past the cursor, **after** filtering by the escalated set.
21
+ - `dispatched_review_ids`: review ids handed to `/muggle-do`. Empty when idle.
22
+ - `terminal`: true when this tick observed the PR merged or closed and wrote `result.md`.
23
+ - `idle`: true when no reviews were dispatched this tick.
@@ -0,0 +1,15 @@
1
+ # Telemetry Event Catalog
2
+
3
+ Canonical shapes for `muggle-local-telemetry-event-emit` events. Emission mechanics: [`telemetry-emit.md`](telemetry-emit.md).
4
+
5
+ All events share two top-level fields: `skill` and `event`. The rest is event-specific — read only the file for the event you're emitting.
6
+
7
+ ## Index
8
+
9
+ | Event | When |
10
+ | :---- | :--- |
11
+ | [`muggle-pr-followup:tick`](telemetry-events/pr-followup-tick.md) | Every watcher iteration (idle or not). |
12
+ | [`muggle-pr-followup:bootstrap`](telemetry-events/pr-followup-bootstrap.md) | Successful bootstrap, before first watcher dispatches. |
13
+ | [`muggle-do:cycle`](telemetry-events/muggle-do-cycle.md) | Every address-reviews invocation, regardless of outcome. |
14
+ | [`muggle-do:escalation`](telemetry-events/muggle-do-escalation.md) | When `/muggle-do` emits a terminal escalation message. |
15
+ | [`muggle-do:resolve-reminder`](telemetry-events/muggle-do-resolve-reminder.md) | After the resolve-reminder stage scans threads. |
@@ -0,0 +1,137 @@
1
+ # Address-Reviews Orchestrator
2
+
3
+ The entry procedure for `/muggle-do`'s **address-reviews** mode — invoked by the watcher loop ([`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md)) when new submitted reviews land on a PR. Orchestrates the cycle: read reviews → classify → execute work on actionables → escalate ambiguous → push and refresh PR → reply per comment → resolve-reminder → respawn watcher.
4
+
5
+ ## Turn preamble
6
+
7
+ ```
8
+ **/muggle-do address-reviews** — handling <count> review(s) on <owner>/<repo>#<n>.
9
+ ```
10
+
11
+ ## Input
12
+
13
+ `$ARGUMENTS` carries:
14
+ - PR URL: `<owner>/<repo>#<n>` derivable from the URL.
15
+ - Session slug: `<slug>`.
16
+ - List of review ids: one or more integers.
17
+
18
+ Exact phrasing comes from the watcher's dispatch (see [`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md#step-5-if-one-or-more-new-reviews-dispatch)). Parse all three out of the directive text.
19
+
20
+ ## Inputs from disk
21
+
22
+ Read from `.muggle-do/sessions/<slug>/`:
23
+
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 the cursor.
26
+ - `state.md` — for the cached `loop_user` login (used by resolve-reminder thread classification).
27
+
28
+ ## Procedure
29
+
30
+ ### Step 1 — Read every listed review off GitHub
31
+
32
+ For each review id in the input:
33
+
34
+ - Fetch reviews per [`../_shared/github-cli-recipes/submitted-reviews.md`](../_shared/github-cli-recipes/submitted-reviews.md) (cursor 0; filter to the specific id).
35
+ - Fetch its line comments per [`../_shared/github-cli-recipes/line-comments-for-review.md`](../_shared/github-cli-recipes/line-comments-for-review.md).
36
+
37
+ Group into one combined batch.
38
+
39
+ ### Step 2 — Classify each review
40
+
41
+ Apply the classify rule in [`../_shared/pr-followup-helpers/classify.md`](../_shared/pr-followup-helpers/classify.md). Two outcomes per review:
42
+
43
+ - **Actionable** — at least one concrete change request, or an answerable question with a target.
44
+ - **Ambiguous** — no actionable signal.
45
+
46
+ Build two sets: `actionable_review_ids` and `ambiguous_review_ids`. Their union is the input list.
47
+
48
+ ### Step 3 — Handle ambiguous (if any)
49
+
50
+ For each id in `ambiguous_review_ids`:
51
+
52
+ 1. Append it to `last_seen.escalated_review_ids` so the watcher won't re-dispatch it.
53
+
54
+ Emit **one** terminal escalation message (not one per ambiguous review) per [`../muggle-pr-followup/output-templates/escalation.md`](../muggle-pr-followup/output-templates/escalation.md) (ambiguous template). The message lists every ambiguous review and its comments inline. Emit an event with `kind: "ambiguous-review"` per [`../_shared/telemetry-events/muggle-do-escalation.md`](../_shared/telemetry-events/muggle-do-escalation.md).
55
+
56
+ The user clarifies on GitHub by submitting a new review. The next watcher tick picks it up.
57
+
58
+ ### Step 4 — Handle actionables (if any)
59
+
60
+ If `actionable_review_ids` is empty, skip Steps 4 and 5; proceed to Step 6 (cursor + respawn). Otherwise:
61
+
62
+ #### 4a. Flatten the work
63
+
64
+ The actionable reviews together carry a flat list of change items, one per line comment plus any directive in the review body. Each item is design, code logic, or test in nature; the cycle does **not** treat each item as a separate cycle iteration — it plans them together and runs the work **once** for the whole batch.
65
+
66
+ #### 4b. Apply design + code-logic changes
67
+
68
+ Invoke [`build.md`](build.md) with the review-derived requirements: for each comment, what to change and where. `build.md` makes the edits in the working tree but does not commit.
69
+
70
+ If `build.md` reports that the requested work requires rethinking the design (e.g. the requested change conflicts with a load-bearing invariant), exit early per Step 7 (design-adjustment escalation).
71
+
72
+ #### 4c. Create or update unit tests
73
+
74
+ Invoke [`unit-tests.md`](unit-tests.md). Cover the surface that just changed; respect existing test conventions.
75
+
76
+ #### 4d. Run ONE E2E acceptance pass
77
+
78
+ Invoke [`e2e-acceptance.md`](e2e-acceptance.md). One pass covering all related test cases for this PR, not one pass per comment. Use the `autoE2ETest` gate per its usual contract.
79
+
80
+ #### 4e. Create or update the PR
81
+
82
+ Invoke [`open-prs/update.md`](open-prs/update.md) (pass the PR URL + slug + existing PR number). It pushes, refreshes title/description on state change, posts a fresh walkthrough comment, and appends the new SHA to `last_seen.pushed_shas[]`. Capture the new `head_sha`.
83
+
84
+ #### 4f. Post per-comment inline replies
85
+
86
+ Invoke [`per-comment-replies.md`](per-comment-replies.md) with the actionable reviews and the new SHA. One reply per comment, in its own thread, describing what was done for it.
87
+
88
+ #### 4g. Run the resolve-reminder stage
89
+
90
+ Invoke [`resolve-reminder.md`](resolve-reminder.md). Scans unresolved threads, classifies, and posts a top-level PR comment listing addressed-by-loop thread ids.
91
+
92
+ ### Step 5 — Update session state
93
+
94
+ - `last_seen.cycles_completed` += 1
95
+ - `last_seen.last_pushed_sha` = the new head SHA (update.md already wrote this; verify)
96
+ - `last_seen.reviewId` = max(input review ids ∪ last_seen.reviewId)
97
+
98
+ ### Step 6 — Respawn the watcher
99
+
100
+ Refresh PR state per [`../_shared/github-cli-recipes/pr-metadata.md`](../_shared/github-cli-recipes/pr-metadata.md). If the PR is now merged or closed:
101
+
102
+ 1. Write `result.md` per [`../muggle-pr-followup/state-schemas.md`](../muggle-pr-followup/state-schemas.md#resultmd).
103
+ 2. Do **not** respawn the watcher.
104
+
105
+ Otherwise, dispatch the next watcher as the last action of this turn:
106
+
107
+ ```
108
+ /loop 1m /muggle:muggle-pr-followup <slug> <n>
109
+ ```
110
+
111
+ ### Step 7 — Telemetry
112
+
113
+ Emit one event per [`../_shared/telemetry-events/muggle-do-cycle.md`](../_shared/telemetry-events/muggle-do-cycle.md). `outcome` is one of:
114
+
115
+ - `"pushed"` — actionables ran and at least one push succeeded.
116
+ - `"escalated"` — only ambiguous; no push.
117
+ - `"mixed"` — both branches ran.
118
+ - `"no-op"` — every input id was already in `escalated_review_ids` (the watcher shouldn't have dispatched, but defensive coverage).
119
+
120
+ Emit additional events as Steps fired them (escalation event in Step 3; resolve-reminder event in Step 4g).
121
+
122
+ ## Design-adjustment escalation (Step 4b early exit)
123
+
124
+ When `build.md` returns `failed: design-adjustment`:
125
+
126
+ 1. Append the affected review ids to `last_seen.escalated_review_ids`.
127
+ 2. Emit one terminal message per [`../muggle-pr-followup/output-templates/escalation.md`](../muggle-pr-followup/output-templates/escalation.md) (design-adjustment template).
128
+ 3. Emit the `escalation` telemetry event with `kind: "design-adjustment"`.
129
+ 4. Skip to Step 6 (respawn watcher). The watcher continues polling — the user can override the design conflict by submitting a new review.
130
+
131
+ Do **not** push, do **not** post replies, do **not** run resolve-reminder. The cycle ended on a design conflict; the work was not applied.
132
+
133
+ ## Invariants
134
+
135
+ - One `/muggle-do` invocation = at most one push and one resolve-reminder, regardless of how many reviews are in the batch.
136
+ - Every input review id ends up in either the cursor (handled) or `escalated_review_ids` (skipped) — never both, never neither.
137
+ - The watcher is respawned exactly when the PR is still open at the end of the cycle.
@@ -41,11 +41,11 @@ Per repo:
41
41
 
42
42
  If a requirement is fundamentally unimplementable as written, halt and escalate with the specific blocker — do not ship a half-finished implementation.
43
43
 
44
- ## Re-entry from stage 8
44
+ ## Re-entry from the address-reviews flow
45
45
 
46
- Stage 8 (PR follow-up) may dispatch back to this stage when a reviewer comment requires real implementation work rather than an in-place doc edit. When re-entered:
46
+ The address-reviews orchestrator ([`address-reviews.md`](address-reviews.md)) invokes this stage when reviewers submit comments that require code/design changes. When re-entered:
47
47
 
48
- - The dispatch from stage 8 carries the comment(s) that triggered the re-build as additional context; treat them as amendments to the goal/AC for this iteration.
48
+ - The orchestrator passes the actionable reviews' bodies + line comments as the requirements amendment for this iteration. Treat them as additions to the goal/AC.
49
49
  - Continue on the existing branch — do not re-create the worktree.
50
- - Cycle forward through impact analysis → unit tests → E2E → open PR (which is a no-op since the PR already exists; just push).
51
- - Stage 8 resumes polling after the push lands.
50
+ - After this stage, the orchestrator runs unit-tests → ONE E2E pass → create-or-update PR (push to the existing branch; refresh title/desc if state changed) → per-comment inline replies → resolve-reminder → respawn the watcher.
51
+ - If the requested work cannot be implemented without rethinking design (e.g. a load-bearing invariant must change), return `failed: design-adjustment` and let the orchestrator escalate via the design-adjustment terminal message. Do not partially implement.
@@ -0,0 +1,80 @@
1
+ # Open PR — forward mode
2
+
3
+ Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fresh feature. Creates the PR via `gh pr create`, seeds session state, dispatches the first watcher.
4
+
5
+ ## Turn preamble
6
+
7
+ ```
8
+ **Stage 7 — Create PR** — pushing the branch and opening the PR.
9
+ ```
10
+
11
+ ## Inputs
12
+
13
+ - Per-repo: name, path, branch.
14
+ - Requirements: goal, acceptance criteria.
15
+ - **Optional** E2E acceptance report from stage 6. Produced by [`../e2e-acceptance.md`](../e2e-acceptance.md); schema in [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md).
16
+
17
+ ## Per repo
18
+
19
+ 0. **`autoCreatePR` gate** — apply per [`../../muggle-preferences/preference-gates/autoCreatePR.md`](../../muggle-preferences/preference-gates/autoCreatePR.md). On skip, record the reason in `result.md` and move on.
20
+
21
+ 1. **Push:** `git push -u origin <branch>` in the repo directory.
22
+
23
+ 2. **Title** (under 70 chars):
24
+ - E2E report exists and has failures → `[E2E FAILING] <goal>`
25
+ - No E2E report (validation was `unit-only` or `skip`) → `[UNVERIFIED] <goal>` or `[UNIT-ONLY] <goal>` to match the validation strategy
26
+ - Otherwise → `<goal>`
27
+
28
+ 3. **Body** — assemble in order:
29
+ - `## Goal` — from requirements.
30
+ - `## Acceptance Criteria` — bulleted; omit if empty.
31
+ - `## Changes` — summary of what changed in this repo.
32
+ - `## Validation` — one line: link to E2E report, `unit-only`, or `skip — <reason>`.
33
+ - **If an E2E report exists,** invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode B to render the walkthrough block. Embed the returned `body` verbatim. If no report, skip this block entirely.
34
+
35
+ 4. **Create:** `gh pr create --title "..." --body "..." --head <branch>`. Capture the PR URL and number.
36
+
37
+ 5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once per [`../../_shared/github-cli-recipes/top-level-comment.md`](../../_shared/github-cli-recipes/top-level-comment.md). Never post when `comment` is `null`.
38
+
39
+ ## Stage 8 handoff
40
+
41
+ After every repo is processed, build the watcher manifest and dispatch one watcher loop per opened PR. The dispatches are the LAST action this stage takes.
42
+
43
+ Write `.muggle-do/sessions/<slug>/prs.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#prsjson):
44
+
45
+ ```json
46
+ [{ "repo": "owner/repo", "number": 142, "url": "...", "head_sha": "...", "state": "open" }]
47
+ ```
48
+
49
+ Seed `.muggle-do/sessions/<slug>/last_seen.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#last_seenjson) — empty cursor shape with `pushed_shas: []`. Forward mode never has prior reviews to skip, so `reviewId: 0`.
50
+
51
+ **Do not** seed `cycle.json` or `requirements.md`. The watcher is a dumb pipe; `/muggle-do` reads reviews off GitHub.
52
+
53
+ For each entry in `prs.json`, dispatch its watcher:
54
+ ```
55
+ /loop 1m /muggle:muggle-pr-followup <slug> <pr-number>
56
+ ```
57
+
58
+ If `prs.json` is empty, **do not dispatch** — record the reason in `result.md` and exit.
59
+
60
+ ## Invariants
61
+
62
+ - PR creation per non-skipped repo; walkthrough block via Mode B; `prs.json`+`last_seen.json` seeded (no `cycle.json`, no `requirements.md`); `/loop` dispatch is the last action.
63
+
64
+ ## Output
65
+
66
+ **PRs Created:** repo → URL
67
+ **Skipped:** repo → reason (when `autoCreatePR` short-circuited)
68
+ **Overflow comments posted:** repo → PR #
69
+ **Stage 8:** `Watching <N> PR(s) — one /loop 1m /muggle:muggle-pr-followup <slug> <pr#> per PR` | `No PRs to watch — stage 8 not dispatched`
70
+ **Errors:** repo → message
71
+
72
+ ## Post-merge cleanup
73
+
74
+ Gated by `autoCleanup`. Fires in a follow-up turn after merge — never from this stage. See [`../../_shared/post-merge-cleanup.md`](../../_shared/post-merge-cleanup.md).
75
+
76
+ Append one short reminder tied to the gate value:
77
+
78
+ - `always` → `Once merged, I'll run the cleanup sequence automatically.`
79
+ - `never` → omit.
80
+ - `ask` / absent → `Once merged, I'll check with you about cleanup.`
@@ -0,0 +1,54 @@
1
+ # Update PR — address-reviews mode
2
+
3
+ Invoked by `/muggle-do`'s address-reviews orchestrator after stages 3–6 ran on the existing PR. The PR already exists; this stage pushes the branch, refreshes title/description if state changed, and posts a fresh walkthrough.
4
+
5
+ Does **not** create a PR, seed session state, or dispatch a watcher (`/muggle-do` respawns the watcher at the end of its address-reviews cycle).
6
+
7
+ ## Turn preamble
8
+
9
+ ```
10
+ **Stage 7 — Update PR** — pushing the branch and refreshing the PR.
11
+ ```
12
+
13
+ ## Inputs
14
+
15
+ - Per-repo: path, branch (head ref name).
16
+ - The existing PR URL + number from `.muggle-do/sessions/<slug>/prs.json`.
17
+ - **Optional** E2E acceptance report from stage 6. Produced by [`../e2e-acceptance.md`](../e2e-acceptance.md); schema in [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md).
18
+
19
+ ## Procedure
20
+
21
+ Skip `autoCreatePR` (it gates creation, not update). The PR's title is left intact unless state changed in Step 3.
22
+
23
+ 1. **Push:** per [`../../_shared/github-cli-recipes/push-to-branch.md`](../../_shared/github-cli-recipes/push-to-branch.md). Capture the new SHA.
24
+
25
+ 2. **Append new SHA** to `last_seen.json[<key>].pushed_shas` (the resolve-reminder stage uses this to recognize threads addressed by the loop). Set `last_seen.last_pushed_sha` to the new SHA too.
26
+
27
+ 3. **Refresh title if state changed.** Compare the new state against the current PR title prefix:
28
+ - E2E now passing, current title has `[E2E FAILING]` → strip the prefix per [`../../_shared/github-cli-recipes/pr-edit.md`](../../_shared/github-cli-recipes/pr-edit.md).
29
+ - E2E now failing, current title has no `[E2E FAILING]` → add the prefix.
30
+ - Validation now ran (was unit-only/skip, now has E2E report) → strip `[UNVERIFIED]` or `[UNIT-ONLY]`.
31
+ - Otherwise → no title change.
32
+
33
+ 4. **Refresh body when validation outcome changed** — only when the `## Validation` section's content differs from what's in the body. Use the `--body-file` form in [`../../_shared/github-cli-recipes/pr-edit.md`](../../_shared/github-cli-recipes/pr-edit.md). Preserve `## Goal` and `## Acceptance Criteria` verbatim.
34
+
35
+ 5. **Visual walkthrough comment** — if an E2E report exists, invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode A. Always a fresh comment per cycle; do not edit prior walkthrough comments.
36
+
37
+ 6. **Overflow comment** — same rule as forward mode: post when the walkthrough skill returns non-null `comment`, via [`../../_shared/github-cli-recipes/top-level-comment.md`](../../_shared/github-cli-recipes/top-level-comment.md).
38
+
39
+ ## Handoff
40
+
41
+ Return control to `/muggle-do`'s address-reviews orchestrator. The orchestrator runs the remaining stages (per-comment replies → resolve-reminder → respawn watcher).
42
+
43
+ ## Invariants
44
+
45
+ - Push; new SHA appended to `pushed_shas`; title/body refreshed only on state change; walkthrough comment via Mode A.
46
+ - No `gh pr create`, no `/loop` dispatch.
47
+
48
+ ## Output
49
+
50
+ **PR updated:** URL (new SHA: `<short-sha>`)
51
+ **Title refreshed:** yes | no
52
+ **Body refreshed:** yes | no
53
+ **Walkthrough comment:** posted | skipped (no report)
54
+ **Overflow comment:** posted | skipped
@@ -1,104 +1,8 @@
1
- # PR Creation Agent (Stage 7 — Open PR)
1
+ # Create-or-Update PR (Stage 7)
2
2
 
3
- Open a pull request for each repo that has changes. If an E2E walkthrough report is available from the previous stage, attach it. Honor preference gates. Hand off to stage 8 once done.
3
+ Two modes, set by the `/muggle-do` invocation. Each consumer loads only its mode's file.
4
4
 
5
- ## Turn preamble
6
-
7
- ```
8
- **Stage 7 — Open PR** — pushing the branch and opening the PR.
9
- ```
10
-
11
- ## Inputs
12
-
13
- - Per-repo: name, path, branch.
14
- - Requirements: goal, acceptance criteria.
15
- - **Optional** E2E acceptance report from stage 6 — only present when validation ran. Produced by [`e2e-acceptance.md`](e2e-acceptance.md); schema is canonical in [`muggle-pr-visual-walkthrough/SKILL.md`](../muggle-pr-visual-walkthrough/SKILL.md) (Zod-validated by the CLI).
16
-
17
- ## Per repo
18
-
19
- 0. **`autoCreatePR` gate** — apply per [`../muggle-preferences/preference-gates/autoCreatePR.md`](../muggle-preferences/preference-gates/autoCreatePR.md). On skip, record the reason in `result.md` and move on.
20
-
21
- 1. **Push:** `git push -u origin <branch>` in the repo directory.
22
-
23
- 2. **Title** (under 70 chars):
24
- - E2E report exists and has failures → `[E2E FAILING] <goal>`
25
- - No E2E report at all (validation was `unit-only` or `skip`) → `[UNVERIFIED] <goal>` or `[UNIT-ONLY] <goal>` to match the validation strategy
26
- - Otherwise → `<goal>`
27
-
28
- 3. **Body** — assemble in order:
29
- - `## Goal` — from requirements.
30
- - `## Acceptance Criteria` — bulleted; omit section if empty.
31
- - `## Changes` — summary of what changed in this repo.
32
- - `## Validation` — one line: link to E2E report, or `unit-only`, or `skip — <reason>`.
33
- - **If an E2E report exists,** invoke [`muggle-pr-visual-walkthrough`](../muggle-pr-visual-walkthrough/SKILL.md) Mode B to render the walkthrough block. Embed the returned `body` verbatim (it brings its own `## E2E Acceptance Results` heading). If no report, skip this block entirely.
34
-
35
- 4. **Create:** `gh pr create --title "..." --body "..." --head <branch>`. Capture the PR URL and number.
36
-
37
- 5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once:
38
- ```bash
39
- jq -r '.comment' /tmp/muggle-pr-section.json | gh pr comment <PR#> --body-file -
40
- ```
41
- Never post when `comment` is `null`.
42
-
43
- ## Stage 8 handoff
44
-
45
- After every repo is processed, build the manifest and dispatch **one follow-up loop per opened PR**. The dispatches are the LAST action this stage takes — once they fire, the original session is free.
46
-
47
- Write `.muggle-do/sessions/<slug>/prs.json` with one entry per **opened** PR (skip repos where `autoCreatePR` short-circuited or PR creation failed):
48
-
49
- ```json
50
- [{ "repo": "owner/repo", "number": 142, "url": "...", "head_sha": "...", "state": "open" }]
51
- ```
52
-
53
- Seed `.muggle-do/sessions/<slug>/last_seen.json` keyed by `"<owner>/<repo>#<n>"` with the empty-cursor shape (full shape in [`../muggle-pr-followup/contract.md`](../muggle-pr-followup/contract.md)). Stage 7 only seeds; each per-PR loop owns advancing its own cursor.
54
-
55
- Also seed `.muggle-do/sessions/<slug>/cycle.json` — the muggle-do implementation cycle declaration the follow-up loop will invoke on each actionable review:
56
-
57
- ```json
58
- {
59
- "cycleName": "muggle-do dev cycle",
60
- "steps": [
61
- { "stage": 3, "file": "../do/build.md" },
62
- { "stage": 4, "file": "../do/impact-analysis.md" },
63
- { "stage": 5, "file": "../do/unit-tests.md" },
64
- { "stage": 6, "file": "../do/e2e-acceptance.md" },
65
- { "name": "post-walkthrough", "skill": "muggle-pr-visual-walkthrough", "mode": "A" }
66
- ],
67
- "pushHandler": "git push origin <branch>",
68
- "useSubagent": false
69
- }
70
- ```
71
-
72
- For each entry in `prs.json`, dispatch its own loop as the final action:
73
- ```
74
- /loop 1m /muggle:muggle-pr-followup <slug> <pr-number>
75
- ```
76
- Resolve `<slug>` from the session directory's basename. One loop per PR — multi-repo sessions opening N PRs result in N independent loops, each tracking its own PR's review thread.
77
-
78
- If `prs.json` is empty (all repos skipped, or all PR creations failed), **do not dispatch** — record the reason in `result.md` and exit.
79
-
80
- ## Self-check before exit
81
-
82
- - [ ] Every non-skipped repo got `gh pr create` to succeed.
83
- - [ ] When an E2E report existed, the walkthrough block was rendered via Mode B (not hand-written).
84
- - [ ] Overflow `comment` was posted only when non-null.
85
- - [ ] `prs.json` and `last_seen.json` reflect the PRs actually opened.
86
- - [ ] If `prs.json` is non-empty, the `/loop` dispatch was the last action.
87
-
88
- ## Output
89
-
90
- **PRs Created:** repo → URL
91
- **Skipped:** repo → reason (when `autoCreatePR` short-circuited)
92
- **Overflow comments posted:** repo → PR #
93
- **Stage 8:** `Watching <N> PR(s) — one /loop 1m /muggle:muggle-pr-followup <slug> <pr#> per PR` | `No PRs to watch — stage 8 not dispatched`
94
- **Errors:** repo → message
95
-
96
- ## Post-merge cleanup
97
-
98
- Gated by `autoCleanup`. Fires in a follow-up turn after merge — never from this stage. See [`../_shared/post-merge-cleanup.md`](../_shared/post-merge-cleanup.md).
99
-
100
- Append one short reminder tied to the gate value:
101
-
102
- - `always` → `Once merged, I'll run the cleanup sequence automatically.`
103
- - `never` → omit.
104
- - `ask` / absent → `Once merged, I'll check with you about cleanup.`
5
+ | Mode | When | Procedure |
6
+ | :--- | :--- | :-------- |
7
+ | Forward | After stages 1–6 of a fresh feature (called by `/muggle-do`'s forward pipeline). Creates the PR, seeds state, dispatches the first watcher. | [`open-prs/forward.md`](open-prs/forward.md) |
8
+ | Update | The PR already exists; the address-reviews orchestrator called this stage to push + refresh title/description/walkthrough. | [`open-prs/update.md`](open-prs/update.md) |
@@ -0,0 +1,61 @@
1
+ # Per-Comment Inline Replies
2
+
3
+ A `/muggle-do` step invoked from [`address-reviews.md`](address-reviews.md) (Step 4f) after the push has succeeded. Posts one inline nested reply on each line comment from the actionable reviews, describing what was done for that comment and referencing the new SHA.
4
+
5
+ This is **not** a top-level "summary reply on the review." Each comment thread gets its own reply, in context, via GitHub's `/comments/{id}/replies` endpoint.
6
+
7
+ ## Inputs
8
+
9
+ - `actionable_reviews` — the list of reviews classified actionable in `address-reviews.md` Step 2.
10
+ - `new_sha` — the SHA `open-prs/update.md` just pushed.
11
+ - The PR's owner, repo, number.
12
+
13
+ ## Procedure
14
+
15
+ ### Step 1 — Build the comment-to-change map
16
+
17
+ For each comment in each actionable review, the cycle's `build.md` produced (or should have produced) a one-line note describing what was changed for that specific comment. Two sources, in order:
18
+
19
+ 1. If `build.md` returned a `comment_changes` map keyed by comment id → short description, use it directly.
20
+ 2. If not (legacy `build.md` that doesn't return that shape yet), infer from the diff + the comment's body: which file/line did the comment refer to, and what changed at or near it. The inferred description is best-effort; better to be brief than wrong.
21
+
22
+ If a comment has no associated change in either source (e.g. the comment was a question, not a change request), use the comment's body to compose a one-sentence answer.
23
+
24
+ ### Step 2 — Post one reply per comment
25
+
26
+ For each comment id with a description:
27
+
28
+ ```bash
29
+ gh api --method POST \
30
+ -H "Accept: application/vnd.github+json" \
31
+ repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
32
+ -f body="<reply-body>"
33
+ ```
34
+
35
+ Reply body uses the template in [`../muggle-pr-followup/output-templates/inline-reply.md`](../muggle-pr-followup/output-templates/inline-reply.md):
36
+
37
+ ```
38
+ Addressed in <short-sha>: <one-line summary of the change made for THIS comment>.
39
+ ```
40
+
41
+ `<short-sha>` is the first 7 chars of `new_sha`. The body must contain the literal `<short-sha>` substring — the resolve-reminder stage greps for it to classify threads as addressed-by-loop.
42
+
43
+ ### Step 3 — Handle review-body-only comments
44
+
45
+ If an actionable review has a non-empty `body` and **zero** line comments, GitHub has no `/replies` endpoint for the review body itself (the API has been inconsistent on this and the only reliable path is a top-level PR comment that references the review). Use the *top-level reference* form:
46
+
47
+ ```
48
+ Re: review #<review_id> — addressed in <short-sha>: <one-line summary>.
49
+ ```
50
+
51
+ Posted per [`../_shared/github-cli-recipes/top-level-comment.md`](../_shared/github-cli-recipes/top-level-comment.md). Fires at most once per actionable review-with-no-line-comments. Does not fire if the review has line comments — Step 2 covers those.
52
+
53
+ ## Failure modes
54
+
55
+ - `gh api` returns an error for an individual reply → log to `followup.log`, continue with the remaining comments. Do not abort the whole step over one failure; the push has already happened and other replies still need posting.
56
+ - All replies fail → surface the most-recent `gh` error to the user, but do not abort the overall `/muggle-do` invocation. The resolve-reminder stage still runs; the watcher still respawns. The next cycle on this PR will produce more replies and the missing ones can be picked up by the human reviewer.
57
+
58
+ ## Invariants
59
+
60
+ - One reply per line comment. No per-review summary reply anywhere.
61
+ - Every reply body contains the new SHA's 7-char prefix — the resolve-reminder stage greps for it.
@@ -0,0 +1,70 @@
1
+ # Resolve-Reminder Stage
2
+
3
+ A `/muggle-do` stage that runs in **address-reviews mode only**, after per-comment inline replies have been posted. Scans every unresolved comment thread on the PR, classifies them, and posts ONE top-level PR comment listing the threads the loop addressed in this push.
4
+
5
+ Runs zero or one times per `/muggle-do` invocation:
6
+
7
+ - Runs when at least one actionable review was processed (i.e. the cycle actually pushed).
8
+ - Does not run when the entire input batch was ambiguous (no push, nothing to remind about).
9
+ - Does not run in the forward pipeline (a fresh PR has no review threads to remind about).
10
+
11
+ ## Turn preamble (inline within `/muggle-do` cycle)
12
+
13
+ This stage does not print a turn preamble — it runs inside `/muggle-do`'s address-reviews cycle, immediately after per-comment replies and before respawning the watcher.
14
+
15
+ ## Inputs
16
+
17
+ - The current PR (URL, owner, repo, number) from the session's `prs.json`.
18
+ - `last_seen.pushed_shas[]` from `last_seen.json` — the list of every SHA `/muggle-do` has pushed for this PR.
19
+ - The loop user's GitHub login (cached in `state.md` under `Loop user:` — re-resolve per [`../_shared/github-cli-recipes/loop-user-identity.md`](../_shared/github-cli-recipes/loop-user-identity.md) if missing).
20
+
21
+ ## Procedure
22
+
23
+ ### Step 1 — Fetch unresolved comment threads
24
+
25
+ Per [`../_shared/github-cli-recipes/unresolved-threads.md`](../_shared/github-cli-recipes/unresolved-threads.md). Filter client-side to `isResolved == false`. Each thread carries its line comments with `author.login`, `body`, and `databaseId`.
26
+
27
+ If the API call fails, log the error to `followup.log` and skip the stage. Do not surface a user-facing error — the resolve reminder is a nice-to-have, not load-bearing. The reply summaries on the threads themselves still happen.
28
+
29
+ ### Step 2 — Classify each thread
30
+
31
+ For each unresolved thread, walk its comments in chronological order. Classify by the **first match** that applies:
32
+
33
+ - **Addressed by the loop** — at least one comment authored by the loop user **and** that comment's body cites a SHA prefix in `last_seen.pushed_shas[]`. Bodies use the form *"Addressed in `<short-sha>`: ..."* per [`../muggle-pr-followup/output-templates/inline-reply.md`](../muggle-pr-followup/output-templates/inline-reply.md), so a substring match on any `pushed_shas[i][:7]` works.
34
+ - **Addressed by a human** — at least one comment authored by a non-loop-user identity created after the original comment's timestamp, and no addressed-by-loop signal.
35
+ - **Not addressed** — neither of the above.
36
+
37
+ The classification considers only the unresolved threads' comments. Do not cross-reference timeline events from outside the threads.
38
+
39
+ ### Step 3 — Build the resolve-reminder list
40
+
41
+ Collect the thread `databaseId` of every thread classified as **addressed by the loop in this push** (i.e. citing a SHA in `pushed_shas[]` where that SHA was added by the current invocation; older SHAs were already covered by prior cycles' reminders).
42
+
43
+ Note: the watcher does not maintain a "addressed-this-cycle" set; this stage derives it by comparing thread comments to the most-recent appended SHA. The simplest deterministic rule: include a thread iff at least one of its bot replies cites the **most recent** `pushed_shas[-1]`. Earlier SHAs were already addressed in past reminders.
44
+
45
+ ### Step 4 — Post the top-level reminder comment
46
+
47
+ If the resolve-reminder list is non-empty, post **one** top-level PR comment using the template in [`../muggle-pr-followup/output-templates/resolve-reminder.md`](../muggle-pr-followup/output-templates/resolve-reminder.md) per [`../_shared/github-cli-recipes/top-level-comment.md`](../_shared/github-cli-recipes/top-level-comment.md).
48
+
49
+ If the list is empty (the push didn't end up addressing any threads — e.g. the actionable work was on lines that had no comment threads), post **nothing**. Still emit telemetry so the stage's run is observable.
50
+
51
+ ### Step 5 — Emit telemetry
52
+
53
+ Emit one event per [`../_shared/telemetry-events/muggle-do-resolve-reminder.md`](../_shared/telemetry-events/muggle-do-resolve-reminder.md). Include:
54
+
55
+ - `addressed_by_loop` — count of threads added to the reminder list in Step 3.
56
+ - `addressed_by_human` — count from Step 2's other category.
57
+ - `not_addressed` — count from Step 2's "not addressed" category.
58
+ - `comment_posted` — true iff Step 4 actually posted a comment.
59
+
60
+ ## Failure modes
61
+
62
+ This stage is best-effort. Any failure is logged to `followup.log` and silently skipped — the reviewer still gets the per-comment inline replies (the canonical signal that work was done), and the cycle continues to respawn the watcher.
63
+
64
+ The one exception: do not silently swallow a `gh pr comment` failure if Step 4 ran. The comment is a user-visible artifact; if it fails, surface the underlying `gh` error to the user so they know the reminder didn't post.
65
+
66
+ ## Invariants
67
+
68
+ - Telemetry fires once per invocation, even when no comment is posted.
69
+ - The reminder only covers threads addressed by the **most recent** push — older SHAs were covered by prior cycles' reminders.
70
+ - This stage suggests; it does not resolve threads on the reviewer's behalf.