@muggleai/works 5.28.0 → 5.29.0-staging.255

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.
@@ -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.28.0"
12
+ "version": "5.29.0-staging.255"
13
13
  }
14
14
  ]
15
15
  }
package/README.md CHANGED
@@ -165,7 +165,7 @@ Nested GitLab namespaces of any depth are handled — the project path is everyt
165
165
 
166
166
  ### The pull-request walkthrough check
167
167
 
168
- When a PR is opened from a session running this plugin, Muggle reserves a comment for the acceptance walkthrough and holds the turn open until it's settled — by the walkthrough, or by a stated reason acceptance testing doesn't apply. A PR that changes only docs, tests, lint/format config, or CI config needs neither: its comment opens already settled as skipped.
168
+ When a PR is opened from a session running this plugin, Muggle reserves a comment for the acceptance walkthrough and holds the turn open until it's settled. The comment moves through three stages, edited in place: **waiting for the E2E feasibility check**; then **E2E running — waiting for the report** or **E2E not feasible** with the reason; then the E2E report. A PR that changes only docs, tests, lint/format config, or CI config opens straight at its reason. A session that ends without a run or a reason leaves the comment at the stage it reached, and the check stays unsettled.
169
169
 
170
170
  A PR opened any other way — the web UI, a teammate without the plugin — never passes through that session, so the same check also runs in GitHub Actions. It fails while the comment is still empty, and whenever the walkthrough reports a failed or inconclusive test case; the check's summary names each one with its failing step and a link to the run. While a new E2E run is underway on the PR, the check stays in progress until that run's walkthrough replaces the previous one. When the Muggle PR watcher sees it red, it repairs the cases, re-runs the whole suite, and re-settles the comment.
171
171
 
