@muggleai/works 5.31.0-staging.276 → 5.31.0-staging.277

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +4 -3
  3. package/dist/{chunk-HK7XCCNT.js → chunk-FVG7CYB7.js} +2 -2
  4. package/dist/{chunk-7J7DS4SA.js → chunk-NTEBYUCA.js} +1 -1
  5. package/dist/cli.js +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  8. package/dist/plugin/.codex-mcp.json +1 -1
  9. package/dist/plugin/.codex-plugin/plugin.json +1 -1
  10. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  11. package/dist/plugin/.mcp.json +1 -1
  12. package/dist/plugin/scripts/guardrails.mjs +5 -3
  13. package/dist/plugin/skills/do/background-e2e.md +3 -3
  14. package/dist/plugin/skills/do/build.md +1 -1
  15. package/dist/plugin/skills/do/e2e-acceptance.md +3 -2
  16. package/dist/plugin/skills/do/open-prs/forward.md +126 -102
  17. package/dist/plugin/skills/do/open-prs.md +2 -2
  18. package/dist/plugin/skills/do/wave-e2e.md +14 -8
  19. package/dist/plugin/skills/muggle-do/SKILL.md +13 -6
  20. package/dist/plugin/skills/muggle-preferences/preference-gates/autoRouteBuildToMuggleDo.md +1 -1
  21. package/dist/release-manifest.json +3 -3
  22. package/dist/{src-QA7TWQ2M.js → src-LYOBYPFZ.js} +1 -1
  23. package/package.json +1 -1
  24. package/plugin/.claude-plugin/plugin.json +1 -1
  25. package/plugin/.codex-mcp.json +1 -1
  26. package/plugin/.codex-plugin/plugin.json +1 -1
  27. package/plugin/.cursor-plugin/plugin.json +1 -1
  28. package/plugin/.mcp.json +1 -1
  29. package/plugin/scripts/guardrails.mjs +5 -3
  30. package/plugin/skills/do/background-e2e.md +3 -3
  31. package/plugin/skills/do/build.md +1 -1
  32. package/plugin/skills/do/e2e-acceptance.md +3 -2
  33. package/plugin/skills/do/open-prs/forward.md +126 -102
  34. package/plugin/skills/do/open-prs.md +2 -2
  35. package/plugin/skills/do/wave-e2e.md +14 -8
  36. package/plugin/skills/muggle-do/SKILL.md +13 -6
  37. package/plugin/skills/muggle-preferences/preference-gates/autoRouteBuildToMuggleDo.md +1 -1
@@ -9,7 +9,7 @@
9
9
  "name": "muggleai",
10
10
  "source": "./plugin",
11
11
  "description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
12
- "version": "5.31.0-staging.276"
12
+ "version": "5.31.0-staging.277"
13
13
  }
14
14
  ]
15
15
  }
