@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.
- package/dist/{chunk-E52757MK.js → chunk-2DVZ2LYO.js} +5 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/scripts/ensure-electron-app.sh +29 -4
- package/dist/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
- package/dist/plugin/skills/_shared/github-cli-recipes.md +20 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers.md +8 -146
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
- package/dist/plugin/skills/_shared/telemetry-events.md +15 -0
- package/dist/plugin/skills/do/address-reviews.md +137 -0
- package/dist/plugin/skills/do/build.md +5 -5
- package/dist/plugin/skills/do/open-prs/forward.md +80 -0
- package/dist/plugin/skills/do/open-prs/update.md +54 -0
- package/dist/plugin/skills/do/open-prs.md +6 -102
- package/dist/plugin/skills/do/per-comment-replies.md +61 -0
- package/dist/plugin/skills/do/resolve-reminder.md +70 -0
- package/dist/plugin/skills/muggle-do/SKILL.md +41 -45
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
- package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
- package/dist/plugin/skills/muggle-pr-followup/contract.md +44 -161
- package/dist/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
- package/dist/release-manifest.json +4 -4
- package/package.json +106 -106
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/scripts/ensure-electron-app.sh +29 -4
- package/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
- package/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
- package/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
- package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
- package/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
- package/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
- package/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
- package/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
- package/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
- package/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
- package/plugin/skills/_shared/github-cli-recipes.md +20 -0
- package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
- package/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
- package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
- package/plugin/skills/_shared/pr-followup-helpers.md +8 -146
- package/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
- package/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
- package/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
- package/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
- package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
- package/plugin/skills/_shared/telemetry-events.md +15 -0
- package/plugin/skills/do/address-reviews.md +137 -0
- package/plugin/skills/do/build.md +5 -5
- package/plugin/skills/do/open-prs/forward.md +80 -0
- package/plugin/skills/do/open-prs/update.md +54 -0
- package/plugin/skills/do/open-prs.md +6 -102
- package/plugin/skills/do/per-comment-replies.md +61 -0
- package/plugin/skills/do/resolve-reminder.md +70 -0
- package/plugin/skills/muggle-do/SKILL.md +41 -45
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
- package/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
- package/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
- package/plugin/skills/muggle-pr-followup/contract.md +44 -161
- package/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
- package/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
- package/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
- package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
- package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
- package/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
- package/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
- package/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
- 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
|
|
44
|
+
## Re-entry from the address-reviews flow
|
|
45
45
|
|
|
46
|
-
|
|
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
|
|
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
|
-
-
|
|
51
|
-
-
|
|
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
|
|
1
|
+
# Create-or-Update PR (Stage 7)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Two modes, set by the `/muggle-do` invocation. Each consumer loads only its mode's file.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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.
|