@@ -360,6 +360,7 @@ function parentIsAlive(pid) {
360
360
  var REPORT_SENTINEL = "muggle-pr-section";
361
361
  var WALKTHROUGH_SLOT_MARKER = "<!-- muggle-pr-walkthrough:v1 -->";
362
362
  var WALKTHROUGH_SKIPPED_MARKER = "<!-- muggle-pr-walkthrough-status:skipped -->";
363
+ var RETIRED_UNREASONED_SKIP_HEADLINE = "**No E2E run, and no verified reason given.**";
363
364
  var WALKTHROUGH_COMMENT_HEADING = "### Muggle AI \u2014 PR visual walkthrough";
364
365
  var RERUN_NOTICE_OPENING = "<!-- muggle-pr-walkthrough-rerun -->";
365
366
  var SHORT_SHA_LENGTH = 7;
@@ -3303,14 +3304,14 @@ function renderReservedComment() {
3303
3304
  return `${WALKTHROUGH_SLOT_MARKER}
3304
3305
  ${WALKTHROUGH_COMMENT_HEADING}
3305
3306
 
3306
- _Awaiting the E2E acceptance run. Muggle edits this comment in place when the run finishes \u2014 or records why E2E does not apply to this change._`;
3307
+ **Waiting for the E2E feasibility check.** This comment updates in place \u2014 next with the E2E run in progress or why E2E is not feasible for this change, then with the E2E report.`;
3307
3308
  }
3308
3309
  function renderDependencyBotComment(login) {
3309
3310
  return `${WALKTHROUGH_SLOT_MARKER}
3310
3311
  ${WALKTHROUGH_SKIPPED_MARKER}
3311
3312
  ${WALKTHROUGH_COMMENT_HEADING}
3312
3313
 
3313
- **No E2E run \u2014 not applicable.** A dependency update opened by \`${login}\`; no coding session ran on it.`;
3314
+ **E2E not feasible.** A dependency update opened by \`${login}\`; no coding session ran on it.`;
3314
3315
  }
3315
3316
  var shortSha = (sha) => sha.slice(0, SHORT_SHA_LENGTH);
3316
3317
  function renderNoAppLogicChangeComment(declared, headSha, lastFullRun) {
@@ -3340,7 +3341,9 @@ function isRerunInProgress(bodies) {
3340
3341
  function classifyComment(body) {
3341
3342
  if (body.includes(REPORT_SENTINEL)) return "reported" /* Reported */;
3342
3343
  if (!body.includes(WALKTHROUGH_SLOT_MARKER)) return "not-designated" /* NotDesignated */;
3343
- if (body.includes(WALKTHROUGH_SKIPPED_MARKER)) return "skipped" /* Skipped */;
3344
+ if (body.includes(WALKTHROUGH_SKIPPED_MARKER) && !body.includes(RETIRED_UNREASONED_SKIP_HEADLINE)) {
3345
+ return "skipped" /* Skipped */;
3346
+ }
3344
3347
  return "pending" /* Pending */;
3345
3348
  }
3346
3349
  function walkthroughVerdict(bodies) {
@@ -3514,7 +3517,7 @@ var OUTCOME_CONCLUSIONS = {
3514
3517
  };
3515
3518
  var FIXED_SUMMARIES = {
3516
3519
  ["settled" /* Settled */]: "This PR's Muggle AI walkthrough comment is settled \u2014 every E2E acceptance case passed, or it states why E2E does not apply.",
3517
- ["rerun-in-progress" /* RerunInProgress */]: "A new E2E acceptance run is in progress on this PR. The walkthrough comment still shows the previous run; this check settles when the new run's walkthrough replaces it.",
3520
+ ["rerun-in-progress" /* RerunInProgress */]: "An E2E acceptance run is in progress on this PR. This check settles when the run's walkthrough replaces the comment.",
3518
3521
  ["unsettled" /* Unsettled */]: "This PR's Muggle AI walkthrough comment is still empty. Run the E2E acceptance suite and post the walkthrough into it (/muggle:muggle-test), or record why E2E does not apply to this change.",
3519
3522
  ["unreadable" /* Unreadable */]: "The PR's comments could not be read, so the walkthrough duty could not be judged."
3520
3523
  };
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-N3G4AX6M.js';
2
+ import { runCli } from './chunk-XEKNDWXD.js';
3
3
  import './chunk-YMFQHBLJ.js';
4
4
 
5
5
  // src/cli/main.ts
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-N3G4AX6M.js';
1
+ export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-XEKNDWXD.js';
2
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-YMFQHBLJ.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.28.0",
4
+ "version": "5.29.0-staging.255",
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.28.0",
7
+ "@muggleai/works@5.29.0-staging.255",
8
8
  "serve"
9
9
  ],
10
10
  "env": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muggle",
3
- "version": "5.28.0",
3
+ "version": "5.29.0-staging.255",
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.28.0",
5
+ "version": "5.29.0-staging.255",
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.28.0",
7
+ "@muggleai/works@5.29.0-staging.255",
8
8
  "serve"
9
9
  ]
10
10
  }
@@ -47,14 +47,14 @@ Pre-filters are bash builtins only (`guardrail-lib.sh`), so an irrelevant event
47
47
  | `PostToolUse` (Bash) | `guardrail-record-comment-replies.sh` | record | an unresolved-thread fetch named review threads still awaiting an answer (classified by the `<!-- muggle-do:bot -->` marker, never the author login), a threaded reply the provider **confirmed** answered comments in one, or a `MUGGLE_REPLY_SKIP` marker deferred them | — | claim each thread and cover the comments answered, in the per-PR ledger beside the muggle-do slot |
48
48
  | `PreToolUse` (Bash, `if` a command names `gh`/`glab`) | `guardrail-report-format.sh` | **enforce** | a `gh pr comment\|create\|edit` body — or a `gh api … issues/comments/<id>` PATCH editing one — reads like an E2E report but lacks the `build-pr-section` sentinel | — | **deny** — render via `muggle build-pr-section` instead |
49
49
  | `PreToolUse` (muggle local execute/replay) | `guardrail-classify-gate.sh` | **enforce** | `muggle-test` is the skill in play and the target test case has no `pre-execution-classification` this session | — | **deny** — run Step 6f first (it calls `muggle-remote-test-script-list`, which is where the run learns the case has never passed) or record a legitimate skip via `MUGGLE_CLASSIFY_SKIP`. Scoped to `muggle-test`, so the single-target skills that legitimately skip classification are untouched; fails open when the test case can't be resolved |
50
- | `PreToolUse` (muggle local execute/replay) | `guardrail-walkthrough-rerun.sh` | record | a browser run is about to start in a working tree (the tool call's `cwd`) whose branch has a PR, and that PR's walkthrough comment already shows a run or a skip | — | a detached job puts a "new E2E run in progress" banner, with its UTC start time, on top of the walkthrough the run will replace. The comment underneath is untouched; the walkthrough check stays in progress until the run's own walkthrough replaces the whole body, banner included, and the walkthrough gate counts the PR as still owing that walkthrough. A reserved placeholder or a comment already bannered is left alone |
51
- | `Stop` | `guardrail-e2e-gate.sh` | **enforce** | unit tests passed this session, no E2E ran yet, and no skip was recorded | `autoE2ETest` | **block** the turn until E2E runs via `muggle-test` or a `MUGGLE_E2E_SKIP` marker records a legitimate skip (full message once, one-line reminders after; releases after 3 blocks). Quiet while a background acceptance-tester is in flight |
50
+ | `PreToolUse` (muggle local execute/replay) | `guardrail-walkthrough-rerun.sh` | record | a browser run is about to start in a working tree (the tool call's `cwd`) whose branch has a PR with a walkthrough comment | — | a detached job marks the comment with the run in progress and its UTC start time: a feasibility-check placeholder becomes "E2E running — waiting for the report"; a comment that already shows a run or a skip gets a "new E2E run in progress" banner on top, left untouched underneath. The walkthrough check stays in progress until the run's own walkthrough replaces the whole body, and the walkthrough gate counts the PR as still owing that walkthrough. A comment already marked is left alone |
51
+ | `Stop` | `guardrail-e2e-gate.sh` | **enforce** | unit tests passed this session, no E2E ran yet, and no skip was recorded | `autoE2ETest` | **block** the turn until E2E runs via `muggle-test` or a `MUGGLE_E2E_SKIP` marker records a legitimate skip (full message once, one-line reminders after; releases after 3 blocks, leaving the PR's walkthrough comment unsettled). Quiet while a background acceptance-tester is in flight |
52
52
  | `Stop` | `guardrail-terminal-gate.sh` | **enforce** | a PR went terminal this session and the next-options offer hasn't run since — an AskUserQuestion, or, while the session owns an open PR's watch slot, a closing message that asks the user | — | **block** the turn until the post-merge handoff runs (full message once, one-line reminders after; releases after 3 blocks; nothing but the offer resets the counter) |
53
53
  | `Stop` | `guardrail-watch-gate.sh` | **enforce** | a PR opened this session that is still open and has no live watch — no session slot for it is terminal, stopped by its owner, or carries a `watch-heartbeat` touched within 15 minutes; a slot that is only seeded does not count | `autoWatchPR` | **block** the turn until the watch is armed per `muggle-pr-followup`'s `arm-watcher.md` or a `MUGGLE_WATCH_SKIP` marker records a legitimate skip (releases after 3 blocks). A PR clears once the session sees it go terminal or a provider lookup reports it merged or closed — what lets a PR whose slot post-merge cleanup deleted through. The lookup runs in a detached scan queued at turn end; a PR it has not reached yet still blocks, and one it could not reach fails open |
54
54
  | `Stop` | `guardrail-watch-follow-through-gate.sh` | **enforce** | a `muggle-pr-followup` watch this session owns (the slot's `owner.json`) recorded due work in `watch-due.env` — a branch behind or conflicting with its base, or a head whose checks settled red — and `last_seen.json` records no attempt for it: no `conflict_resolve_attempts` entry for the rebase key (enforced under `autoRebase=always` while catch-up budget is left), no `ci_fix_attempts` entry for the red head. An escalation entry with no attempt behind it counts for nothing, and re-arming the watch changes nothing | `autoRebase` | **block** the turn, naming each PR and its `/muggle-do rebase` or `fix ci` dispatch. No release after N blocks: only a recorded attempt, a newer head in the slot's `prs.json`, or the user's literal `HOLD WATCH #<pr-number>` clears it. The hold must come from a turn the user typed — never a tool result, a loaded skill, a hook's message, a task notification, a compaction summary, or the agent's own text — and is kept for the session once read. Reads local files only, never a provider: the pre-filter is a builtins glob for a non-empty `watch-due.env` whose `owner.json` names the session, so Node starts only when a slot has due work |
55
55
  | `Stop` | `guardrail-walkthrough-gate.sh` | **enforce** | an E2E acceptance run happened this session and a PR in play — opened this session or on the working branch — carries no walkthrough in its body or comments, or only one under a "new E2E run in progress" banner; or an E2E skip was accepted and a PR it covers still shows the empty placeholder instead of the skip's record | `postPRVisualWalkthrough` | **block** the turn until the walkthrough is posted via `muggle-pr-visual-walkthrough` or a `MUGGLE_WALKTHROUGH_SKIP` marker records a legitimate skip. For a PR an accepted E2E skip covers, the block hands over the foreground `settle-walkthrough-skipped <pr> <session>` command, which prints `{"settled":true}` once the PR reads back the skip, and re-queues the detached write; a PR the skip does not cover is a different change and is told to run its own E2E or declare its own skip from its tree (releases after 3 blocks; an unreachable PR fails open to not-owed). A skip accepted before its PR existed is written straight into the PR's slot when it opens for the same repo and branch. The provider lookups run in a detached scan, queued when the gate arms, when a PR opens, and at each turn end; the gate decides on the last scan for the session's current PRs, begun after the last observed post, and stays open until one lands. Each PR is checked for its own walkthrough: a post to one PR settles only that one, and opening another re-arms the check. A PR whose own background run is in flight is set aside |
56
56
  | `Stop` | `guardrail-stage-gate.sh` | **enforce** | a skill invoked this session declared `mandatoryStages` and one of those files was never opened | — | **block** the turn until the stages are read or a `MUGGLE_STAGE_SKIP` marker records a legitimate skip (releases after 3 blocks) |
57
- | `Stop` | `guardrail-debug-path-gate.sh` | **enforce** | a local execution returned a non-passing run this session and nothing routed it through `_shared/debug-failed-run.md` | — | **block** the turn until the run is diagnosed (its `*-failure-classified` emit or feedback naming it) or a `MUGGLE_DEBUG_SKIP: <runId> <reason>` marker clears that run (releases after 3 blocks). Quiet while a background acceptance-tester is in flight — its failed runs land in this session's state and it debugs them itself |
57
+ | `Stop` | `guardrail-debug-path-gate.sh` | **enforce** | a local execution returned a non-passing run this session and nothing routed it through `_shared/debug-failed-run.md` | — | **block** the turn until the run is diagnosed (its `*-failure-classified` emit or feedback naming it) or a `MUGGLE_DEBUG_SKIP: <runId> <reason>` marker clears that run (releases after 3 blocks, leaving the PR's walkthrough comment unsettled). Quiet while a background acceptance-tester is in flight — its failed runs land in this session's state and it debugs them itself |
58
58
  | `Stop` | `guardrail-comment-reply-gate.sh` | **enforce** | this session claimed a review thread and left one of its comments uncovered | — | **block** the turn until each comment gets its threaded reply per `do/per-comment-replies.md` or a `MUGGLE_REPLY_SKIP: <comment-id> <reason>` marker defers it (releases after 3 blocks). The claim is the signal, not a push, so a round that answers a question with no code change is caught too; a thread another session claimed never blocks this one |
59
59
  | `Stop` | `guardrail-capability-claim.sh` | **enforce** | the closing turn tells the user a login- or code-gated flow can't be tested, reached, or verified | — | **block** once, citing `_shared/identity-and-inbox.md`: the managed profile's live inbox, provisioned number, stored TOTP secret, credentials, and CAPTCHA solver clear exactly that blocker. Reads the claim out of the transcript, since a Stop payload carries no message text. Per-sentence matching, and a sentence naming a voice call, an OAuth-only provider, or a deployment with no number provisioned never fires — those limits are real. Nudges once per session, then stays quiet, so a misread costs one turn |
60
60
  | `UserPromptSubmit` | `guardrail-build-router.sh` | advise | a build/implement/fix request (first one this session), or a direct `/muggle-do` / `/mdo` forward invocation | `autoRouteBuildToMuggleDo` | route the work through `muggle-do` (build delegated to superpowers); a direct invocation gets no advice. Either arms the build follow-through Stop gate |
@@ -280,7 +280,6 @@ var GuardrailBackgroundJob = /* @__PURE__ */ ((GuardrailBackgroundJob2) => {
280
280
  GuardrailBackgroundJob2["ReserveWalkthroughComment"] = "reserve-walkthrough-comment";
281
281
  GuardrailBackgroundJob2["SettleWalkthroughSkipped"] = "settle-walkthrough-skipped";
282
282
  GuardrailBackgroundJob2["SettleBranchWalkthroughSkipped"] = "settle-branch-walkthrough-skipped";
283
- GuardrailBackgroundJob2["SettleWalkthroughUnreasoned"] = "settle-walkthrough-unreasoned";
284
283
  GuardrailBackgroundJob2["SettleWalkthroughNoAppLogicChange"] = "settle-walkthrough-no-app-logic-change";
285
284
  GuardrailBackgroundJob2["NoteWalkthroughRerun"] = "note-walkthrough-rerun";
286
285
  GuardrailBackgroundJob2["ScanWatchPrStates"] = "scan-watch-pr-states";
@@ -629,6 +628,7 @@ function applyE2eSkipScope(state, verifiedChange) {
629
628
  var REPORT_SENTINEL = "muggle-pr-section";
630
629
  var WALKTHROUGH_SLOT_MARKER = "<!-- muggle-pr-walkthrough:v1 -->";
631
630
  var WALKTHROUGH_SKIPPED_MARKER = "<!-- muggle-pr-walkthrough-status:skipped -->";
631
+ var RETIRED_UNREASONED_SKIP_HEADLINE = "**No E2E run, and no verified reason given.**";
632
632
  var WALKTHROUGH_COMMENT_HEADING = "### Muggle AI \u2014 PR visual walkthrough";
633
633
  var REPORT_SENTINEL_TAG = new RegExp(`<!--\\s*${REPORT_SENTINEL}[^>]*-->\\s*`, "g");
634
634
  var LAST_FULL_RUN_OPENING = "<details><summary>Last full E2E run on this PR</summary>";
@@ -1024,7 +1024,15 @@ function renderReservedComment() {
1024
1024
  return `${WALKTHROUGH_SLOT_MARKER}
1025
1025
  ${WALKTHROUGH_COMMENT_HEADING}
1026
1026
 
1027
- _Awaiting the E2E acceptance run. Muggle edits this comment in place when the run finishes \u2014 or records why E2E does not apply to this change._`;
1027
+ **Waiting for the E2E feasibility check.** This comment updates in place \u2014 next with the E2E run in progress or why E2E is not feasible for this change, then with the E2E report.`;
1028
+ }
1029
+ function renderRunningComment(startedAt) {
1030
+ return `${WALKTHROUGH_SLOT_MARKER}
1031
+ ${WALKTHROUGH_COMMENT_HEADING}
1032
+
1033
+ ${RERUN_NOTICE_OPENING}
1034
+ **E2E running \u2014 waiting for the report.** Started ${formatUtcMinute(startedAt)}. This comment becomes the E2E report when the run finishes.
1035
+ ${RERUN_NOTICE_CLOSING}`;
1028
1036
  }
1029
1037
  function renderSkippedComment(code, detail) {
1030
1038
  const stated = detail.trim();
@@ -1034,24 +1042,17 @@ function renderSkippedComment(code, detail) {
1034
1042
  ${WALKTHROUGH_SKIPPED_MARKER}
1035
1043
  ${WALKTHROUGH_COMMENT_HEADING}
1036
1044
 
1037
- **No E2E run.** Muggle verified \`${code}\` \u2014 ${E2E_SKIP_CODE_CLAIMS[code]}${qualifier}.`;
1045
+ **E2E not feasible.** Muggle verified \`${code}\` \u2014 ${E2E_SKIP_CODE_CLAIMS[code]}${qualifier}.`;
1038
1046
  }
1039
1047
  function renderNotApplicableComment(reason) {
1040
1048
  return `${WALKTHROUGH_SLOT_MARKER}
1041
1049
  ${WALKTHROUGH_SKIPPED_MARKER}
1042
1050
  ${WALKTHROUGH_COMMENT_HEADING}
1043
1051
 
1044
- **No E2E run \u2014 not applicable.** ${reason.replace(/\.$/, "")}.
1052
+ **E2E not feasible.** ${reason.replace(/\.$/, "")}.
1045
1053
 
1046
1054
  _The coding agent judged that a browser run cannot exercise this change (\`${"NOT_APPLICABLE" /* NotApplicable */}\`). Muggle did not verify this reason._`;
1047
1055
  }
1048
- function renderUnreasonedSkipComment() {
1049
- return `${WALKTHROUGH_SLOT_MARKER}
1050
- ${WALKTHROUGH_SKIPPED_MARKER}
1051
- ${WALKTHROUGH_COMMENT_HEADING}
1052
-
1053
- **No E2E run, and no verified reason given.** The session ended still owing an acceptance run and cited no skip code that Muggle could verify. Treat this PR as unvalidated by E2E.`;
1054
- }
1055
1056
  var shortSha = (sha) => sha.slice(0, SHORT_SHA_LENGTH);
1056
1057
  function renderNoAppLogicChangeComment(declared, headSha, lastFullRun) {
1057
1058
  const cycleStart = declared.preCycleSha.toLowerCase();
@@ -1101,7 +1102,9 @@ ${RERUN_NOTICE_CLOSING}
1101
1102
  function classifyComment(body) {
1102
1103
  if (body.includes(REPORT_SENTINEL)) return "reported" /* Reported */;
1103
1104
  if (!body.includes(WALKTHROUGH_SLOT_MARKER)) return "not-designated" /* NotDesignated */;
1104
- if (body.includes(WALKTHROUGH_SKIPPED_MARKER)) return "skipped" /* Skipped */;
1105
+ if (body.includes(WALKTHROUGH_SKIPPED_MARKER) && !body.includes(RETIRED_UNREASONED_SKIP_HEADLINE)) {
1106
+ return "skipped" /* Skipped */;
1107
+ }
1105
1108
  return "pending" /* Pending */;
1106
1109
  }
1107
1110
  function walkthroughVerdict(bodies) {
@@ -2059,19 +2062,16 @@ function settleWalkthroughCommentForNoAppLogicChange(prUrl, detail, run = defaul
2059
2062
  const body = renderNoAppLogicChangeComment(declared, readPrHeadSha(pr, run), lastFullRun);
2060
2063
  return designated ? patchPrComment(pr, designated.id, body, run) : postPrComment(pr, body, run);
2061
2064
  }
2062
- function noteWalkthroughRerunStarted(prUrl, run = defaultGhRunner, startedAt = /* @__PURE__ */ new Date()) {
2065
+ function noteE2eRunStarted(prUrl, run = defaultGhRunner, startedAt = /* @__PURE__ */ new Date()) {
2063
2066
  const pr = parsePrUrl(prUrl);
2064
2067
  if (!pr) return false;
2065
2068
  const comments = listPrComments(pr, run);
2066
2069
  if (comments === null) return false;
2067
2070
  const designated = designatedComment(comments);
2068
2071
  if (!designated) return false;
2069
- if (classifyComment(designated.body) === "pending" /* Pending */) return false;
2070
2072
  if (hasRerunNotice(designated.body)) return false;
2071
- return patchPrComment(pr, designated.id, withRerunNotice(designated.body, startedAt), run);
2072
- }
2073
- function settleWalkthroughCommentAsUnreasoned(prUrl, run = defaultGhRunner) {
2074
- return settleWith(prUrl, renderUnreasonedSkipComment(), run);
2073
+ const body = classifyComment(designated.body) === "pending" /* Pending */ ? renderRunningComment(startedAt) : withRerunNotice(designated.body, startedAt);
2074
+ return patchPrComment(pr, designated.id, body, run);
2075
2075
  }
2076
2076
 
2077
2077
  // src/e2e-skip/parseSkipDeclaration.ts
@@ -2872,12 +2872,7 @@ function debugPathGate() {
2872
2872
  function e2eGate() {
2873
2873
  const state = readState(sessionId);
2874
2874
  const decision = e2eGateDecision(state);
2875
- if (decision.action === "release" /* Release */) {
2876
- for (const prUrl of state.prsHandled) {
2877
- startBackgroundJob("settle-walkthrough-unreasoned" /* SettleWalkthroughUnreasoned */, [prUrl]);
2878
- }
2879
- return releaseGate("e2eReleased");
2880
- }
2875
+ if (decision.action === "release" /* Release */) return releaseGate("e2eReleased");
2881
2876
  if (decision.action === "none" /* None */) return "{}";
2882
2877
  recordGateBlock("e2eBlockCount", decision.blockCount);
2883
2878
  const reason = decision.blockCount === 1 ? `Do not end the turn yet. This session went unit-green or opened a PR, but no E2E acceptance run has happened. Per the autoE2ETest preference (default: always), run change-driven E2E now via ${muggleSkill("/muggle:muggle-test")}, then finish. To skip, run \`echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"\` citing one of ${E2E_SKIP_CODES.join(", ")}. Muggle verifies each code itself, so one that does not hold leaves this gate blocked. Only when no verifiable code fits and a browser run genuinely cannot exercise the change, cite ${"NOT_APPLICABLE" /* NotApplicable */} with the reason \u2014 the PR shows it as your unverified judgment.` : `E2E acceptance run still owed (reminder ${decision.blockCount}/${MAX_E2E_BLOCKS}): run ${muggleSkill("/muggle:muggle-test")}, or cite a verifiable code via \`echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"\`.`;
@@ -3162,11 +3157,6 @@ function settleBranchWalkthroughSkippedJob() {
3162
3157
  if (prUrl) settleWalkthroughCommentAsSkipped(prUrl, code, detail ?? "");
3163
3158
  return "{}";
3164
3159
  }
3165
- function settleWalkthroughUnreasonedJob() {
3166
- const [prUrl] = jobArgs;
3167
- if (prUrl) settleWalkthroughCommentAsUnreasoned(prUrl);
3168
- return "{}";
3169
- }
3170
3160
  function settleWalkthroughNoAppLogicChangeJob() {
3171
3161
  const [detail] = jobArgs;
3172
3162
  const prUrl = defaultPrWalkthroughLookup.branchPrUrl();
@@ -3175,7 +3165,7 @@ function settleWalkthroughNoAppLogicChangeJob() {
3175
3165
  }
3176
3166
  function noteWalkthroughRerunJob() {
3177
3167
  const prUrl = defaultPrWalkthroughLookup.branchPrUrl();
3178
- if (prUrl) noteWalkthroughRerunStarted(prUrl);
3168
+ if (prUrl) noteE2eRunStarted(prUrl);
3179
3169
  return "{}";
3180
3170
  }
3181
3171
  function scanWatchPrStatesJob() {
@@ -3197,7 +3187,6 @@ var backgroundJobs = {
3197
3187
  ["reserve-walkthrough-comment" /* ReserveWalkthroughComment */]: reserveWalkthroughCommentJob,
3198
3188
  ["settle-walkthrough-skipped" /* SettleWalkthroughSkipped */]: settleWalkthroughSkippedJob,
3199
3189
  ["settle-branch-walkthrough-skipped" /* SettleBranchWalkthroughSkipped */]: settleBranchWalkthroughSkippedJob,
3200
- ["settle-walkthrough-unreasoned" /* SettleWalkthroughUnreasoned */]: settleWalkthroughUnreasonedJob,
3201
3190
  ["settle-walkthrough-no-app-logic-change" /* SettleWalkthroughNoAppLogicChange */]: settleWalkthroughNoAppLogicChangeJob,
3202
3191
  ["note-walkthrough-rerun" /* NoteWalkthroughRerun */]: noteWalkthroughRerunJob,
3203
3192
  ["scan-watch-pr-states" /* ScanWatchPrStates */]: scanWatchPrStatesJob,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "release": "5.26.0",
3
- "buildId": "run-254-1",
4
- "commitSha": "827f3558e259bf1905d25d297bdb875478a1a9e0",
5
- "buildTime": "2026-10-30T07:03:59Z",
3
+ "buildId": "run-255-1",
4
+ "commitSha": "f4a6f4a4e2005d404c165622085b743e0612d0c3",
5
+ "buildTime": "2026-10-30T19:07:28Z",
6
6
  "serviceName": "muggle-ai-works-mcp"
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@muggleai/works",
3
3
  "mcpName": "io.github.multiplex-ai/muggle",
4
- "version": "5.28.0",
4
+ "version": "5.29.0-staging.255",
5
5
  "description": "13 years of Microsoft hyperscale engineering, packaged as a harness for your AI coding agent.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -64,7 +64,7 @@
64
64
  "muggleConfig": {
65
65
  "electronAppVersion": "1.17.0",
66
66
  "downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
67
- "runtimeTargetDefault": "production",
67
+ "runtimeTargetDefault": "staging",
68
68
  "checksumsByStream": {
69
69
  "production": {
70
70
  "win32-x64": "03687e6c511f407875354954cb1b0f8e95d4ad0602174d76388a52d1aa1554e4",
@@ -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.28.0",
4
+ "version": "5.29.0-staging.255",
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.28.0",
7
+ "@muggleai/works@5.29.0-staging.255",
8
8
  "serve"
9
9
  ],
10
10
  "env": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muggle",
3
- "version": "5.28.0",
3
+ "version": "5.29.0-staging.255",
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.28.0",
5
+ "version": "5.29.0-staging.255",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
package/plugin/.mcp.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@muggleai/works@5.28.0",
7
+ "@muggleai/works@5.29.0-staging.255",
8
8
  "serve"
9
9
  ]
10
10
  }
@@ -47,14 +47,14 @@ Pre-filters are bash builtins only (`guardrail-lib.sh`), so an irrelevant event
47
47
  | `PostToolUse` (Bash) | `guardrail-record-comment-replies.sh` | record | an unresolved-thread fetch named review threads still awaiting an answer (classified by the `<!-- muggle-do:bot -->` marker, never the author login), a threaded reply the provider **confirmed** answered comments in one, or a `MUGGLE_REPLY_SKIP` marker deferred them | — | claim each thread and cover the comments answered, in the per-PR ledger beside the muggle-do slot |
48
48
  | `PreToolUse` (Bash, `if` a command names `gh`/`glab`) | `guardrail-report-format.sh` | **enforce** | a `gh pr comment\|create\|edit` body — or a `gh api … issues/comments/<id>` PATCH editing one — reads like an E2E report but lacks the `build-pr-section` sentinel | — | **deny** — render via `muggle build-pr-section` instead |
49
49
  | `PreToolUse` (muggle local execute/replay) | `guardrail-classify-gate.sh` | **enforce** | `muggle-test` is the skill in play and the target test case has no `pre-execution-classification` this session | — | **deny** — run Step 6f first (it calls `muggle-remote-test-script-list`, which is where the run learns the case has never passed) or record a legitimate skip via `MUGGLE_CLASSIFY_SKIP`. Scoped to `muggle-test`, so the single-target skills that legitimately skip classification are untouched; fails open when the test case can't be resolved |
50
- | `PreToolUse` (muggle local execute/replay) | `guardrail-walkthrough-rerun.sh` | record | a browser run is about to start in a working tree (the tool call's `cwd`) whose branch has a PR, and that PR's walkthrough comment already shows a run or a skip | — | a detached job puts a "new E2E run in progress" banner, with its UTC start time, on top of the walkthrough the run will replace. The comment underneath is untouched; the walkthrough check stays in progress until the run's own walkthrough replaces the whole body, banner included, and the walkthrough gate counts the PR as still owing that walkthrough. A reserved placeholder or a comment already bannered is left alone |
51
- | `Stop` | `guardrail-e2e-gate.sh` | **enforce** | unit tests passed this session, no E2E ran yet, and no skip was recorded | `autoE2ETest` | **block** the turn until E2E runs via `muggle-test` or a `MUGGLE_E2E_SKIP` marker records a legitimate skip (full message once, one-line reminders after; releases after 3 blocks). Quiet while a background acceptance-tester is in flight |
50
+ | `PreToolUse` (muggle local execute/replay) | `guardrail-walkthrough-rerun.sh` | record | a browser run is about to start in a working tree (the tool call's `cwd`) whose branch has a PR with a walkthrough comment | — | a detached job marks the comment with the run in progress and its UTC start time: a feasibility-check placeholder becomes "E2E running — waiting for the report"; a comment that already shows a run or a skip gets a "new E2E run in progress" banner on top, left untouched underneath. The walkthrough check stays in progress until the run's own walkthrough replaces the whole body, and the walkthrough gate counts the PR as still owing that walkthrough. A comment already marked is left alone |
51
+ | `Stop` | `guardrail-e2e-gate.sh` | **enforce** | unit tests passed this session, no E2E ran yet, and no skip was recorded | `autoE2ETest` | **block** the turn until E2E runs via `muggle-test` or a `MUGGLE_E2E_SKIP` marker records a legitimate skip (full message once, one-line reminders after; releases after 3 blocks, leaving the PR's walkthrough comment unsettled). Quiet while a background acceptance-tester is in flight |
52
52
  | `Stop` | `guardrail-terminal-gate.sh` | **enforce** | a PR went terminal this session and the next-options offer hasn't run since — an AskUserQuestion, or, while the session owns an open PR's watch slot, a closing message that asks the user | — | **block** the turn until the post-merge handoff runs (full message once, one-line reminders after; releases after 3 blocks; nothing but the offer resets the counter) |
53
53
  | `Stop` | `guardrail-watch-gate.sh` | **enforce** | a PR opened this session that is still open and has no live watch — no session slot for it is terminal, stopped by its owner, or carries a `watch-heartbeat` touched within 15 minutes; a slot that is only seeded does not count | `autoWatchPR` | **block** the turn until the watch is armed per `muggle-pr-followup`'s `arm-watcher.md` or a `MUGGLE_WATCH_SKIP` marker records a legitimate skip (releases after 3 blocks). A PR clears once the session sees it go terminal or a provider lookup reports it merged or closed — what lets a PR whose slot post-merge cleanup deleted through. The lookup runs in a detached scan queued at turn end; a PR it has not reached yet still blocks, and one it could not reach fails open |
54
54
  | `Stop` | `guardrail-watch-follow-through-gate.sh` | **enforce** | a `muggle-pr-followup` watch this session owns (the slot's `owner.json`) recorded due work in `watch-due.env` — a branch behind or conflicting with its base, or a head whose checks settled red — and `last_seen.json` records no attempt for it: no `conflict_resolve_attempts` entry for the rebase key (enforced under `autoRebase=always` while catch-up budget is left), no `ci_fix_attempts` entry for the red head. An escalation entry with no attempt behind it counts for nothing, and re-arming the watch changes nothing | `autoRebase` | **block** the turn, naming each PR and its `/muggle-do rebase` or `fix ci` dispatch. No release after N blocks: only a recorded attempt, a newer head in the slot's `prs.json`, or the user's literal `HOLD WATCH #<pr-number>` clears it. The hold must come from a turn the user typed — never a tool result, a loaded skill, a hook's message, a task notification, a compaction summary, or the agent's own text — and is kept for the session once read. Reads local files only, never a provider: the pre-filter is a builtins glob for a non-empty `watch-due.env` whose `owner.json` names the session, so Node starts only when a slot has due work |
55
55
  | `Stop` | `guardrail-walkthrough-gate.sh` | **enforce** | an E2E acceptance run happened this session and a PR in play — opened this session or on the working branch — carries no walkthrough in its body or comments, or only one under a "new E2E run in progress" banner; or an E2E skip was accepted and a PR it covers still shows the empty placeholder instead of the skip's record | `postPRVisualWalkthrough` | **block** the turn until the walkthrough is posted via `muggle-pr-visual-walkthrough` or a `MUGGLE_WALKTHROUGH_SKIP` marker records a legitimate skip. For a PR an accepted E2E skip covers, the block hands over the foreground `settle-walkthrough-skipped <pr> <session>` command, which prints `{"settled":true}` once the PR reads back the skip, and re-queues the detached write; a PR the skip does not cover is a different change and is told to run its own E2E or declare its own skip from its tree (releases after 3 blocks; an unreachable PR fails open to not-owed). A skip accepted before its PR existed is written straight into the PR's slot when it opens for the same repo and branch. The provider lookups run in a detached scan, queued when the gate arms, when a PR opens, and at each turn end; the gate decides on the last scan for the session's current PRs, begun after the last observed post, and stays open until one lands. Each PR is checked for its own walkthrough: a post to one PR settles only that one, and opening another re-arms the check. A PR whose own background run is in flight is set aside |
56
56
  | `Stop` | `guardrail-stage-gate.sh` | **enforce** | a skill invoked this session declared `mandatoryStages` and one of those files was never opened | — | **block** the turn until the stages are read or a `MUGGLE_STAGE_SKIP` marker records a legitimate skip (releases after 3 blocks) |
57
- | `Stop` | `guardrail-debug-path-gate.sh` | **enforce** | a local execution returned a non-passing run this session and nothing routed it through `_shared/debug-failed-run.md` | — | **block** the turn until the run is diagnosed (its `*-failure-classified` emit or feedback naming it) or a `MUGGLE_DEBUG_SKIP: <runId> <reason>` marker clears that run (releases after 3 blocks). Quiet while a background acceptance-tester is in flight — its failed runs land in this session's state and it debugs them itself |
57
+ | `Stop` | `guardrail-debug-path-gate.sh` | **enforce** | a local execution returned a non-passing run this session and nothing routed it through `_shared/debug-failed-run.md` | — | **block** the turn until the run is diagnosed (its `*-failure-classified` emit or feedback naming it) or a `MUGGLE_DEBUG_SKIP: <runId> <reason>` marker clears that run (releases after 3 blocks, leaving the PR's walkthrough comment unsettled). Quiet while a background acceptance-tester is in flight — its failed runs land in this session's state and it debugs them itself |
58
58
  | `Stop` | `guardrail-comment-reply-gate.sh` | **enforce** | this session claimed a review thread and left one of its comments uncovered | — | **block** the turn until each comment gets its threaded reply per `do/per-comment-replies.md` or a `MUGGLE_REPLY_SKIP: <comment-id> <reason>` marker defers it (releases after 3 blocks). The claim is the signal, not a push, so a round that answers a question with no code change is caught too; a thread another session claimed never blocks this one |
59
59
  | `Stop` | `guardrail-capability-claim.sh` | **enforce** | the closing turn tells the user a login- or code-gated flow can't be tested, reached, or verified | — | **block** once, citing `_shared/identity-and-inbox.md`: the managed profile's live inbox, provisioned number, stored TOTP secret, credentials, and CAPTCHA solver clear exactly that blocker. Reads the claim out of the transcript, since a Stop payload carries no message text. Per-sentence matching, and a sentence naming a voice call, an OAuth-only provider, or a deployment with no number provisioned never fires — those limits are real. Nudges once per session, then stays quiet, so a misread costs one turn |
60
60
  | `UserPromptSubmit` | `guardrail-build-router.sh` | advise | a build/implement/fix request (first one this session), or a direct `/muggle-do` / `/mdo` forward invocation | `autoRouteBuildToMuggleDo` | route the work through `muggle-do` (build delegated to superpowers); a direct invocation gets no advice. Either arms the build follow-through Stop gate |
@@ -280,7 +280,6 @@ var GuardrailBackgroundJob = /* @__PURE__ */ ((GuardrailBackgroundJob2) => {
280
280
  GuardrailBackgroundJob2["ReserveWalkthroughComment"] = "reserve-walkthrough-comment";
281
281
  GuardrailBackgroundJob2["SettleWalkthroughSkipped"] = "settle-walkthrough-skipped";
282
282
  GuardrailBackgroundJob2["SettleBranchWalkthroughSkipped"] = "settle-branch-walkthrough-skipped";
283
- GuardrailBackgroundJob2["SettleWalkthroughUnreasoned"] = "settle-walkthrough-unreasoned";
284
283
  GuardrailBackgroundJob2["SettleWalkthroughNoAppLogicChange"] = "settle-walkthrough-no-app-logic-change";
285
284
  GuardrailBackgroundJob2["NoteWalkthroughRerun"] = "note-walkthrough-rerun";
286
285
  GuardrailBackgroundJob2["ScanWatchPrStates"] = "scan-watch-pr-states";
@@ -629,6 +628,7 @@ function applyE2eSkipScope(state, verifiedChange) {
629
628
  var REPORT_SENTINEL = "muggle-pr-section";
630
629
  var WALKTHROUGH_SLOT_MARKER = "<!-- muggle-pr-walkthrough:v1 -->";
631
630
  var WALKTHROUGH_SKIPPED_MARKER = "<!-- muggle-pr-walkthrough-status:skipped -->";
631
+ var RETIRED_UNREASONED_SKIP_HEADLINE = "**No E2E run, and no verified reason given.**";
632
632
  var WALKTHROUGH_COMMENT_HEADING = "### Muggle AI \u2014 PR visual walkthrough";
633
633
  var REPORT_SENTINEL_TAG = new RegExp(`<!--\\s*${REPORT_SENTINEL}[^>]*-->\\s*`, "g");
634
634
  var LAST_FULL_RUN_OPENING = "<details><summary>Last full E2E run on this PR</summary>";
@@ -1024,7 +1024,15 @@ function renderReservedComment() {
1024
1024
  return `${WALKTHROUGH_SLOT_MARKER}
1025
1025
  ${WALKTHROUGH_COMMENT_HEADING}
1026
1026
 
1027
- _Awaiting the E2E acceptance run. Muggle edits this comment in place when the run finishes \u2014 or records why E2E does not apply to this change._`;
1027
+ **Waiting for the E2E feasibility check.** This comment updates in place \u2014 next with the E2E run in progress or why E2E is not feasible for this change, then with the E2E report.`;
1028
+ }
1029
+ function renderRunningComment(startedAt) {
1030
+ return `${WALKTHROUGH_SLOT_MARKER}
1031
+ ${WALKTHROUGH_COMMENT_HEADING}
1032
+
1033
+ ${RERUN_NOTICE_OPENING}
1034
+ **E2E running \u2014 waiting for the report.** Started ${formatUtcMinute(startedAt)}. This comment becomes the E2E report when the run finishes.
1035
+ ${RERUN_NOTICE_CLOSING}`;
1028
1036
  }
1029
1037
  function renderSkippedComment(code, detail) {
1030
1038
  const stated = detail.trim();
@@ -1034,24 +1042,17 @@ function renderSkippedComment(code, detail) {
1034
1042
  ${WALKTHROUGH_SKIPPED_MARKER}
1035
1043
  ${WALKTHROUGH_COMMENT_HEADING}
1036
1044
 
1037
- **No E2E run.** Muggle verified \`${code}\` \u2014 ${E2E_SKIP_CODE_CLAIMS[code]}${qualifier}.`;
1045
+ **E2E not feasible.** Muggle verified \`${code}\` \u2014 ${E2E_SKIP_CODE_CLAIMS[code]}${qualifier}.`;
1038
1046
  }
1039
1047
  function renderNotApplicableComment(reason) {
1040
1048
  return `${WALKTHROUGH_SLOT_MARKER}
1041
1049
  ${WALKTHROUGH_SKIPPED_MARKER}
1042
1050
  ${WALKTHROUGH_COMMENT_HEADING}
1043
1051
 
1044
- **No E2E run \u2014 not applicable.** ${reason.replace(/\.$/, "")}.
1052
+ **E2E not feasible.** ${reason.replace(/\.$/, "")}.
1045
1053
 
1046
1054
  _The coding agent judged that a browser run cannot exercise this change (\`${"NOT_APPLICABLE" /* NotApplicable */}\`). Muggle did not verify this reason._`;
1047
1055
  }
1048
- function renderUnreasonedSkipComment() {
1049
- return `${WALKTHROUGH_SLOT_MARKER}
1050
- ${WALKTHROUGH_SKIPPED_MARKER}
1051
- ${WALKTHROUGH_COMMENT_HEADING}
1052
-
1053
- **No E2E run, and no verified reason given.** The session ended still owing an acceptance run and cited no skip code that Muggle could verify. Treat this PR as unvalidated by E2E.`;
1054
- }
1055
1056
  var shortSha = (sha) => sha.slice(0, SHORT_SHA_LENGTH);
1056
1057
  function renderNoAppLogicChangeComment(declared, headSha, lastFullRun) {
1057
1058
  const cycleStart = declared.preCycleSha.toLowerCase();
@@ -1101,7 +1102,9 @@ ${RERUN_NOTICE_CLOSING}
1101
1102
  function classifyComment(body) {
1102
1103
  if (body.includes(REPORT_SENTINEL)) return "reported" /* Reported */;
1103
1104
  if (!body.includes(WALKTHROUGH_SLOT_MARKER)) return "not-designated" /* NotDesignated */;
1104
- if (body.includes(WALKTHROUGH_SKIPPED_MARKER)) return "skipped" /* Skipped */;
1105
+ if (body.includes(WALKTHROUGH_SKIPPED_MARKER) && !body.includes(RETIRED_UNREASONED_SKIP_HEADLINE)) {
1106
+ return "skipped" /* Skipped */;
1107
+ }
1105
1108
  return "pending" /* Pending */;
1106
1109
  }
1107
1110
  function walkthroughVerdict(bodies) {
@@ -2059,19 +2062,16 @@ function settleWalkthroughCommentForNoAppLogicChange(prUrl, detail, run = defaul
2059
2062
  const body = renderNoAppLogicChangeComment(declared, readPrHeadSha(pr, run), lastFullRun);
2060
2063
  return designated ? patchPrComment(pr, designated.id, body, run) : postPrComment(pr, body, run);
2061
2064
  }
2062
- function noteWalkthroughRerunStarted(prUrl, run = defaultGhRunner, startedAt = /* @__PURE__ */ new Date()) {
2065
+ function noteE2eRunStarted(prUrl, run = defaultGhRunner, startedAt = /* @__PURE__ */ new Date()) {
2063
2066
  const pr = parsePrUrl(prUrl);
2064
2067
  if (!pr) return false;
2065
2068
  const comments = listPrComments(pr, run);
2066
2069
  if (comments === null) return false;
2067
2070
  const designated = designatedComment(comments);
2068
2071
  if (!designated) return false;
2069
- if (classifyComment(designated.body) === "pending" /* Pending */) return false;
2070
2072
  if (hasRerunNotice(designated.body)) return false;
2071
- return patchPrComment(pr, designated.id, withRerunNotice(designated.body, startedAt), run);
2072
- }
2073
- function settleWalkthroughCommentAsUnreasoned(prUrl, run = defaultGhRunner) {
2074
- return settleWith(prUrl, renderUnreasonedSkipComment(), run);
2073
+ const body = classifyComment(designated.body) === "pending" /* Pending */ ? renderRunningComment(startedAt) : withRerunNotice(designated.body, startedAt);
2074
+ return patchPrComment(pr, designated.id, body, run);
2075
2075
  }
2076
2076
 
2077
2077
  // src/e2e-skip/parseSkipDeclaration.ts
@@ -2872,12 +2872,7 @@ function debugPathGate() {
2872
2872
  function e2eGate() {
2873
2873
  const state = readState(sessionId);
2874
2874
  const decision = e2eGateDecision(state);
2875
- if (decision.action === "release" /* Release */) {
2876
- for (const prUrl of state.prsHandled) {
2877
- startBackgroundJob("settle-walkthrough-unreasoned" /* SettleWalkthroughUnreasoned */, [prUrl]);
2878
- }
2879
- return releaseGate("e2eReleased");
2880
- }
2875
+ if (decision.action === "release" /* Release */) return releaseGate("e2eReleased");
2881
2876
  if (decision.action === "none" /* None */) return "{}";
2882
2877
  recordGateBlock("e2eBlockCount", decision.blockCount);
2883
2878
  const reason = decision.blockCount === 1 ? `Do not end the turn yet. This session went unit-green or opened a PR, but no E2E acceptance run has happened. Per the autoE2ETest preference (default: always), run change-driven E2E now via ${muggleSkill("/muggle:muggle-test")}, then finish. To skip, run \`echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"\` citing one of ${E2E_SKIP_CODES.join(", ")}. Muggle verifies each code itself, so one that does not hold leaves this gate blocked. Only when no verifiable code fits and a browser run genuinely cannot exercise the change, cite ${"NOT_APPLICABLE" /* NotApplicable */} with the reason \u2014 the PR shows it as your unverified judgment.` : `E2E acceptance run still owed (reminder ${decision.blockCount}/${MAX_E2E_BLOCKS}): run ${muggleSkill("/muggle:muggle-test")}, or cite a verifiable code via \`echo "MUGGLE_E2E_SKIP: <CODE>: <detail>"\`.`;
@@ -3162,11 +3157,6 @@ function settleBranchWalkthroughSkippedJob() {
3162
3157
  if (prUrl) settleWalkthroughCommentAsSkipped(prUrl, code, detail ?? "");
3163
3158
  return "{}";
3164
3159
  }
3165
- function settleWalkthroughUnreasonedJob() {
3166
- const [prUrl] = jobArgs;
3167
- if (prUrl) settleWalkthroughCommentAsUnreasoned(prUrl);
3168
- return "{}";
3169
- }
3170
3160
  function settleWalkthroughNoAppLogicChangeJob() {
3171
3161
  const [detail] = jobArgs;
3172
3162
  const prUrl = defaultPrWalkthroughLookup.branchPrUrl();
@@ -3175,7 +3165,7 @@ function settleWalkthroughNoAppLogicChangeJob() {
3175
3165
  }
3176
3166
  function noteWalkthroughRerunJob() {
3177
3167
  const prUrl = defaultPrWalkthroughLookup.branchPrUrl();
3178
- if (prUrl) noteWalkthroughRerunStarted(prUrl);
3168
+ if (prUrl) noteE2eRunStarted(prUrl);
3179
3169
  return "{}";
3180
3170
  }
3181
3171
  function scanWatchPrStatesJob() {
@@ -3197,7 +3187,6 @@ var backgroundJobs = {
3197
3187
  ["reserve-walkthrough-comment" /* ReserveWalkthroughComment */]: reserveWalkthroughCommentJob,
3198
3188
  ["settle-walkthrough-skipped" /* SettleWalkthroughSkipped */]: settleWalkthroughSkippedJob,
3199
3189
  ["settle-branch-walkthrough-skipped" /* SettleBranchWalkthroughSkipped */]: settleBranchWalkthroughSkippedJob,
3200
- ["settle-walkthrough-unreasoned" /* SettleWalkthroughUnreasoned */]: settleWalkthroughUnreasonedJob,
3201
3190
  ["settle-walkthrough-no-app-logic-change" /* SettleWalkthroughNoAppLogicChange */]: settleWalkthroughNoAppLogicChangeJob,
3202
3191
  ["note-walkthrough-rerun" /* NoteWalkthroughRerun */]: noteWalkthroughRerunJob,
3203
3192
  ["scan-watch-pr-states" /* ScanWatchPrStates */]: scanWatchPrStatesJob,