package/README.md CHANGED
@@ -40,12 +40,13 @@ Works in Claude Code, Cursor, Codex, Windsurf, and any MCP client. Powered by [M
40
40
  | 3 | Build | Delegates real design surface to a design → plan → subagent build, then commits (signed) | Conventional commits |
41
41
  | 4 | Impact analysis | Maps the diff to the user flows it can break | Affected-flow list |
42
42
  | 5 | Unit tests | Runs the suite authored in stage 3 | Exit code |
43
- | 6 | Acceptance | Drives a real browser through the affected flows | Verdict + `runId` + per-step screenshots |
44
- | 7 | Pull request | Opens the PR/MR with the evidence block in the body | PR URL |
43
+ | 5.5 | Pull request | Opens the PR/MR before the browser run, its walkthrough check red until stage 7 | PR URL |
44
+ | 6 | Acceptance | Drives a real browser through the affected flows, its progress showing on the PR | Verdict + `runId` + per-step screenshots |
45
+ | 7 | Walkthrough | Settles the PR's walkthrough comment with the run's evidence | Walkthrough comment |
45
46
  | 7.5 | Repair | Investigates and fixes acceptance failures, up to 3 iterations | Repair log |
46
47
  | 8 | Watcher | Polls reviews, CI, and base-branch drift; re-enters the cycle on each | `followup.log` |
47
48
 
48
- Stage 7 refuses to run until the Definition of Done holds. Stage 8 is dispatched only once stage 7.5 clears — a cycle with unrepaired failures never reaches a watcher.
49
+ Stage 5.5 refuses to open the PR until build and unit tests meet the Definition of Done, and stage 7 refuses to settle its walkthrough without an E2E verdict. Stage 8 is dispatched only once stage 7.5 clears — a cycle with unrepaired failures never reaches a watcher.
49
50
 
50
51
  ---
51
52
 
@@ -1,4 +1,4 @@
1
- import { __export, getLogger, TelemetryEnvironment, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getActiveRuntimeTarget, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, reconcileProjectPreferences, getDataDir2, PREFERENCES_FILE_NAME, isFirstRun, writePreferences, DEFAULT_PREFERENCES, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, getElectronAppSignedFromVersion, getReleaseSignerIdentityUri, verifyFileChecksum, resolveElectronAppExecutablePath, calculateFileChecksum, initTelemetry, Surface, ServiceName, track, EventName, getQaTools, getLocalQaTools, performLogout, assertDeviceCodeClientProvisioned, performLogin, getActiveElectronAppReleaseStream, buildToolInputJsonSchema, toolRequiresAuth, getCallerCredentials, parseElectronAppDirName, getElectronAppReleaseTagPrefix, hasShownDisclosure, markDisclosureShown, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, ElectronAppReleaseStream, buildElectronAppChecksumsUrl, __require, getDisclosureCopy, buildOnboardingPlan, applyOnboardingAnswers, needsOnboarding, buildLlmSourcePlan, describeLlmSourceStatus, resolveLlmSourceStatus, readRunView, RunViewError, renderRunViewText, writeRunViewHtml, openBrowserUrl, evaluateCheckoutFreshness, runGitCommand, BLOCKING_FRESHNESS_VERDICTS, claimServiceStack, BUSY_EXIT_CODE, checkServiceStack, releaseServiceStack, readServiceStackStatus, resolvePreferences, OnboardingBlanketChoice, PreferenceKey, LlmSource, applyLlmSourceSelection, describeStackMismatch, getDataDir, DEFAULT_LOCAL_LLM_BASE_URL, isValidLocalBaseUrl, local_exports } from './chunk-7J7DS4SA.js';
1
+ import { __export, getLogger, TelemetryEnvironment, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getActiveRuntimeTarget, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, reconcileProjectPreferences, getDataDir2, PREFERENCES_FILE_NAME, isFirstRun, writePreferences, DEFAULT_PREFERENCES, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, getElectronAppSignedFromVersion, getReleaseSignerIdentityUri, verifyFileChecksum, resolveElectronAppExecutablePath, calculateFileChecksum, initTelemetry, Surface, ServiceName, track, EventName, getQaTools, getLocalQaTools, performLogout, assertDeviceCodeClientProvisioned, performLogin, getActiveElectronAppReleaseStream, buildToolInputJsonSchema, toolRequiresAuth, getCallerCredentials, parseElectronAppDirName, getElectronAppReleaseTagPrefix, hasShownDisclosure, markDisclosureShown, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, ElectronAppReleaseStream, buildElectronAppChecksumsUrl, __require, getDisclosureCopy, buildOnboardingPlan, applyOnboardingAnswers, needsOnboarding, buildLlmSourcePlan, describeLlmSourceStatus, resolveLlmSourceStatus, readRunView, RunViewError, renderRunViewText, writeRunViewHtml, openBrowserUrl, evaluateCheckoutFreshness, runGitCommand, BLOCKING_FRESHNESS_VERDICTS, claimServiceStack, BUSY_EXIT_CODE, checkServiceStack, releaseServiceStack, readServiceStackStatus, resolvePreferences, OnboardingBlanketChoice, PreferenceKey, LlmSource, applyLlmSourceSelection, describeStackMismatch, getDataDir, DEFAULT_LOCAL_LLM_BASE_URL, isValidLocalBaseUrl, local_exports } from './chunk-NTEBYUCA.js';
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { v4 } from 'uuid';
@@ -1165,7 +1165,7 @@ async function resolveGsScreenshotUrls(report, opts) {
1165
1165
  if (gsUrls.length === 0) {
1166
1166
  return report;
1167
1167
  }
1168
- const mcps = await import('./src-QA7TWQ2M.js');
1168
+ const mcps = await import('./src-LYOBYPFZ.js');
1169
1169
  const credentials = await mcps.getCallerCredentialsAsync();
1170
1170
  if (!credentials.bearerToken && !credentials.apiKey) {
1171
1171
  stderrWrite(
@@ -2858,7 +2858,7 @@ var PREFERENCES_SCHEMA = {
2858
2858
  description: "When a prior session left an E2E validation context for this working tree, reuse it instead of re-asking the validation questions"
2859
2859
  },
2860
2860
  ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: {
2861
- description: "When a prompt looks like a build/implement/fix request, route it through the /muggle-do pipeline (requirements \u2192 build \u2192 impact \u2192 tests \u2192 E2E \u2192 PR \u2192 watcher)"
2861
+ description: "When a prompt looks like a build/implement/fix request, route it through the /muggle-do pipeline (requirements \u2192 build \u2192 impact \u2192 tests \u2192 PR \u2192 E2E \u2192 watcher)"
2862
2862
  },
2863
2863
  ["autoWatchPR" /* AutoWatchPR */]: {
2864
2864
  description: "After a test run opens a PR, start a muggle-pr-followup watcher that polls for new reviews and hands them to /muggle-do"
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-HK7XCCNT.js';
3
- import './chunk-7J7DS4SA.js';
2
+ import { runCli } from './chunk-FVG7CYB7.js';
3
+ import './chunk-NTEBYUCA.js';
4
4
 
5
5
  // src/cli/main.ts
6
6
  runCli().catch((error) => {
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-HK7XCCNT.js';
2
- export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-7J7DS4SA.js';
1
+ export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-FVG7CYB7.js';
2
+ export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-NTEBYUCA.js';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "muggle",
3
3
  "description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
4
- "version": "5.31.0-staging.276",
4
+ "version": "5.31.0-staging.277",
5
5
  "author": {
6
6
  "name": "Muggle AI",
7
7
  "email": "support@muggle-ai.com"
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@muggleai/works@5.31.0-staging.276",
7
+ "@muggleai/works@5.31.0-staging.277",
8
8
  "serve"
9
9
  ],
10
10
  "env": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muggle",
3
- "version": "5.31.0-staging.276",
3
+ "version": "5.31.0-staging.277",
4
4
  "description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
5
5
  "author": {
6
6
  "name": "Muggle AI",
@@ -2,7 +2,7 @@
2
2
  "name": "muggle",
3
3
  "displayName": "Muggle AI",
4
4
  "description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
5
- "version": "5.31.0-staging.276",
5
+ "version": "5.31.0-staging.277",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@muggleai/works@5.31.0-staging.276",
7
+ "@muggleai/works@5.31.0-staging.277",
8
8
  "serve"
9
9
  ]
10
10
  }
@@ -137,6 +137,7 @@ var RECONCILE_REPLAY_TIMEOUT_MS = 15e3;
137
137
  var GUARDRAIL_SUBCOMMAND_ASSIGNMENT = /^GUARDRAIL_SUBCOMMAND="([\w-]+)"/m;
138
138
  var HOOK_COMMAND_SCRIPTS = /^set -- ([^;]+);/;
139
139
  var GUARDRAIL_DISPATCH_SCRIPT = "guardrail-dispatch.sh";
140
+ var MUGGLE_DO_STAGES_AFTER_BUILD = "impact \u2192 unit tests \u2192 PR \u2192 E2E \u2192 watcher";
140
141
 
141
142
  // src/guardrails/store/fileLock.ts
142
143
  function isProcessAlive(pid) {
@@ -3305,7 +3306,8 @@ function watchGate() {
3305
3306
  if (state.watchSkipped !== true && prsAwaitingStateScan(unwatchedPrUrls, state, prStateScan).length > 0) {
3306
3307
  startBackgroundJob("scan-watch-pr-states" /* ScanWatchPrStates */, [sessionId]);
3307
3308
  }
3308
- const decision = watchGateDecision(state, resolveOwedWatchPrs(unwatchedPrUrls, state, prStateScan));
3309
+ const owedWatchPrUrls = resolveOwedWatchPrs(withoutDelegatedPrs(unwatchedPrUrls, state, Date.now()), state, prStateScan);
3310
+ const decision = watchGateDecision(state, owedWatchPrUrls);
3309
3311
  if (decision.action === "release" /* Release */) return releaseGate("watchReleased");
3310
3312
  if (decision.action === "none" /* None */) return "{}";
3311
3313
  recordGateBlock("watchBlockCount", decision.blockCount);
@@ -3359,7 +3361,7 @@ function buildFollowthroughGate() {
3359
3361
  if (decision.action === "release" /* Release */) return releaseGate("buildSkipped");
3360
3362
  if (decision.action === "none" /* None */) return "{}";
3361
3363
  recordGateBlock("buildBlockCount", decision.blockCount);
3362
- const reason = decision.blockCount === 1 ? `Do not end the turn yet. This session took a build/implement/fix request but no PR was opened. A root cause written into the transcript ships nothing \u2014 once the session ends it is gone, and the watcher gate never fires because it only looks at PRs that exist. Carry the work to a PR via ${muggleSkill("/muggle-do")}, which runs requirements \u2192 build \u2192 impact \u2192 unit tests \u2192 E2E \u2192 PR \u2192 watcher. If no PR is owed here (the user changed their mind, the fix landed in another repo, the answer was advice rather than a change), tell the user why and run \`echo "MUGGLE_BUILD_SKIP: <reason>"\` \u2014 that records the skip and keeps this gate quiet for the rest of the session.` : `Build request still unanswered \u2014 no PR opened (reminder ${decision.blockCount}/${MAX_BUILD_BLOCKS}): carry it to a PR via ${muggleSkill("/muggle-do")}, or record a legitimate skip via \`echo "MUGGLE_BUILD_SKIP: <reason>"\`.`;
3364
+ const reason = decision.blockCount === 1 ? `Do not end the turn yet. This session took a build/implement/fix request but no PR was opened. A root cause written into the transcript ships nothing \u2014 once the session ends it is gone, and the watcher gate never fires because it only looks at PRs that exist. Carry the work to a PR via ${muggleSkill("/muggle-do")}, which runs requirements \u2192 build \u2192 ${MUGGLE_DO_STAGES_AFTER_BUILD}. If no PR is owed here (the user changed their mind, the fix landed in another repo, the answer was advice rather than a change), tell the user why and run \`echo "MUGGLE_BUILD_SKIP: <reason>"\` \u2014 that records the skip and keeps this gate quiet for the rest of the session.` : `Build request still unanswered \u2014 no PR opened (reminder ${decision.blockCount}/${MAX_BUILD_BLOCKS}): carry it to a PR via ${muggleSkill("/muggle-do")}, or record a legitimate skip via \`echo "MUGGLE_BUILD_SKIP: <reason>"\`.`;
3363
3365
  return blockStop(reason, host);
3364
3366
  }
3365
3367
  function walkthroughGate() {
@@ -3482,7 +3484,7 @@ function buildRouter() {
3482
3484
  if (readState(sessionId).buildIntentRouted) return "{}";
3483
3485
  commitStateMutation(sessionId, { kind: "fields" /* Fields */, fields: { buildIntentRouted: true } });
3484
3486
  if (isMuggleDoInvoked) return "{}";
3485
- const ctx = `This looks like a build/implement/fix request. Per the autoRouteBuildToMuggleDo preference, route it through ${muggleSkill("/muggle-do")} \u2014 which runs requirements \u2192 build (delegated to superpowers' design\u2192plan\u2192review) \u2192 impact \u2192 unit tests \u2192 E2E \u2192 PR \u2192 watcher. If autoRouteBuildToMuggleDo=always, enter that flow; if =ask, offer it; if =never, proceed normally.`;
3487
+ const ctx = `This looks like a build/implement/fix request. Per the autoRouteBuildToMuggleDo preference, route it through ${muggleSkill("/muggle-do")} \u2014 which runs requirements \u2192 build (delegated to superpowers' design\u2192plan\u2192review) \u2192 ${MUGGLE_DO_STAGES_AFTER_BUILD}. If autoRouteBuildToMuggleDo=always, enter that flow; if =ask, offer it; if =never, proceed normally.`;
3486
3488
  return envelope("UserPromptSubmit", ctx, host);
3487
3489
  }
3488
3490
  function closingAssistantText() {
@@ -34,7 +34,7 @@ The agent knows only its brief. Pass every value itself, never a pointer to the
34
34
 
35
35
  - The change's slug and absolute session directory.
36
36
  - Repo, worktree path, branch, base branch, and the head SHA under test — for a wave, one entry per member, from its `Wave members` block.
37
- - The PR URL when the PR already exists (a repair re-run, or a PR opened outside `muggle-do`) — for a wave, every member's. Muggle reads PR URLs from the brief to know which PR's walkthrough is waiting on this run, so leave none out and add none that belong to another change.
37
+ - The change's PR URL — for a wave, every member's. Stage 5.5 opened it before this run; only an `autoCreatePR` skip leaves none. Muggle reads PR URLs from the brief to know which PR's walkthrough is waiting on this run, so leave none out and add none that belong to another change.
38
38
  - `Validation`, `localUrl`, `projectId`, and test-credential status from `state.md`.
39
39
  - The `e2eScope` from impact analysis, verbatim; a Stage 7.5 re-run passes only its affected test cases.
40
40
  - The acceptance criteria from `requirements.md`.
@@ -63,7 +63,7 @@ On the agent's completion notification, the main session:
63
63
 
64
64
  1. **Checks freshness.** Every `Tested:` line must name its branch's current head. A branch that moved since dispatch — any member's, for a wave — makes the report stale: queue the change again and post nothing.
65
65
  2. **Records it** — `E2E dispatch: reported — …` in `state.md`.
66
- 3. **Continues that change from Stage 7**, in its worktree. [`open-prs/forward.md`](open-prs/forward.md) opens its PR with this report — every member's PR for a wave, in the wave order of [`wave-e2e.md`](wave-e2e.md) Stage 7; when a PR already exists, the report settles that PR's walkthrough through Mode A with its number and repo passed explicitly — never from the current branch, since the session's cwd may be another change's worktree.
66
+ 3. **Continues that change from Stage 7**, in its worktree. [`open-prs/forward.md`](open-prs/forward.md) settles the walkthrough of the PR Stage 5.5 opened with this report — every member's PR for a wave, per [`wave-e2e.md`](wave-e2e.md) Stage 7 — through Mode A with its number and repo passed explicitly, never from the current branch, since the session's cwd may be another change's worktree.
67
67
  4. **Runs Stage 7.5** inline. A repair pass's re-run returns to this lane, behind any local run in flight.
68
68
  5. **Arms the change's watcher** once 7.5 clears (Stage 8).
69
69
  6. **Dispatches the next queued change.**
@@ -76,7 +76,7 @@ A session that fixed bugs by hand and opened each PR itself follows the same lan
76
76
 
77
77
  ## Enforcement
78
78
 
79
- - Dispatching a background acceptance-tester holds the E2E and debug-path Stop gates off for a bounded window, and for that window the walkthrough gate sets aside the PRs its brief names — the session is never steered into a second browser run alongside the agent's.
79
+ - Dispatching a background acceptance-tester holds the E2E and debug-path Stop gates off for a bounded window, and for that window the walkthrough and watch gates set aside the PRs its brief names — the session is never steered into a second browser run alongside the agent's, nor into arming a watch on a PR whose run has not come back.
80
80
  - A walkthrough posted to one PR settles that PR only; every other PR the session opened is still checked for its own.
81
81
  - A skip declaration settles only the PRs of the change it is declared in — the working tree's repo and branch: the PRs opened for that branch and its branch's PR, never another change's, wherever the session's directory sat when a PR was opened.
82
82
  - Every PR's walkthrough check stays red until its slot is settled, so a change whose run never came back cannot pass as tested.
@@ -33,7 +33,7 @@ For each affected repo:
33
33
 
34
34
  ## Delegation
35
35
 
36
- For a non-trivial change — multiple files, real design surface, or anything you would otherwise brainstorm before coding — run the implementation through superpowers' design → plan → subagent-driven build, then return to this stage's Output. That is a runtime hand-off (an action), not a doc dependency; do not encode superpowers' internals here. Routing a build request into this pipeline (the `autoRouteBuildToMuggleDo` front-door guardrail) exists to combine superpowers' design rigor with this pipeline's impact analysis, E2E, PR, and watcher — neither delivers both alone.
36
+ For a non-trivial change — multiple files, real design surface, or anything you would otherwise brainstorm before coding — run the implementation through superpowers' design → plan → subagent-driven build, then return to this stage's Output. That is a runtime hand-off (an action), not a doc dependency; do not encode superpowers' internals here. Routing a build request into this pipeline (the `autoRouteBuildToMuggleDo` front-door guardrail) exists to combine superpowers' design rigor with this pipeline's impact analysis, PR, E2E, and watcher — neither delivers both alone.
37
37
 
38
38
  ## Output
39
39
 
@@ -50,6 +50,7 @@ You receive everything from `state.md` already — pre-flight resolved it:
50
50
  - The list of changed repos, files, and a summary of changes
51
51
  - The requirements goal
52
52
  - The `e2eScope` from impact analysis — the impacted, retired, and new use cases and test cases this stage runs
53
+ - In a forward run, the PR Stage 5.5 opened from the working tree — the run's [PR progress](../_shared/dev-loop/run.md#pr-progress) shows on it, and Stage 7 settles its walkthrough
53
54
 
54
55
  ## Your Job
55
56
 
@@ -79,9 +80,9 @@ A scope with deliverable checks and no browser case runs none of these and goes
79
80
  2. **Auth** — `muggle-remote-auth-status` must be `authenticated`; else escalate.
80
81
  3. **Identity tenant/domain match** — if test credentials were marked `existing`, confirm the repo's configured identity tenant/domain matches the recorded tenant/domain. Mismatch → halt.
81
82
 
82
- **Readiness halt.** The cycle planned a browser run and still owes it, so an app that cannot be brought up halts the cycle and is never a `SKIPPED` exit: declare no skip code, write no waiver, run nothing downstream. Report the URL probed, what `muggle-test-prepare` tried (the start command and how it failed), and that no PR was opened or updated; then end the turn and wait for the user. This holds for unattended runs and for a request that asked for the PR. A forward cycle opens no PR, posts no walkthrough, runs no Stage 7.5 and arms no watcher; a watcher cycle pushes nothing and escalates through its own path, which keeps the watcher running. A run that failed because the app never answered (connection refused, page never loaded) returns to item 1, and still down is this halt. A Stop-hook reminder that E2E is still owed does not lift it.
83
+ **Readiness halt.** The cycle planned a browser run and still owes it, so an app that cannot be brought up halts the cycle and is never a `SKIPPED` exit: declare no skip code, write no waiver, run nothing downstream. Report the URL probed, what `muggle-test-prepare` tried (the start command and how it failed), and that the PR's walkthrough is unsettled; then end the turn and wait for the user. This holds for unattended runs and for a request that asked for the PR. A forward cycle leaves its PR open with the walkthrough unsettled, so its walkthrough check stays red: it posts no walkthrough, runs no Stage 7.5 and arms no watcher. Nothing more is owed that PR until the app answers, so run `echo "MUGGLE_WALKTHROUGH_SKIP: readiness halt — <PR URL>"` and `echo "MUGGLE_WATCH_SKIP: readiness halt — <PR URL>"`, which keep the Stop gates from pressing for a walkthrough or watch the halt withholds. A watcher cycle pushes nothing and escalates through its own path, which keeps the watcher running. A run that failed because the app never answered (connection refused, page never loaded) returns to item 1, and still down is this halt. A Stop-hook reminder that E2E is still owed does not lift it.
83
84
 
84
- Only the app not answering is this halt. A run the runner itself lost while readiness passes — an Electron or MCP crash, a Studio abort before the first step — is a failing run, however often it repeats: record it in the Failed block and go on to Stage 7, which opens the PR `[E2E FAILING]` for Stage 7.5 to classify.
85
+ Only the app not answering is this halt. A run the runner itself lost while readiness passes — an Electron or MCP crash, a Studio abort before the first step — is a failing run, however often it repeats: record it in the Failed block and go on to Stage 7, which retitles the PR `[E2E FAILING]` for Stage 7.5 to classify.
85
86
 
86
87
  ### Step 1: Authentication already verified
87
88
 
@@ -1,102 +1,126 @@
1
- # Open PR — forward mode
2
-
3
- Forward pipeline's Stage 7. Invoked by `/muggle-do` after stages 1–6 of a fresh feature. Opens the change — a PR on GitHub (`gh pr create`) or an MR on GitLab (`glab mr create`), provider resolved in Step 5 — then seeds session state and arms the first watch.
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) — inline, or returned by this change's background acceptance-tester ([`../background-e2e.md`](../background-e2e.md)); schema in [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md). Only this change's own report: a sibling change's run never settles this PR.
16
-
17
- ## Wave cycles
18
-
19
- `state.md` records `Wave: on` → follow the Stage 7 ordering in [`../wave-e2e.md`](../wave-e2e.md): open every wave PR before settling any walkthrough, add the `## Wave` section to each body, give every member the same title prefix, and settle each PR's walkthrough with the same report (Mode A, PR number + repo passed explicitly). The per-repo steps below run with those changes.
20
-
21
- ## Per repo
22
-
23
- 0. **`autoCreatePR` gate** — apply per [`../../muggle-preferences/preference-gates/autoCreatePR.md`](../../muggle-preferences/preference-gates/autoCreatePR.md), its explicit-request rule included. On skip, record the reason in `result.md` and move on.
24
-
25
- 1. **Sync with the base:** run [`../../_shared/sync-branch-with-base.md`](../../_shared/sync-branch-with-base.md) with `base` set to the PR target recorded in `state.md` — gated by [`autoRebase`](../../muggle-preferences/preference-gates/autoRebase.md), fires only when `behind > 0`. The branch is not on the remote yet, so the rebase lands without a force-push. Sync here rather than trusting stage 6: a `unit-only` or `skip` run never reaches the E2E stage's sync at all, and on a run that does, the base moves freely while stages 3–6 build and test. If the sync escalates — conflicts under `autoResolveConflicts=never`, or a resolution that fails the verify-or-rollback gate — stop and report; never open a PR on a tree that did not verify.
26
-
27
- 1.5. **CI check:** GitHub repos only, gated by [`autoInstallCiWorkflow`](../../muggle-preferences/preference-gates/autoInstallCiWorkflow.md).
28
- - **Tree lacks `.github/workflows/muggle-walkthrough.yml`** — on `always`, or `CI check: add` recorded by pre-flight, run `muggle ci-install` in the working tree and commit the workflow as `ci: add the Muggle E2E walkthrough check`, so it ships in this PR. On `never` or `CI check: skip`, leave CI alone.
29
- - **Tree has it** — unless the gate resolves to `never`, run `muggle ci-install` without `--force`. It replaces only an unedited copy an earlier release wrote and leaves a current or edited one alone, so nothing needs asking. When it reports `Updated`, commit the file as `ci: update the Muggle E2E walkthrough check`.
30
-
31
- 2. **Push:** Execute per [`../../_shared/vcs/common/push-to-branch.md`](../../_shared/vcs/common/push-to-branch.md) — the tool-agnostic instruction for pushing commits (handles the signing gate and directs to provider-specific recipes).
32
-
33
- 3. **Title** (under 70 chars):
34
- - E2E report exists and has failures → `[E2E FAILING] <goal>`
35
- - No E2E report (validation was `unit-only` or `skip`) → `[UNVERIFIED] <goal>` or `[UNIT-ONLY] <goal>` to match the validation strategy
36
- - Otherwise → `<goal>`
37
-
38
- 4. **Body** — assemble in order:
39
- - `## Goal` — from requirements.
40
- - `## Acceptance Criteria` — bulleted; omit if empty.
41
- - `## Changes` — summary of what changed in this repo.
42
- - `## Validation` — one line: link to E2E report, `unit-only`, or `skip — <reason>`. A report with a **Deliverables** block adds its tables below that line, per [`../../_shared/deliverable-categories.md`](../../_shared/deliverable-categories.md#evidence-on-the-pr).
43
- - The walkthrough does **not** go in the body. It settles the PR's designated walkthrough comment in Step 6, so a rerun updates one comment instead of rewriting the description.
44
- - **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.
45
-
46
- 5. **Create:** resolve the provider per [`../../_shared/vcs/detect-vcs.md`](../../_shared/vcs/detect-vcs.md).
47
- - `github` → `gh pr create --title "..." --body-file <signed-file> --head <branch>`, passing the file signed in Step 4. Capture the PR URL and number.
48
- - `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.
49
-
50
- 6. **Settle the designated walkthrough comment.** Creating the PR reserves one comment for the walkthrough, marked `muggle-pr-walkthrough` and empty until settled — the PR's walkthrough check fails while it stays that way.
51
- - E2E report exists → fire [`postPRVisualWalkthrough`](../../muggle-preferences/preference-gates/postPRVisualWalkthrough.md); on skip, leave the comment to the gate's own skip record. Otherwise invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode A with the PR number and repo passed explicitly — it fills the reserved comment in place. Explicit because a session carrying several changes may sit in another change's worktree, where the current branch's PR is the wrong one.
52
- - No E2E report (validation was `unit-only` or `skip`), a `SKIPPED` report, or a report whose browser part was skipped (a `**Browser skip:**` line, or deliverable entries and no test cases) → now that the PR exists, re-declare the skip Stage 6 recorded with the same code, from this PR's worktree: `echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"`, codes per [`../e2e-acceptance.md`](../e2e-acceptance.md). An accepted code settles only the PRs of that worktree's change — the PRs opened from it and its checked-out branch's PR; a free-text reason is rejected and leaves it unsettled. A PR whose title says `[UNIT-ONLY]` still owes reviewers the why. The write runs detached, so confirm it landed: the walkthrough Stop gate holds the turn while the PR still shows the placeholder, and its block names a foreground command that prints `{"settled":true}` once the PR reads back the skip — run it, never a hand-written PATCH.
53
-
54
- 7. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once using the provider resolved in Step 5 — `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`.
55
-
56
- ## Stage 7.5 gate
57
-
58
- The E2E repair loop ([`../e2e-repair.md`](../e2e-repair.md)) runs once this stage has created the PR and posted the walkthrough, and before the handoff below. Proceed only on its `green`, `waived`, or `iteration-cap` clearance — handing a red run to a watcher puts it in front of reviewers with nobody looking at the failures. When Stage 6 recorded `PASS` or `SKIPPED` the loop clears in its own Step 0 at no cost, so there is never a reason to skip it.
59
-
60
- ## Stage 8 handoff
61
-
62
- After every repo is processed and Stage 7.5 has cleared, build the watcher manifest and arm one watch per opened PR. The arms are the LAST action this stage takes.
63
-
64
- Write `~/.muggle-ai/muggle-do/sessions/<slug>/prs.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#prsjson):
65
-
66
- ```json
67
- [{ "repo": "owner/repo", "number": 142, "url": "...", "head_sha": "...", "state": "open" }]
68
- ```
69
-
70
- Seed `~/.muggle-ai/muggle-do/sessions/<slug>/last_seen.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#last_seenjson) — empty-watermark shape with `pushed_shas: []`. Forward mode never has prior reviews to skip, so `lastBodyReviewId: 0`.
71
-
72
- **Do not** seed `cycle.json` or `requirements.md`. The watcher is a dumb pipe; `/muggle-do` reads reviews off GitHub.
73
-
74
- For each entry in `prs.json`, arm its watch per [`../../muggle-pr-followup/arm-watcher.md`](../../muggle-pr-followup/arm-watcher.md) — the same procedure [`../respawn-watcher.md`](../respawn-watcher.md) settles every later watch with: one drain tick, the watermark seeded from it, then one persistent labeled monitor. Never a recurring `/loop` cron — every cron fire is a model turn, while the monitor polls token-free. The PR counts as watched only once that monitor's loop is running; a seeded slot alone is not a watch.
75
-
76
- If `prs.json` is empty, **do not arm** — record the reason in `result.md` and exit.
77
-
78
- ## Invariants
79
-
80
- - Branch synced with its base before the push, never after; PR creation per non-skipped repo; designated walkthrough comment settled via Mode A, or its skip reason stated; `prs.json`+`last_seen.json` seeded (no `cycle.json`, no `requirements.md`); Stage 7.5 cleared before arming; arming per `arm-watcher.md` is the last action.
81
-
82
- ## Output
83
-
84
- **Synced with base:** repo → `rebased onto <base> (<n> behind)` | `already up to date` | `skipped (autoRebase)`
85
- **PRs Created:** repo → URL
86
- **Skipped:** repo → reason (when `autoCreatePR` short-circuited)
87
- **Overflow comments posted:** repo → PR #
88
- **Stage 7.5:** `green` | `waived — <reason>` | `iteration-cap`
89
- **Stage 8:** `Watching <N> PR(s) — one watch loop per PR, armed per arm-watcher.md` | `No PRs to watch — stage 8 not armed`
90
- **Errors:** repo → message
91
-
92
- ## Post-merge cleanup
93
-
94
- Gated by `autoCleanup`. Triggered when the watcher's terminal tick observes the merge and dispatches `/muggle-do`'s cleanup directive ([`../cleanup.md`](../cleanup.md)) — never from this stage. See [`../../_shared/post-merge-cleanup.md`](../../_shared/post-merge-cleanup.md).
95
-
96
- Append one short reminder tied to the gate value:
97
-
98
- - `always` → `Once merged, I'll run the cleanup sequence automatically.`
99
- - `never` → omit.
100
- - `ask` / absent → `Once merged, I'll check with you about cleanup.`
101
-
102
- Regardless of the gate, also append: `Once it's merged or closed, I'll move to the next plan item — or ask where to go next if there's no plan.`
1
+ # Open PR — forward mode
2
+
3
+ The forward pipeline's PR stages, which run on either side of Stage 6. **Stage 5.5** opens the change — a PR on GitHub (`gh pr create`) or an MR on GitLab (`glab mr create`), provider resolved in Step 5 — as soon as unit tests pass, before any browser run. **Stage 7** settles that run's result on it, then seeds session state and arms the first watch.
4
+
5
+ The PR goes up first because opening it needs nothing from the run: CI and reviewers start on the diff while E2E runs, and the run's [PR progress](../../_shared/dev-loop/run.md#pr-progress) fills the PR's walkthrough comment case by case. The walkthrough check stays red until Stage 7 settles that comment, so an open PR never passes as tested before its run lands.
6
+
7
+ ## Turn preambles
8
+
9
+ ```
10
+ **Stage 5.5 — Open PR** — pushing the branch and opening the PR before E2E.
11
+ ```
12
+
13
+ ```
14
+ **Stage 7 — Report on the PR** — settling the walkthrough with the E2E result.
15
+ ```
16
+
17
+ ## Inputs
18
+
19
+ - Per-repo: name, path, branch.
20
+ - Requirements: goal, acceptance criteria.
21
+ - The validation strategy from `state.md` — Stage 5.5 titles the PR from it, since no run has happened yet.
22
+ - Stage 7 only: the **optional** E2E acceptance report from Stage 6. Produced by [`../e2e-acceptance.md`](../e2e-acceptance.md) — inline, or returned by this change's background acceptance-tester ([`../background-e2e.md`](../background-e2e.md)); schema in [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md). Only this change's own report: a sibling change's run never settles this PR.
23
+
24
+ ## Wave cycles
25
+
26
+ `state.md` records `Wave: on` → follow the Stage 5.5 and Stage 7 ordering in [`../wave-e2e.md`](../wave-e2e.md): open every wave PR before Stage 6 starts, add the `## Wave` section to each body, give every member the same title prefix, and settle each PR's walkthrough with the same report (Mode A, PR number + repo passed explicitly). The per-repo steps below run with those changes.
27
+
28
+ ## Stage 5.5 — open the PR
29
+
30
+ Per repo:
31
+
32
+ 0. **`autoCreatePR` gate** — apply per [`../../muggle-preferences/preference-gates/autoCreatePR.md`](../../muggle-preferences/preference-gates/autoCreatePR.md), its explicit-request rule included. On skip, record the reason in `result.md` and move on; Stage 6 still runs.
33
+
34
+ 1. **Sync with the base:** run [`../../_shared/sync-branch-with-base.md`](../../_shared/sync-branch-with-base.md) with `base` set to the PR target recorded in `state.md` — gated by [`autoRebase`](../../muggle-preferences/preference-gates/autoRebase.md), fires only when `behind > 0`. The branch is not on the remote yet, so the rebase lands without a force-push. Sync here rather than leaving it to Stage 6: a `unit-only` or `skip` run never reaches the E2E stage's sync at all, and on a run that does, the PR opens before that sync. If the sync escalates — conflicts under `autoResolveConflicts=never`, or a resolution that fails the verify-or-rollback gate — stop and report; never open a PR on a tree that did not verify.
35
+
36
+ 1.5. **CI check:** GitHub repos only, gated by [`autoInstallCiWorkflow`](../../muggle-preferences/preference-gates/autoInstallCiWorkflow.md).
37
+ - **Tree lacks `.github/workflows/muggle-walkthrough.yml`** — on `always`, or `CI check: add` recorded by pre-flight, run `muggle ci-install` in the working tree and commit the workflow as `ci: add the Muggle E2E walkthrough check`, so it ships in this PR. On `never` or `CI check: skip`, leave CI alone.
38
+ - **Tree has it** — unless the gate resolves to `never`, run `muggle ci-install` without `--force`. It replaces only an unedited copy an earlier release wrote and leaves a current or edited one alone, so nothing needs asking. When it reports `Updated`, commit the file as `ci: update the Muggle E2E walkthrough check`.
39
+
40
+ 2. **Push:** Execute per [`../../_shared/vcs/common/push-to-branch.md`](../../_shared/vcs/common/push-to-branch.md) — the tool-agnostic instruction for pushing commits (handles the signing gate and directs to provider-specific recipes).
41
+
42
+ 3. **Title** (under 70 chars), from the validation strategy:
43
+ - `unit-only` → `[UNIT-ONLY] <goal>`
44
+ - `skip` → `[UNVERIFIED] <goal>`
45
+ - Otherwise → `<goal>`. Stage 7 adds `[E2E FAILING]` if the run fails.
46
+
47
+ The prefix goes on at creation: no later stage adds `[UNIT-ONLY]` or `[UNVERIFIED]`.
48
+
49
+ 4. **Body** — assemble in order:
50
+ - `## Goal` — from requirements.
51
+ - `## Acceptance Criteria` — bulleted; omit if empty.
52
+ - `## Changes` — summary of what changed in this repo.
53
+ - `## Validation` — one line naming the strategy: `local-e2e — results in the walkthrough comment`, `unit-only`, or `skip — <reason>`. Stage 7 adds a report's **Deliverables** tables below it.
54
+ - The walkthrough does **not** go in the body. It settles the PR's designated walkthrough comment in Stage 7, so a rerun updates one comment instead of rewriting the description.
55
+ - **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). `editable` is the mode a description carries so later refreshes replace it instead of stacking.
56
+
57
+ 5. **Create:** resolve the provider per [`../../_shared/vcs/detect-vcs.md`](../../_shared/vcs/detect-vcs.md).
58
+ - `github` → `gh pr create --title "<Step 3 title>" --body-file <signed-file> --head <branch>`, passing Step 3's title, prefix included, and the file signed in Step 4. Capture the PR URL and number.
59
+ - `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 "<Step 3 title>" --description "..."`. Capture the MR URL and iid.
60
+
61
+ A create that fails ends the cycle for that repo: report it under **Errors** and run no E2E for it, since the run would have no PR to settle.
62
+
63
+ ## Stage 7 — report on the PR
64
+
65
+ Runs once Stage 6 has recorded this change's verdict; a Stage 6 readiness halt never reaches it. Per repo, using the provider resolved in Stage 5.5:
66
+
67
+ 1. **Push the tested head.** Stage 6's readiness check syncs the worktree onto the latest base, so a base that moved after Stage 5.5 leaves HEAD — the commit the run tested — rebased past what the PR shows. When HEAD differs from the PR's head, push it per [`../../_shared/vcs/common/push-to-branch.md`](../../_shared/vcs/common/push-to-branch.md) with `git push --force-with-lease`, since the rebase rewrote commits the remote already has. Never sync again here: a rebase after the run would put a tree the run never tested under its walkthrough.
68
+
69
+ 2. **Title:** a report with failures → retitle `[E2E FAILING] <goal>` per [`../../_shared/vcs/github/pr-edit.md`](../../_shared/vcs/github/pr-edit.md) (`gitlab`: [`../../_shared/vcs/gitlab/mr-edit.md`](../../_shared/vcs/gitlab/mr-edit.md)). Otherwise the Stage 5.5 title stands.
70
+
71
+ 3. **Body:** a report with a **Deliverables** block → add its tables below `## Validation` per [`../../_shared/deliverable-categories.md`](../../_shared/deliverable-categories.md#evidence-on-the-pr), with the `--body-file` form of the same edit recipe. Delete the old signature block from the `<!-- muggle-works:signature -->` marker to the end and re-sign the body as Stage 5.5 Step 4 did, so it keeps exactly one signature.
72
+
73
+ 4. **Settle the designated walkthrough comment.** Creating the PR reserved one comment for the walkthrough, marked `muggle-pr-walkthrough`; the run's PR progress fills it with per-case rows, and the PR's walkthrough check fails until it is settled.
74
+ - E2E report exists → fire [`postPRVisualWalkthrough`](../../muggle-preferences/preference-gates/postPRVisualWalkthrough.md); on skip, leave the comment to the gate's own skip record. Otherwise invoke [`../../muggle-pr-visual-walkthrough/SKILL.md`](../../muggle-pr-visual-walkthrough/SKILL.md) Mode A with the PR number and repo passed explicitly — it replaces the progress rows with the walkthrough in place. Explicit because a session carrying several changes may sit in another change's worktree, where the current branch's PR is the wrong one.
75
+ - No E2E report (validation was `unit-only` or `skip`), a `SKIPPED` report, or a report whose browser part was skipped (a `**Browser skip:**` line, or deliverable entries and no test cases) → the skip Stage 6 declared from this PR's worktree settles it. A background run returns its skip code instead of declaring it: declare it now, from this PR's worktree, with the same code — `echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"`, codes per [`../e2e-acceptance.md`](../e2e-acceptance.md). An accepted code settles only the PRs of that worktree's change — the PRs opened from it and its checked-out branch's PR; a free-text reason is rejected and leaves it unsettled. A PR whose title says `[UNIT-ONLY]` still owes reviewers the why. The write runs detached, so confirm it landed: the walkthrough Stop gate holds the turn while the PR still shows the placeholder, and its block names a foreground command that prints `{"settled":true}` once the PR reads back the skip — run it, never a hand-written PATCH.
76
+
77
+ 5. **Overflow comment:** if the walkthrough skill returned a non-null `comment`, post it once — `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`.
78
+
79
+ ## Stage 7.5 gate
80
+
81
+ The E2E repair loop ([`../e2e-repair.md`](../e2e-repair.md)) runs once Stage 7 has settled the walkthrough, and before the handoff below. Proceed only on its `green`, `waived`, or `iteration-cap` clearance — handing a red run to a watcher puts it in front of reviewers with nobody looking at the failures. When Stage 6 recorded `PASS` or `SKIPPED` the loop clears in its own Step 0 at no cost, so there is never a reason to skip it.
82
+
83
+ ## Stage 8 handoff
84
+
85
+ After every repo is processed and Stage 7.5 has cleared, build the watcher manifest and arm one watch per opened PR. The arms are the LAST action this stage takes.
86
+
87
+ Write `~/.muggle-ai/muggle-do/sessions/<slug>/prs.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#prsjson):
88
+
89
+ ```json
90
+ [{ "repo": "owner/repo", "number": 142, "url": "...", "head_sha": "...", "state": "open" }]
91
+ ```
92
+
93
+ Seed `~/.muggle-ai/muggle-do/sessions/<slug>/last_seen.json` per [`../../muggle-pr-followup/state-schemas.md`](../../muggle-pr-followup/state-schemas.md#last_seenjson) — empty-watermark shape with `pushed_shas: []`. Forward mode never has prior reviews to skip, so `lastBodyReviewId: 0`.
94
+
95
+ **Do not** seed `cycle.json` or `requirements.md`. The watcher is a dumb pipe; `/muggle-do` reads reviews off GitHub.
96
+
97
+ For each entry in `prs.json`, arm its watch per [`../../muggle-pr-followup/arm-watcher.md`](../../muggle-pr-followup/arm-watcher.md) — the same procedure [`../respawn-watcher.md`](../respawn-watcher.md) settles every later watch with: one drain tick, the watermark seeded from it, then one persistent labeled monitor. Never a recurring `/loop` cron — every cron fire is a model turn, while the monitor polls token-free. The PR counts as watched only once that monitor's loop is running; a seeded slot alone is not a watch.
98
+
99
+ If `prs.json` is empty, **do not arm** — record the reason in `result.md` and exit.
100
+
101
+ ## Invariants
102
+
103
+ - Branch synced with its base before the push, never after; PR opened per non-skipped repo before Stage 6 starts; the tested head is the PR's head before its walkthrough settles; designated walkthrough comment settled via Mode A, or its skip reason stated; `prs.json`+`last_seen.json` seeded (no `cycle.json`, no `requirements.md`); Stage 7.5 cleared before arming; arming per `arm-watcher.md` is the last action.
104
+
105
+ ## Output
106
+
107
+ **Synced with base:** repo → `rebased onto <base> (<n> behind)` | `already up to date` | `skipped (autoRebase)`
108
+ **PRs Created:** repo → URL
109
+ **Skipped:** repo → reason (when `autoCreatePR` short-circuited)
110
+ **Tested head:** repo → `already the PR head` | `force-pushed <short-sha> (Stage 6 synced onto a moved base)`
111
+ **Overflow comments posted:** repo → PR #
112
+ **Stage 7.5:** `green` | `waived — <reason>` | `iteration-cap`
113
+ **Stage 8:** `Watching <N> PR(s) — one watch loop per PR, armed per arm-watcher.md` | `No PRs to watch — stage 8 not armed`
114
+ **Errors:** repo → message
115
+
116
+ ## Post-merge cleanup
117
+
118
+ Gated by `autoCleanup`. Triggered when the watcher's terminal tick observes the merge and dispatches `/muggle-do`'s cleanup directive ([`../cleanup.md`](../cleanup.md)) — never from this stage. See [`../../_shared/post-merge-cleanup.md`](../../_shared/post-merge-cleanup.md).
119
+
120
+ Append one short reminder tied to the gate value:
121
+
122
+ - `always` → `Once merged, I'll run the cleanup sequence automatically.`
123
+ - `never` → omit.
124
+ - `ask` / absent → `Once merged, I'll check with you about cleanup.`
125
+
126
+ Regardless of the gate, also append: `Once it's merged or closed, I'll move to the next plan item — or ask where to go next if there's no plan.`
@@ -1,8 +1,8 @@
1
- # Create-or-Update PR (Stage 7)
1
+ # Create-or-Update PR (Stages 5.5 and 7)
2
2
 
3
3
  Two modes, set by the `/muggle-do` invocation. Each consumer loads only its mode's file.
4
4
 
5
5
  | Mode | When | Procedure |
6
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) |
7
+ | Forward | A fresh feature, called by `/muggle-do`'s forward pipeline. Opens the PR after Stage 5 (Stage 5.5), settles its walkthrough after Stage 6 (Stage 7), seeds state, dispatches the first watcher. | [`open-prs/forward.md`](open-prs/forward.md) |
8
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) |
@@ -26,7 +26,7 @@ Under `## Pre-flight answers`, alongside `Repos:`:
26
26
  - <owner/repo-b> → <worktree path b> (branch <branch>)
27
27
  ```
28
28
 
29
- Stage 7 appends each member's PR number to its line once the PRs exist.
29
+ Stage 5.5 appends each member's PR number to its line once the PRs exist.
30
30
 
31
31
  The worktree path is where that repo's wave branch is checked out — the path its service must start from. Record it per repo; never derive it later from the branch name.
32
32
 
@@ -44,21 +44,27 @@ The report carries one extra metadata block:
44
44
 
45
45
  ```markdown
46
46
  **Wave:**
47
- - <owner/repo-a> @ <head sha> → PR #<n> (filled in at Stage 7)
47
+ - <owner/repo-a> @ <head sha> → PR #<n>
48
48
  - <owner/repo-b> @ <head sha> → PR #<n>
49
49
  ```
50
50
 
51
51
  The SHAs are what the run tested. A result is only valid for the PRs at those SHAs.
52
52
 
53
- ## Stage 7 — post the same result to every PR
53
+ ## Stage 5.5 — open every PR before the run
54
54
 
55
- [`open-prs/forward.md`](open-prs/forward.md) runs per repo as usual, with the wave ordering:
55
+ [`open-prs/forward.md`](open-prs/forward.md) Stage 5.5 runs per repo as usual, with the wave ordering:
56
56
 
57
- 1. **Open every PR first**, before settling any walkthrough. Each PR's walkthrough names its siblings, so all PR numbers must exist before the first one is written.
57
+ 1. **Open every PR** before Stage 6 starts. The run's report and each PR's walkthrough name the siblings, so all PR numbers must exist before the wave stack starts.
58
58
  2. **Body.** Add a `## Wave` section to each PR's body, after `## Validation`: one line per sibling PR (`owner/repo#n`), and the line `Tested together — merge all PRs in this wave together.`
59
- 3. **Title.** Every PR in the wave takes the same E2E prefix. A wave run with failures retitles every member `[E2E FAILING]`, not only the implicated one — the run tested them as one change.
60
- 4. **Walkthrough.** Settle each PR's designated walkthrough comment with the **same** E2E report through [`muggle-pr-visual-walkthrough`](../muggle-pr-visual-walkthrough/SKILL.md) Mode A, passing that PR's number and repo explicitly — Mode A otherwise looks up the PR from the current branch, which in a multi-repo cycle is the wrong repo. The runs, screenshots, and dashboard links are identical on every PR; only the PR it is posted to differs. A `SKIPPED` wave run is re-declared from each member's worktree in turn: a declaration settles only the PR of the tree it runs in.
61
- 5. **Record the PR numbers** on the `Wave members` lines in `state.md` (`… (branch <branch>) → PR #<n>`), so a later cycle on any one PR can find its siblings. Every slot a wave PR's watcher runs from gets the same `Wave` block.
59
+ 3. **Title.** Every PR in the wave takes the same prefix.
60
+ 4. **Record the PR numbers** on the `Wave members` lines in `state.md` (`… (branch <branch>) → PR #<n>`), so a later cycle on any one PR can find its siblings. Every slot a wave PR's watcher runs from gets the same `Wave` block.
61
+
62
+ ## Stage 7 — post the same result to every PR
63
+
64
+ [`open-prs/forward.md`](open-prs/forward.md) Stage 7 runs per repo as usual, with these changes:
65
+
66
+ 1. **Title.** A wave run with failures retitles every member `[E2E FAILING]`, not only the implicated one — the run tested them as one change.
67
+ 2. **Walkthrough.** Settle each PR's designated walkthrough comment with the **same** E2E report through [`muggle-pr-visual-walkthrough`](../muggle-pr-visual-walkthrough/SKILL.md) Mode A, passing that PR's number and repo explicitly — Mode A otherwise looks up the PR from the current branch, which in a multi-repo cycle is the wrong repo. The runs, screenshots, and dashboard links are identical on every PR; only the PR it is posted to differs. A `SKIPPED` wave run is re-declared from each member's worktree in turn: a declaration settles only the PR of the tree it runs in.
62
68
 
63
69
  ## Stage 7.5 — repair across the wave
64
70