@muggleai/works 5.9.0 → 5.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/{chunk-LVUJ4E62.js → chunk-MSPMXBO5.js} +638 -468
- package/dist/{chunk-5PYK7DWB.js → chunk-WUMAJOBT.js} +17 -6
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/README.md +19 -13
- package/dist/plugin/agents/test-prepare-runner.md +4 -1
- package/dist/plugin/agents/visual-walkthrough-builder.md +12 -5
- package/dist/plugin/commands/mtest.md +1 -1
- package/dist/plugin/hooks/README.md +15 -3
- package/dist/plugin/hooks/hooks.json +79 -0
- package/dist/plugin/scripts/ensure-electron-app.sh +62 -47
- package/dist/plugin/scripts/guardrail-classify-gate.sh +23 -0
- package/dist/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
- package/dist/plugin/scripts/guardrail-pr-terminal.sh +6 -2
- package/dist/plugin/scripts/guardrail-record-stage-read.sh +21 -0
- package/dist/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
- package/dist/plugin/scripts/guardrail-record-tests.sh +11 -4
- package/dist/plugin/scripts/guardrail-report-format.sh +9 -5
- package/dist/plugin/scripts/guardrail-skill-stages.sh +35 -0
- package/dist/plugin/scripts/guardrail-stage-gate.sh +43 -0
- package/dist/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
- package/dist/plugin/scripts/guardrails.mjs +474 -80
- package/dist/plugin/scripts/pr-watch-events.sh +116 -0
- package/dist/plugin/scripts/pr-watch-guards.sh +8 -2
- package/dist/plugin/scripts/pr-watch-loop.sh +254 -0
- package/dist/plugin/scripts/reconcile-stale-watchers.sh +66 -18
- package/dist/plugin/scripts/sign-body.sh +88 -0
- package/dist/plugin/skills/CLAUDE.md +16 -0
- package/dist/plugin/skills/_shared/post-merge-cleanup.md +83 -3
- package/dist/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
- package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
- package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/dist/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
- package/dist/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
- package/dist/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
- package/dist/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
- package/dist/plugin/skills/_shared/vcs/post-signature.md +23 -12
- package/dist/plugin/skills/do/open-prs/forward.md +2 -2
- package/dist/plugin/skills/do/per-comment-replies.md +2 -15
- package/dist/plugin/skills/muggle/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
- package/dist/plugin/skills/muggle-pr-followup/adopt.md +60 -0
- package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
- package/dist/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
- package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
- package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
- package/dist/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
- package/dist/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/dist/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/dist/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/dist/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
- package/dist/plugin/skills/muggle-test/SKILL.md +23 -3
- package/dist/plugin/skills/muggle-test/execute-local.md +2 -0
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
- package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
- package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
- package/dist/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
- package/dist/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
- package/dist/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/release-manifest.json +4 -4
- package/dist/src-B5OTAZKM.js +1 -0
- package/package.json +7 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/README.md +19 -13
- package/plugin/agents/test-prepare-runner.md +4 -1
- package/plugin/agents/visual-walkthrough-builder.md +12 -5
- package/plugin/commands/mtest.md +1 -1
- package/plugin/hooks/README.md +15 -3
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/scripts/ensure-electron-app.sh +62 -47
- package/plugin/scripts/guardrail-classify-gate.sh +23 -0
- package/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
- package/plugin/scripts/guardrail-pr-terminal.sh +6 -2
- package/plugin/scripts/guardrail-record-stage-read.sh +21 -0
- package/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
- package/plugin/scripts/guardrail-record-tests.sh +11 -4
- package/plugin/scripts/guardrail-report-format.sh +9 -5
- package/plugin/scripts/guardrail-skill-stages.sh +35 -0
- package/plugin/scripts/guardrail-stage-gate.sh +43 -0
- package/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
- package/plugin/scripts/guardrails.mjs +474 -80
- package/plugin/scripts/pr-watch-events.sh +116 -0
- package/plugin/scripts/pr-watch-guards.sh +8 -2
- package/plugin/scripts/pr-watch-loop.sh +254 -0
- package/plugin/scripts/reconcile-stale-watchers.sh +66 -18
- package/plugin/scripts/sign-body.sh +88 -0
- package/plugin/skills/CLAUDE.md +16 -0
- package/plugin/skills/_shared/post-merge-cleanup.md +83 -3
- package/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
- package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
- package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
- package/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
- package/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
- package/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
- package/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
- package/plugin/skills/_shared/vcs/post-signature.md +23 -12
- package/plugin/skills/do/open-prs/forward.md +2 -2
- package/plugin/skills/do/per-comment-replies.md +2 -15
- package/plugin/skills/muggle/SKILL.md +1 -1
- package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
- package/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
- package/plugin/skills/muggle-pr-followup/adopt.md +60 -0
- package/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
- package/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
- package/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
- package/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
- package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
- package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
- package/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
- package/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
- package/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
- package/plugin/skills/muggle-test/SKILL.md +23 -3
- package/plugin/skills/muggle-test/execute-local.md +2 -0
- package/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
- package/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
- package/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
- package/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
- package/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
- package/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
- package/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
- package/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
- package/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
- package/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
- package/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
- package/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/src-FDK2KQSZ.js +0 -1
|
@@ -4,6 +4,86 @@ Gated by [`autoCleanup`](../muggle-preferences/preference-gates/autoCleanup.md).
|
|
|
4
4
|
|
|
5
5
|
On `always`, the steps below run as one pre-authorized sequence (no per-step prompts). Stop on the first failure; do not force.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
**Verify every step, assume none.** Each step states what proves it succeeded. A step whose side effect *usually* happens is not a step that ran — that assumption is how a branch survives a cleanup that reported success. The [report](#report) states verified state, never intent.
|
|
8
|
+
|
|
9
|
+
## Preconditions
|
|
10
|
+
|
|
11
|
+
Confirm the PR is `MERGED` from provider state. A closed-unmerged PR keeps its branch and worktree: the work never landed, so deleting it destroys it.
|
|
12
|
+
|
|
13
|
+
## 1. Remove the worktree — link-safe
|
|
14
|
+
|
|
15
|
+
Only if a worktree was used.
|
|
16
|
+
|
|
17
|
+
A worktree's dependency dir (`node_modules`, and nested workspace copies) is often a **link** — a symlink or a Windows junction — to a shared tree rather than a real copy. A forced or recursive delete follows the link and wipes that shared target, breaking every other worktree.
|
|
18
|
+
|
|
19
|
+
1. **Never `--force`.** It is the one flag that follows links.
|
|
20
|
+
2. Clear the dependency and build dirs first, nested workspace ones included (`packages/*/node_modules`, `dist`). A plain `git worktree remove` fails with `Directory not empty` while they remain, and the obvious fix for that error is exactly the forbidden flag.
|
|
21
|
+
- A **link** → unlink it first with the host OS's unlink, removing the link only and never its target.
|
|
22
|
+
- A **real directory** → delete it in place.
|
|
23
|
+
Check which it is before deleting; the two are indistinguishable from a listing but not from a `rm -r`.
|
|
24
|
+
3. Then plain `git worktree remove {worktreePath}`.
|
|
25
|
+
4. `git worktree prune` to drop the administrative entry when the directory went away out from under git.
|
|
26
|
+
|
|
27
|
+
**Verify:** the path is gone, it no longer appears in `git worktree list`, **and** the shared dependency tree the links pointed at still exists. That last check is the one that catches a link-follow.
|
|
28
|
+
|
|
29
|
+
## 2. Delete the local branch
|
|
30
|
+
|
|
31
|
+
**Skip entirely when no worktree was used** — the branch is then the user's live checkout, and a checked-out branch must never be deleted.
|
|
32
|
+
|
|
33
|
+
`git branch -d` refuses after a **squash merge**, and always will: squashing mints a new commit carrying the same tree, so the branch tip is never an ancestor of the base. This is not a safety check that failed; it is one that cannot pass.
|
|
34
|
+
|
|
35
|
+
Do not reach for `-D` on faith. Replace the ancestry check with a content check:
|
|
36
|
+
|
|
37
|
+
1. Confirm the merged content is on the base — files the PR added exist at `origin/<base>`, and anything it removed is absent there.
|
|
38
|
+
2. Only then `git branch -D {branch}`.
|
|
39
|
+
|
|
40
|
+
**Verify:** the branch is absent from `git branch --list`.
|
|
41
|
+
|
|
42
|
+
## 3. Delete the remote branch
|
|
43
|
+
|
|
44
|
+
**A provider that auto-deletes the head branch on merge is a setting, not a guarantee.** It can be off for the repo, off for a fork, or simply not fire. Treat auto-delete as something to detect, never as this step having run.
|
|
45
|
+
|
|
46
|
+
1. Query the ref. Already gone → record it deleted and move on.
|
|
47
|
+
2. Still present → delete it explicitly.
|
|
48
|
+
|
|
49
|
+
**Verify:** querying the ref returns not-found.
|
|
50
|
+
|
|
51
|
+
## 4. Clear the session slot
|
|
52
|
+
|
|
53
|
+
The slot is `~/.muggle-ai/muggle-do/sessions/<slug>/` — the home directory, not the project.
|
|
54
|
+
|
|
55
|
+
Clear it only when `prs.json` records a terminal state. A slot for a still-open PR is live state that a watcher and [`reconcile`](../muggle-pr-followup/reconcile.md) both read. The merged PR is the durable record, so the slot's `result.md` is not lost history.
|
|
56
|
+
|
|
57
|
+
**Verify:** the slot directory is gone, and no non-terminal slot was touched.
|
|
58
|
+
|
|
59
|
+
## 5. Clear this run's prepare artifacts
|
|
60
|
+
|
|
61
|
+
The PID tracker and per-service logs written during environment prep — `/tmp/muggle-test-prepare.json` and `/tmp/muggle-prepare-*.log`.
|
|
62
|
+
|
|
63
|
+
**Only this run's.** Artifacts belonging to another session are not yours to delete; it may be mid-prepare with services running. When the tracker is absent no services are tracked, so any remaining logs are orphaned — report them rather than removing them.
|
|
64
|
+
|
|
65
|
+
Cloud results always stay.
|
|
66
|
+
|
|
67
|
+
**Verify:** this run's artifacts are gone; anything left is named in the report as out of scope.
|
|
68
|
+
|
|
69
|
+
## Report
|
|
70
|
+
|
|
71
|
+
Print as the last action, one row per step, filled from the verification checks — never from the fact that a command was issued:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Cleanup — <slug> (PR #<n>, merged)
|
|
75
|
+
|
|
76
|
+
| Step | Result |
|
|
77
|
+
|:----------------------|:------------------------------------------------|
|
|
78
|
+
| Worktree removed | ✅ .claude/worktrees/<name> (shared deps intact) |
|
|
79
|
+
| Local branch deleted | ✅ users/<user>/<branch> |
|
|
80
|
+
| Remote branch deleted | ✅ (already gone — provider auto-delete) |
|
|
81
|
+
| Session slot cleared | ✅ <slug> |
|
|
82
|
+
| Prepare artifacts | — none for this run |
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Markers: `✅` verified done · `⚠️` done with a caveat worth reading · `❌` not done, with the reason · `—` nothing to do.
|
|
86
|
+
|
|
87
|
+
Every step gets a row. A step that did not run is `❌` plus its reason — never omitted, and never `✅` when its verification did not actually run. A silent gap is worse than a visible one, because it is the version the user ends up believing.
|
|
88
|
+
|
|
89
|
+
State anything deliberately left alone — another session's artifacts, a foreign worktree, an orphaned watcher — on a line below the table, so out-of-scope is declared rather than invisible.
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
# Loop comment signature
|
|
2
2
|
|
|
3
|
-
Every
|
|
3
|
+
Every comment `/muggle-do` posts on either provider — inline thread replies (`gitlab`: discussion notes), top-level reference comments, resolve-reminders — **must** carry the loop marker. It is the only reliable way to tell loop-authored comments from human comments: in single-account workflows the loop posts under the change author's own identity, so the author login cannot distinguish them. Echo-protection and addressed-by-loop classification both depend on this marker.
|
|
4
4
|
|
|
5
|
-
## The
|
|
6
|
-
|
|
7
|
-
Append these two lines as the end of every loop-posted comment body:
|
|
5
|
+
## The marker
|
|
8
6
|
|
|
9
7
|
```
|
|
10
8
|
<!-- muggle-do:bot -->
|
|
11
|
-
🤖 _Posted by `/muggle-do` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
12
9
|
```
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
Hidden HTML: both providers render it invisibly and humans never type it. It must stay exactly as written — echo-protection and addressed-by-loop classification read this literal string.
|
|
12
|
+
|
|
13
|
+
Signing a body with `--mode loop` ([`../vcs/post-signature.md`](../vcs/post-signature.md)) emits the marker above the visible Muggle Works line. Never hand-write either one; a body assembled by hand is the one that silently ships without them.
|
|
16
14
|
|
|
17
15
|
## Detection
|
|
18
16
|
|
|
19
17
|
- **Loop-authored** — the comment body contains the literal `<!-- muggle-do:bot -->`.
|
|
20
18
|
- **Human** — the body does not contain the marker.
|
|
21
19
|
|
|
22
|
-
Classify by the marker, never by `author.login`
|
|
20
|
+
Classify by the marker, never by the author login alone (`github`: `author.login`, `gitlab`: `author.username`) — the login is ambiguous under a shared account. A comment that carries the marker is the loop's own and must never re-trigger a cycle; a comment without it is human intent to act on.
|
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
# Reply routing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reply endpoints are not uniform across comment types. Route by parent type, then run the resolved provider's recipe for that route — resolve the provider once per [`../vcs/detect-vcs.md`](../vcs/detect-vcs.md). The routing decision below is provider-agnostic; the commands live with their tool.
|
|
4
4
|
|
|
5
5
|
## Line-level review comment (most common)
|
|
6
6
|
|
|
7
|
-
A comment attached to a specific file:line that belongs to a review thread.
|
|
7
|
+
A comment attached to a specific file:line that belongs to a review thread. Reply in the same thread, so the reply keeps its context:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
--method POST \
|
|
12
|
-
-H "Accept: application/vnd.github+json" \
|
|
13
|
-
/repos/<owner>/<repo>/pulls/<number>/comments/<comment_id>/replies \
|
|
14
|
-
-f body="Done in $(git rev-parse --short HEAD) — renamed \`fooBar\` to \`foo_bar\`."
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The reply lands in the same review thread with `in_reply_to_id = <comment_id>`.
|
|
9
|
+
- `github` — [`../vcs/github/reply-line-comment.md`](../vcs/github/reply-line-comment.md). The reply lands in the thread with `in_reply_to_id = <comment_id>`.
|
|
10
|
+
- `gitlab` — [`../vcs/gitlab/reply-discussion.md`](../vcs/gitlab/reply-discussion.md). The reply is a new note on the discussion.
|
|
18
11
|
|
|
19
|
-
## Review body
|
|
12
|
+
## Review body with no inline comments
|
|
20
13
|
|
|
21
|
-
A reviewer left a summary review with
|
|
14
|
+
A reviewer left a summary review with a body but **no** inline comments. There is no thread to reply into, so answer with a top-level comment that names the review:
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
16
|
+
- `github` — [`../vcs/github/top-level-comment.md`](../vcs/github/top-level-comment.md). GitHub has no reply-to-review-body endpoint, which is why this route exists at all.
|
|
17
|
+
- `gitlab` — does not arise. GitLab has no review envelope; every note belongs to a discussion, so it routes as a line-level reply above.
|
|
26
18
|
|
|
27
19
|
## Failing CI check
|
|
28
20
|
|
|
29
|
-
No reply. The fix commit IS the response. Include the failing check name in the commit subject:
|
|
21
|
+
No reply on either provider. The fix commit IS the response. Include the failing check name in the commit subject:
|
|
30
22
|
|
|
31
23
|
```
|
|
32
24
|
fix(ci): typecheck — narrow type of foo
|
|
@@ -36,5 +28,5 @@ fix(ci): lint — remove unused import
|
|
|
36
28
|
## Never
|
|
37
29
|
|
|
38
30
|
- Never post a top-level comment in reply to a line-level comment. It loses thread context.
|
|
39
|
-
- Never `gh pr review --comment`
|
|
31
|
+
- Never open a *new* review to carry a reply (`github`: `gh pr review --comment`) — those endpoints are for new reviews, not replies.
|
|
40
32
|
- 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.
|
|
@@ -25,6 +25,7 @@ Resolve without prompting; use as questionnaire defaults:
|
|
|
25
25
|
3. Candidate projects — `muggle-remote-project-list`, ranked against the repo's dev URL and the PR title.
|
|
26
26
|
4. Existing test-user secrets — `muggle-remote-secret-list` per candidate project (`managed_profile_email` / `managed_profile_password`).
|
|
27
27
|
5. Auth0 tenant for local dev — grep the repo env file for `*AUTH0_DOMAIN*`.
|
|
28
|
+
6. E2E run instructions — read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists, keyed on this stack's identity. It records startup order, manual steps, and local gotchas. Treat it as read-only input: surface its gotchas rather than re-deriving them, and never re-ask what it already answers. Absent is normal — proceed without it.
|
|
28
29
|
|
|
29
30
|
## Questions
|
|
30
31
|
|
|
@@ -5,6 +5,7 @@ Provider-agnostic seam for the watcher and dev-cycle skills. A caller resolves o
|
|
|
5
5
|
## Index
|
|
6
6
|
|
|
7
7
|
- [`detect-vcs.md`](detect-vcs.md) — resolve `github` | `gitlab` from a URL argument or the origin remote.
|
|
8
|
+
- [`post-signature.md`](post-signature.md) — sign every posted body through `scripts/sign-body.sh`; provider-agnostic, required by every recipe below that writes a body.
|
|
8
9
|
- [`github.md`](github.md) — `gh` / `git` recipe TOC; per-recipe files in [`github/`](github/).
|
|
9
10
|
- [`gitlab.md`](gitlab.md) — `glab` / `git` recipe TOC; per-recipe files in [`gitlab/`](gitlab/).
|
|
10
11
|
- `common/` — provider-agnostic entry points (`ci-rollup`, `branch-standing`, `push-to-branch`, `verify-working-tree`), indexed by both provider TOCs.
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
For `open-prs/update.md` when E2E state flips (passing↔failing) or validation strategy changes.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-do --mode editable < <draft-file> > <signed-file>
|
|
6
7
|
gh pr edit <pr-number> --repo <owner>/<repo> --title "<new-title>"
|
|
7
|
-
gh pr edit <pr-number> --repo <owner>/<repo> --body-file <file>
|
|
8
|
+
gh pr edit <pr-number> --repo <owner>/<repo> --body-file <signed-file>
|
|
8
9
|
```
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
Sign with `--mode editable` per [`../post-signature.md`](../post-signature.md) — a description is re-posted on every refresh, and that mode cuts the previous signature before appending so they never stack.
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Used by `/muggle-do` per-comment inline replies.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
body="$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-do --mode loop < <draft-file>)"
|
|
6
7
|
gh api --method POST \
|
|
7
8
|
-H "Accept: application/vnd.github+json" \
|
|
8
9
|
repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
|
|
9
|
-
-f body="
|
|
10
|
+
-f body="$body"
|
|
10
11
|
```
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
Sign with `--mode loop` per [`../post-signature.md`](../post-signature.md) — the `<!-- muggle-do:bot -->` marker it prefixes is what keeps the loop from re-triggering on its own reply.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
For the resolve-reminder stage and any non-threaded notice.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
6
|
+
body="$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command <command> --mode <mode> < <draft-file>)"
|
|
7
|
+
gh pr comment <pr-number> --repo <owner>/<repo> --body "$body"
|
|
7
8
|
```
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
Sign per [`../post-signature.md`](../post-signature.md). A resolve-reminder takes `--mode loop`, so the loop recognises the reminder as its own on a later tick; a one-shot notice nothing needs to detect takes `--mode plain`.
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
For `open-prs`. Push the branch first (see [`../common/push-to-branch.md`](../common/push-to-branch.md)), then open the MR.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-do --mode editable < <draft-file> > <signed-file>
|
|
6
7
|
glab mr create -R <group>/<project> \
|
|
7
8
|
--source-branch <branch> --target-branch <base> \
|
|
8
|
-
--title "<title>" --description "$(cat <file>)"
|
|
9
|
+
--title "<title>" --description "$(cat <signed-file>)"
|
|
9
10
|
```
|
|
10
11
|
|
|
12
|
+
Sign with `--mode editable` per [`../post-signature.md`](../post-signature.md) — the description this opens the MR with is the same body `mr-edit.md` refreshes later, so it carries the dedup marker from the start.
|
|
13
|
+
|
|
11
14
|
`glab` prints the created MR's URL on success — capture stdout and store the URL for handoff (the watcher seeds from it, the user gets the link).
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
For `open-prs/update.md` when E2E state flips (passing↔failing) or validation strategy changes. GitLab calls the body the **description**.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-do --mode editable < <draft-file> > <signed-file>
|
|
6
7
|
glab mr update <iid> -R <group>/<project> --title "<new-title>"
|
|
7
|
-
glab mr update <iid> -R <group>/<project> --description "$(cat <file>)"
|
|
8
|
+
glab mr update <iid> -R <group>/<project> --description "$(cat <signed-file>)"
|
|
8
9
|
```
|
|
10
|
+
|
|
11
|
+
Sign with `--mode editable` per [`../post-signature.md`](../post-signature.md) — a description is re-posted on every refresh, and that mode cuts the previous signature before appending so they never stack.
|
|
@@ -3,5 +3,8 @@
|
|
|
3
3
|
For the resolve-reminder stage and any non-threaded notice.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
6
|
+
body="$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command <command> --mode <mode> < <draft-file>)"
|
|
7
|
+
glab mr note <iid> -R <group>/<project> -m "$body"
|
|
7
8
|
```
|
|
9
|
+
|
|
10
|
+
Sign per [`../post-signature.md`](../post-signature.md). A resolve-reminder takes `--mode loop`, so the loop recognises the reminder as its own on a later tick; a one-shot notice nothing needs to detect takes `--mode plain`.
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
Used by `/muggle-do` per-comment inline replies. A threaded reply is a new note appended to an existing discussion.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
body="$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-do --mode loop < <draft-file>)"
|
|
6
7
|
glab api --method POST \
|
|
7
8
|
projects/:id/merge_requests/<iid>/discussions/<discussion-id>/notes \
|
|
8
|
-
-f body="
|
|
9
|
+
-f body="$body"
|
|
9
10
|
```
|
|
11
|
+
|
|
12
|
+
Sign with `--mode loop` per [`../post-signature.md`](../post-signature.md) — the `<!-- muggle-do:bot -->` marker it prefixes is what keeps the loop from re-triggering on its own note.
|
|
@@ -2,14 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
Every pull-request / merge-request body, comment, and review-thread reply that muggle works posts ends with a signature line. Under a single-account workflow the automation posts as the repo owner, so the signature is what tells a reader — and a reviewer — that the post came from Muggle Works and which command produced it.
|
|
4
4
|
|
|
5
|
+
## Signing a body
|
|
6
|
+
|
|
7
|
+
Pipe the body through the shipped script. Never retype the line from this doc: a signature derived from prose is dropped silently, and a post that lost it still succeeds, still looks right to the poster, and is indistinguishable from a human comment forever after.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
body="$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command <command> --mode <mode> < <draft-file>)"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`--mode` picks the marker that precedes the visible line:
|
|
14
|
+
|
|
15
|
+
| Mode | Marker | Use for |
|
|
16
|
+
|------|--------|---------|
|
|
17
|
+
| `loop` | `<!-- muggle-do:bot -->` | Thread replies and resolve-reminders — anything the loop must later recognise as its own. |
|
|
18
|
+
| `editable` | `<!-- muggle-works:signature -->` | PR/MR descriptions. The script cuts the previous signature before appending, so a description re-posted on every refresh keeps exactly one. |
|
|
19
|
+
| `plain` | none | One-shot comments that nothing needs to detect later. |
|
|
20
|
+
|
|
21
|
+
Signing is idempotent — an already-signed body is re-signed, not double-signed — so a body may be passed through on a refresh path without checking whether it carries a signature already.
|
|
22
|
+
|
|
5
23
|
## The line
|
|
6
24
|
|
|
7
|
-
|
|
25
|
+
What the script emits, recorded here so a reader can recognise it. The script is the definition:
|
|
8
26
|
|
|
9
27
|
```
|
|
10
28
|
🤖 _Posted by `<command>` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
11
29
|
```
|
|
12
30
|
|
|
31
|
+
## Naming the command
|
|
32
|
+
|
|
13
33
|
`<command>` is the slash-command of the skill whose `gh` / `glab` call posts the body:
|
|
14
34
|
|
|
15
35
|
- `/muggle-do` — PR/MR descriptions, per-comment thread replies, top-level reference replies, and resolve-reminders.
|
|
@@ -17,15 +37,6 @@ Append this as the last line of the posted body:
|
|
|
17
37
|
|
|
18
38
|
Name the command that owns the post, not the one that generated the content. When the walkthrough hands its rendered block back for embedding (Mode B/C), the caller owns the post, so the caller's command is what the signature names.
|
|
19
39
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
A description is re-posted whenever state changes, so its signature must not stack. Precede the line with a hidden marker and treat the pair as one unit:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
<!-- muggle-works:signature -->
|
|
26
|
-
🤖 _Posted by `/muggle-do` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Before writing an edited body, delete everything from the `<!-- muggle-works:signature -->` marker to the end of the body, then append the block fresh. This keeps exactly one signature no matter how many times the description is refreshed.
|
|
40
|
+
## Enforcement
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
`scripts/check-post-signatures.mjs` fails any recipe that posts a body without signing it, on every PR via the `post-signatures` CI job and locally via `pnpm run verify:signatures`.
|
|
@@ -31,10 +31,10 @@ Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fres
|
|
|
31
31
|
- `## Changes` — summary of what changed in this repo.
|
|
32
32
|
- `## Validation` — one line: link to E2E report, `unit-only`, or `skip — <reason>`.
|
|
33
33
|
- **Walkthrough block** — only when an E2E report exists. Fire [`postPRVisualWalkthrough`](../../muggle-preferences/preference-gates/postPRVisualWalkthrough.md); on skip, omit this block. Otherwise invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode B and embed the returned `body` verbatim. No report → skip the block.
|
|
34
|
-
- **Signature** —
|
|
34
|
+
- **Signature** — write the assembled body to a file and sign it with `--command /muggle-do --mode editable` per [`../../_shared/vcs/post-signature.md`](../../_shared/vcs/post-signature.md). The signature lands last, after the walkthrough block; `editable` is the mode a description carries so later refreshes replace it instead of stacking.
|
|
35
35
|
|
|
36
36
|
4. **Create:** resolve the provider per [`../../_shared/vcs/detect-vcs.md`](../../_shared/vcs/detect-vcs.md).
|
|
37
|
-
- `github` → `gh pr create --title "..." --body
|
|
37
|
+
- `github` → `gh pr create --title "..." --body-file <signed-file> --head <branch>`, passing the file signed in Step 3. Capture the PR URL and number.
|
|
38
38
|
- `gitlab` → open the change via [`../../_shared/vcs/gitlab/mr-create.md`](../../_shared/vcs/gitlab/mr-create.md): `glab mr create --source-branch <branch> --target-branch <base> --title "..." --description "..."`. Capture the MR URL and iid.
|
|
39
39
|
|
|
40
40
|
5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once using the provider resolved in Step 4 — `github` per [`../../_shared/vcs/github/top-level-comment.md`](../../_shared/vcs/github/top-level-comment.md), `gitlab` per [`../../_shared/vcs/gitlab/mr-note.md`](../../_shared/vcs/gitlab/mr-note.md). End the posted body with the signature line (command `/muggle-do`) per [`../../_shared/vcs/post-signature.md`](../../_shared/vcs/post-signature.md). Never post when `comment` is `null`.
|
|
@@ -27,14 +27,7 @@ If a comment has no associated change in either source (e.g. the comment was a q
|
|
|
27
27
|
|
|
28
28
|
For each comment id with a description, post the reply with the resolved provider:
|
|
29
29
|
|
|
30
|
-
- **`github`** — nested reply on the line comment
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
gh api --method POST \
|
|
34
|
-
-H "Accept: application/vnd.github+json" \
|
|
35
|
-
repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
|
|
36
|
-
-f body="<reply-body>"
|
|
37
|
-
```
|
|
30
|
+
- **`github`** — nested reply on the line comment, one per actionable comment, per [`../_shared/vcs/github/reply-line-comment.md`](../_shared/vcs/github/reply-line-comment.md).
|
|
38
31
|
|
|
39
32
|
- **`gitlab`** — one threaded note per actionable discussion per [`../_shared/vcs/gitlab/reply-discussion.md`](../_shared/vcs/gitlab/reply-discussion.md) (the discussion id stands in for the comment id), then resolve each fully-addressed thread per [`../_shared/vcs/gitlab/resolve-discussion.md`](../_shared/vcs/gitlab/resolve-discussion.md). The loop-marked reply is the addressed signal; the resolve is GitLab's equivalent of a reviewer closing the thread, which the loop can do directly. Resolving folds the discussion out of the next tick's actionable set, so no separate resolve-reminder nudge is needed for it.
|
|
40
33
|
|
|
@@ -42,12 +35,9 @@ Reply body uses the template in [`../muggle-pr-followup/output-templates/inline-
|
|
|
42
35
|
|
|
43
36
|
```
|
|
44
37
|
Addressed in <short-sha>: <one-line summary of the change made for THIS comment>.
|
|
45
|
-
|
|
46
|
-
<!-- muggle-do:bot -->
|
|
47
|
-
🤖 _Posted by `/muggle-do` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
48
38
|
```
|
|
49
39
|
|
|
50
|
-
`<short-sha>` is the first 7 chars of `new_sha`; the body must contain that substring so the resolve-reminder stage knows which push addressed the thread.
|
|
40
|
+
`<short-sha>` is the first 7 chars of `new_sha`; the body must contain that substring so the resolve-reminder stage knows which push addressed the thread. Write the content only — the posting recipe signs it with `--mode loop`, which appends the `<!-- muggle-do:bot -->` marker that identifies the reply as loop-authored (see [`../_shared/pr-followup-helpers/loop-signature.md`](../_shared/pr-followup-helpers/loop-signature.md)).
|
|
51
41
|
|
|
52
42
|
### Step 3 — Handle review-body-only comments (GitHub only)
|
|
53
43
|
|
|
@@ -58,9 +48,6 @@ If an actionable review has a non-empty `body` and **zero** line comments, GitHu
|
|
|
58
48
|
|
|
59
49
|
```
|
|
60
50
|
Re: review #<review_id> — addressed in <short-sha>: <one-line summary>.
|
|
61
|
-
|
|
62
|
-
<!-- muggle-do:bot -->
|
|
63
|
-
🤖 _Posted by `/muggle-do` · [Muggle Works](https://github.com/multiplex-ai/muggle-ai-works)_
|
|
64
51
|
```
|
|
65
52
|
|
|
66
53
|
Posted per [`../_shared/vcs/github/top-level-comment.md`](../_shared/vcs/github/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.
|
|
@@ -12,7 +12,7 @@ Use this as the top-level Muggle Test command router.
|
|
|
12
12
|
|
|
13
13
|
## Preferences
|
|
14
14
|
|
|
15
|
-
User preferences are injected by the SessionStart hook into a `Muggle Test Preferences` line in session context (key=value pairs). Resolution: defaults → `~/.muggle-ai/preferences.json
|
|
15
|
+
User preferences are injected by the SessionStart hook into a `Muggle Test Preferences` line in session context (key=value pairs). Resolution: defaults → `~/.muggle-ai/preferences.json`. Preferences are user-level and apply to every repo. Treat absent prefs as `ask`.
|
|
16
16
|
|
|
17
17
|
This router skill itself does not gate any decision on a preference — it just routes user intent to a downstream skill. Each downstream skill consults its own preferences. For example, `checkForUpdates` is consulted by `muggle-status` (Check 4), not here.
|
|
18
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: muggle-browser-task
|
|
3
|
-
description: "Perform a real action on a website or web app from a plain-English instruction — log in and do the thing: submit a form, create or update a record, place an order, refund a charge, file a ticket, publish a post,
|
|
3
|
+
description: "Perform a real action on a website or web app from a plain-English instruction — log in and do the thing: submit a form, create or update a record, send a message, place an order, refund a charge, file a ticket, publish a post, complete a multi-step flow. Muggle drives a real browser through the site's own UI and supplies the session — managed login profiles and stored credentials — so a named service (Slack, Jira, Stripe, WordPress, a marketplace, a booking site, an admin panel) is a signal to reach for this skill, not to hunt for its API or connector; \"no logged-in session\" is not a reason to decline. Use whenever the user wants something done in a web UI ('log into X and …', 'send the message', 'create the ticket'), rather than declining or doing it by hand; ask for missing specifics inside the flow, not instead of starting it. Boundary: this performs the action (operating a site's own UI), not migrating personal data between consumer apps; to verify a flow works instead, use muggle-test-feature-local."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Muggle Test Task Runner
|
|
@@ -13,7 +13,8 @@ This folder holds the watcher loop that drives one PR toward merge-ready. The wa
|
|
|
13
13
|
- [`finalize.md`](finalize.md) — shared termination sequence for a terminal PR (mark terminal, `result.md`, log/telemetry, unschedule cron, post-merge cleanup handoff). Called by `contract.md` and `reconcile.md`.
|
|
14
14
|
- [`cancel-cron.md`](cancel-cron.md) — stops this watcher's cron, recorded-id-first (survives `CronList` going blind) with a `CronList`-match fallback, plus the tool-call-not-shell guard. Referenced by `contract.md` and `finalize.md`.
|
|
15
15
|
- [`record-cron-id.md`](record-cron-id.md) — the per-tick self-record that keeps this slot's cron id in `cron.json` deletable after a compaction blinds `CronList`. Referenced by `contract.md` Step 0.
|
|
16
|
-
- [`reconcile.md`](reconcile.md) — sweep that finalizes slots whose PR went terminal while polling lapsed, deletes orphaned crons, and re-arms open slots whose watcher stopped silently (dropped respawn);
|
|
16
|
+
- [`reconcile.md`](reconcile.md) — sweep that finalizes slots whose PR went terminal while polling lapsed, deletes orphaned crons, and re-arms **this session's own** open slots whose watcher stopped silently (dropped respawn); reports slots owned by other sessions without touching them. Runs on demand, at the top of auto-track, and nudged by a session-start hook.
|
|
17
|
+
- [`adopt.md`](adopt.md) — the user's explicit takeover of a slot owned by a session that is gone: claim `owner.json`, then arm. The only path by which a slot changes hands; nothing automatic calls it.
|
|
17
18
|
- [`stop.md`](stop.md) — the owner's kill switch: tears down monitor + cron per slot, renames the slot to `<slug>.stopped` (invisible to every recovery path), and — for stop-everything — writes the global kill file; surviving orphaned crons arrive as one-line absorbs.
|
|
18
19
|
- [`state-schemas.md`](state-schemas.md) — canonical JSON shapes of session state files.
|
|
19
20
|
- [`output-templates.md`](output-templates.md) — TOC of message templates; per-group files in `output-templates/`.
|
|
@@ -20,7 +20,9 @@ A watcher that babysits one open PR toward **merge-ready** — review threads ad
|
|
|
20
20
|
|
|
21
21
|
**Cron lifecycle.** Each tick records its `/loop` cron id to `cron.json` while `CronList` can still see it ([`record-cron-id.md`](record-cron-id.md)), so teardown can delete the cron by id after a session continue / compaction blinds `CronList` to it. Reconcile ([`reconcile.md`](reconcile.md)) sweeps crons whose PR is terminal or whose slot is gone, and re-arms an open slot whose watcher stopped silently — monitor-first, never with a recurring cron.
|
|
22
22
|
|
|
23
|
-
**Session death.** Monitors and crons are both session-bound — a session that ends or hits its usage limit takes every watch with it. That is by design: nothing polls out of session, because a review is addressed only inside a session that carries the context to address it, never by a headless process replying context-blind.
|
|
23
|
+
**Session death.** Monitors and crons are both session-bound — a session that ends or hits its usage limit takes every watch with it. That is by design: nothing polls out of session, because a review is addressed only inside a session that carries the context to address it, never by a headless process replying context-blind.
|
|
24
|
+
|
|
25
|
+
**A watcher belongs to the session that armed it.** Each slot records its owning session in `owner.json` ([`state-schemas.md`](state-schemas.md#ownerjson)), and recovery is owner-scoped: [`reconcile`](reconcile.md#triggers) re-arms a dead watcher only inside the session that armed it, and reports the rest as orphans it declined to touch. Starting a session therefore picks up nothing new — the same context argument that rules out a headless daemon rules out inheriting a stranger's PR, which would hand review work to a session that never saw the design. A PR whose owning session is gone stays unwatched until the user adopts it by name ([`adopt.md`](adopt.md)).
|
|
24
26
|
|
|
25
27
|
## Routing
|
|
26
28
|
|
|
@@ -39,12 +41,13 @@ The skill recognizes its mode by inspecting `$ARGUMENTS` and falling back to on-
|
|
|
39
41
|
| empty | — | **auto-track** → [`auto-track.md`](auto-track.md) |
|
|
40
42
|
| `help` / `?` | — | **help:** list active loops per [`output-templates/help.md`](output-templates/help.md) |
|
|
41
43
|
| `reconcile` / `sweep` (optional `<slug>`) | — | **reconcile** → [`reconcile.md`](reconcile.md) |
|
|
44
|
+
| `adopt` (optional `<slug>`) | — | **adopt** → [`adopt.md`](adopt.md) — take over a slot owned by a dead session; no slug lists the adoptable ones |
|
|
42
45
|
|
|
43
46
|
**`--wake=<event>`** is passed only by an event wake's dispatch ([`arm-watcher.md`](arm-watcher.md) step 5) — it asserts the monitor already saw something new, so the tick's poll is justified. Manual invocations never pass it; with a live watcher they get watch-status, because a poll that nothing prompted is a main-session poll wasted. Recovery fires need no flag — a recoverable slot's watcher is dead by definition, so the gate falls through to tick. A stale cron firing against a re-armed slot hits the live-`watch.pid` branch and is absorbed as a status line, no provider calls.
|
|
44
47
|
|
|
45
|
-
Auto-track runs **reconcile** first, so a no-arg invocation also finalizes any slot whose PR merged or closed while its watcher was down (expired cron, ended session) and re-arms
|
|
48
|
+
Auto-track runs **reconcile** first, so a no-arg invocation also finalizes any slot whose PR merged or closed while its watcher was down (expired cron, ended session) and re-arms this session's own open slots whose watcher stopped silently (a dropped respawn). Reconcile recovers a watcher that was already running; it never seeds a first watcher for a PR — that is auto-track's / bootstrap's job — and never re-arms one this session does not own. A no-arg invocation is a request to track *your* PRs, so it can only ever end with the session watching PRs it pushed plus watchers it already had.
|
|
46
49
|
|
|
47
|
-
**Reconcile also runs at session start** — a `SessionStart` hook ([`../../hooks/README.md`](../../hooks/README.md)) surfaces the sweep when open slots exist, catching a watcher that died with its session (end, or 7-day `/loop` expiry) before a manual sweep would. See [`reconcile.md`](reconcile.md#triggers).
|
|
50
|
+
**Reconcile also runs at session start** — a `SessionStart` hook ([`../../hooks/README.md`](../../hooks/README.md)) surfaces the sweep when open slots exist, catching a watcher that died with its session (end, or 7-day `/loop` expiry) before a manual sweep would. It reports orphaned slots without acting on them; treat that report as inventory, never as a to-do. See [`reconcile.md`](reconcile.md#triggers).
|
|
48
51
|
|
|
49
52
|
Bootstrap accepts three optional trailing flags:
|
|
50
53
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Adopt Procedure
|
|
2
|
+
|
|
3
|
+
The procedure for the **adopt mode** of `muggle-pr-followup` — the user's deliberate takeover of a watcher slot owned by a session that is gone. Routing is in [`SKILL.md`](SKILL.md#routing).
|
|
4
|
+
|
|
5
|
+
Adoption is the only way a slot changes hands. [`reconcile.md`](reconcile.md) re-arms watchers this session already owns and refuses everything else; this file is the door it refuses through. Both exist because a watcher polls a PR in order to hand review work to `/muggle-do` **inside the owning session** — the session that carries the design decisions, the reviewer's phrasing, and the reasoning behind the branch. A session adopting a PR takes that job on without any of it, so the user has to say so.
|
|
6
|
+
|
|
7
|
+
## Input
|
|
8
|
+
|
|
9
|
+
`$ARGUMENTS` is `adopt`, optionally followed by a `<slug>`.
|
|
10
|
+
|
|
11
|
+
- **`adopt <slug>`** — take that slot.
|
|
12
|
+
- **`adopt`** alone — list the adoptable slots and stop. It is a query, not a prompt: print the list and end the turn, with no `AskUserQuestion` and no arming. The user picks by running the command again with a slug.
|
|
13
|
+
|
|
14
|
+
## Procedure
|
|
15
|
+
|
|
16
|
+
### Step 1 — Resolve the slot
|
|
17
|
+
|
|
18
|
+
Slot path is `~/.muggle-ai/muggle-do/sessions/<slug>/`. Refuse, one line each, when:
|
|
19
|
+
|
|
20
|
+
- **The dir is missing** — `no slot for <slug>; pass a PR URL to start one`.
|
|
21
|
+
- **The name ends in `.stopped`** — `<slug> was stopped by its owner; rename it back to adopt it`. Adoption is not a way around [`stop.md`](stop.md): the rename is the owner's kill switch, and honoring it here keeps that switch meaning one thing everywhere.
|
|
22
|
+
- **`result.md` exists** — `<slug> is finalized (<state>); nothing to watch`.
|
|
23
|
+
- **This session already owns it** — `<slug> is already owned by this session`, then run [`reconcile.md`](reconcile.md) scoped to that slug so a dead poller still gets recovered. Adopting what you own is a no-op, not an error.
|
|
24
|
+
|
|
25
|
+
With no slug, list every adoptable slot — open, not `.stopped`, owned by another session or none — as `<slug> → <owner>/<repo>#<n> (owner: <session-id | none>)`, then stop.
|
|
26
|
+
|
|
27
|
+
### Step 2 — Confirm the PR is still worth watching
|
|
28
|
+
|
|
29
|
+
Fetch the PR per [`../_shared/vcs/github/pr-metadata.md`](../_shared/vcs/github/pr-metadata.md) using `prs.json[0].url`.
|
|
30
|
+
|
|
31
|
+
- `MERGED` / `CLOSED` → do not adopt. Run [`finalize.md`](finalize.md) on the slot instead and say so: the slot needed closing out, not a watcher.
|
|
32
|
+
- `gh` fails → surface the error verbatim and stop. Never claim a slot whose PR cannot be read; the claim would strand it under a session that cannot poll it either.
|
|
33
|
+
- `open` → continue.
|
|
34
|
+
|
|
35
|
+
Refresh `prs.json[0].head_sha` to the current `headRefOid`. The branch has almost certainly moved since the original owner last looked.
|
|
36
|
+
|
|
37
|
+
### Step 3 — Warn about the context gap
|
|
38
|
+
|
|
39
|
+
State plainly, in one line, what adoption does not carry: `adopting <slug> — this session has none of the original session's context for #<n>; cycles will work from the PR and the diff alone.`
|
|
40
|
+
|
|
41
|
+
This is a statement, not a gate — the user asked. It exists because the failure it describes is silent: an adopted watcher looks identical to one this session armed, and the first sign of the gap is usually a reply that misreads why the code is the way it is.
|
|
42
|
+
|
|
43
|
+
### Step 4 — Claim and arm
|
|
44
|
+
|
|
45
|
+
Write `owner.json` ([`state-schemas.md`](state-schemas.md#ownerjson)) with `session_id` from `$CLAUDE_CODE_SESSION_ID` and `claimed_at` now, overwriting any previous owner. If `$CLAUDE_CODE_SESSION_ID` is unset, stop with `cannot adopt: no session id to record` — an unidentifiable owner leaves the slot recoverable by nobody.
|
|
46
|
+
|
|
47
|
+
Then arm per [`arm-watcher.md`](arm-watcher.md): one drain tick, then the persistent monitor. The drain is what makes the adoption honest — it acts on everything outstanding now, rather than resuming from a watermark seeded against a wave the original session read and this one never saw.
|
|
48
|
+
|
|
49
|
+
Append `adopted (from <previous-owner | unowned>)` to the slot's `followup.log`.
|
|
50
|
+
|
|
51
|
+
### Step 5 — Report
|
|
52
|
+
|
|
53
|
+
One line: `adopted <slug> → <owner>/<repo>#<n> — armed`.
|
|
54
|
+
|
|
55
|
+
## Invariants
|
|
56
|
+
|
|
57
|
+
- **Explicit only.** Adoption runs when the user names it. No sweep, hook, auto-track, or tick ever calls this procedure, and nothing offers it unprompted.
|
|
58
|
+
- **One slot per invocation.** No bulk adopt, no `--all`. Adopting every orphan in one command is the behavior the ownership gate exists to prevent, wearing a different name.
|
|
59
|
+
- **Stopped stays stopped.** A `.stopped` slot is unreachable here, as it is from every other recovery path.
|
|
60
|
+
- **Claim before arm.** `owner.json` is written before the monitor starts, so a crash between the two leaves a slot this session owns and can recover — never an armed watcher no session claims.
|