@muggleai/works 5.30.0 → 5.31.0-staging.268
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-mcp.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/.mcp.json +1 -1
- package/dist/plugin/hooks/README.md +1 -1
- package/dist/plugin/scripts/guardrails.mjs +18 -10
- package/dist/release-manifest.json +3 -3
- package/package.json +11 -11
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-mcp.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/hooks/README.md +1 -1
- package/plugin/scripts/guardrails.mjs +18 -10
|
@@ -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.
|
|
12
|
+
"version": "5.31.0-staging.268"
|
|
13
13
|
}
|
|
14
14
|
]
|
|
15
15
|
}
|
|
@@ -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.
|
|
4
|
+
"version": "5.31.0-staging.268",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.31.0-staging.268",
|
|
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.
|
|
5
|
+
"version": "5.31.0-staging.268",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/dist/plugin/.mcp.json
CHANGED
|
@@ -37,7 +37,7 @@ Pre-filters are bash builtins only (`guardrail-lib.sh`), so an irrelevant event
|
|
|
37
37
|
| Hook event | Wrapper | Strength | Condition | Preference | Effect |
|
|
38
38
|
| :--------- | :------ | :------- | :-------- | :--------- | :----- |
|
|
39
39
|
| `PostToolUse` (Bash) | `guardrail-pr-opened.sh` | advise | a `gh pr create`/`gh pr ready` just succeeded | `autoWatchPR` | start a `muggle-pr-followup` watcher on the new PR |
|
|
40
|
-
| `PostToolUse` (Bash + muggle execute/replay MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable, or a walkthrough carrying the `build-pr-section` sentinel was posted / declared un-postable via `echo "MUGGLE_WALKTHROUGH_SKIP: <reason>"` | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` / `walkthroughPostedPrUrls` / `walkthroughSkipped` session state. A post settles only the PR its output names (`gh pr comment`/`create` print it; a `gh api` comment edit returns `html_url`); a post whose output names none is timestamped (`walkthroughPostedAt`), and a scan begun after it decides. An accepted skip records the change it was verified for — the working tree's origin repo and checked-out branch (`e2eSkipVerifiedChange`) — and settles only that change: the PRs opened for it (`prChanges`, tracked in `e2eSkipCoveredPrs`) and its checked-out branch's PR. A PR's change is its URL's repo plus the command's `--head`/`--source-branch`, or else the hook tree's branch when the command never changed directory and that tree is in the PR's repo; a PR whose change is unknown is never covered, so `Push-Location`/`cd`/`--repo` cannot hand it another change's skip |
|
|
40
|
+
| `PostToolUse` (Bash + muggle execute/replay MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable, or a walkthrough carrying the `build-pr-section` sentinel was posted / declared un-postable via `echo "MUGGLE_WALKTHROUGH_SKIP: <reason>"` | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` / `walkthroughPostedPrUrls` / `walkthroughSkipped` session state. A post settles only the PR its output names (`gh pr comment`/`create` print it; a `gh api` comment edit returns `html_url`); a post whose output names none is timestamped (`walkthroughPostedAt`), and a scan begun after it decides. An accepted skip records the change it was verified for — the working tree's origin repo and checked-out branch (`e2eSkipVerifiedChange`) — and settles only that change: the PRs opened for it (`prChanges`, tracked in `e2eSkipCoveredPrs`) and its checked-out branch's PR. One whose tree's change cannot be resolved records none, never the previous declaration's, and a PR an earlier skip covered keeps that skip's record. A PR's change is its URL's repo plus the command's `--head`/`--source-branch`, or else the hook tree's branch when the command never changed directory and that tree is in the PR's repo; a PR whose change is unknown is never covered, so `Push-Location`/`cd`/`--repo` cannot hand it another change's skip |
|
|
41
41
|
| `PostToolUse` (Bash) | `guardrail-pr-terminal.sh` | advise | a PR just went terminal — a `gh pr merge`/`gh pr close` success line or the `TERMINAL pr=N` line the watch scripts print in a shell result (never bare `"state":"MERGED"` metadata). Not registered on `Monitor`: a Monitor call returns before its script prints anything, so its result never carries that line | — | record `terminalPending`, direct the post-merge handoff: finalize the watcher slot, tear down per `autoCleanup`, offer next options via AskUserQuestion — or as text in the closing message while the session still owns an open PR's watch slot, since a pending AskUserQuestion holds the turn and that watcher with it |
|
|
42
42
|
| `PostToolUse` (AskUserQuestion) | `guardrail-offer-ran.sh` | record | a next-options offer ran while a terminal PR was pending | — | clear `terminalPending` — the only exit for the post-merge Stop gate |
|
|
43
43
|
| `PostToolUse` (Agent, Task) | `guardrail-record-delegation.sh` | record | a `muggle:acceptance-tester` was launched into the background — its response carries no finished report yet | — | append to `e2eDelegations` with the PR URLs its brief names; for a bounded window the E2E and debug-path gates hold off, and the walkthrough gate sets those PRs aside, so the session is never steered into a second browser run alongside the agent's. Shares the AskUserQuestion group's dispatch |
|
|
@@ -520,8 +520,11 @@ function withoutDelegatedPrs(prUrls, state, now) {
|
|
|
520
520
|
|
|
521
521
|
// src/guardrails/shouldRunE2E.ts
|
|
522
522
|
var MAX_E2E_BLOCKS = 3;
|
|
523
|
+
function isEnvironmentSkip(skip) {
|
|
524
|
+
return skip !== null && !CYCLE_SCOPED_SKIP_CODES.includes(skip.code);
|
|
525
|
+
}
|
|
523
526
|
function applyE2eSkipDeclaration(state, skip) {
|
|
524
|
-
if (!
|
|
527
|
+
if (!isEnvironmentSkip(skip)) return state;
|
|
525
528
|
return { ...state, e2eSkipDeclaration: { code: skip.code, detail: skip.detail } };
|
|
526
529
|
}
|
|
527
530
|
function shouldRunE2E(state) {
|
|
@@ -682,6 +685,9 @@ function isSameChange(left, right) {
|
|
|
682
685
|
function isPrOfSkipChange(state, prUrl) {
|
|
683
686
|
return isSameChange(state.prChanges?.[prUrl], state.e2eSkipVerifiedChange);
|
|
684
687
|
}
|
|
688
|
+
function prsOfSkipChange(state) {
|
|
689
|
+
return state.prsHandled.filter((prUrl) => isPrOfSkipChange(state, prUrl));
|
|
690
|
+
}
|
|
685
691
|
function withCoveredPrs(state, prUrls) {
|
|
686
692
|
const covered = new Set(state.e2eSkipCoveredPrs ?? []);
|
|
687
693
|
const uncovered = prUrls.filter((prUrl) => !covered.has(prUrl));
|
|
@@ -697,12 +703,14 @@ function applyPrOpenedToSkipScope(state, prUrl, prChange) {
|
|
|
697
703
|
return isPrOfSkipChange(recorded, prUrl) && recorded.e2eSkipDeclaration ? withCoveredPrs(recorded, [prUrl]) : recorded;
|
|
698
704
|
}
|
|
699
705
|
function applyE2eSkipScope(state, verifiedChange) {
|
|
700
|
-
if (!
|
|
706
|
+
if (!state.e2eSkipDeclaration) return state;
|
|
707
|
+
if (!verifiedChange) {
|
|
708
|
+
if (!state.e2eSkipVerifiedChange) return state;
|
|
709
|
+
const { e2eSkipVerifiedChange: _previousDeclarationChange, ...unscoped } = state;
|
|
710
|
+
return unscoped;
|
|
711
|
+
}
|
|
701
712
|
const scoped = { ...state, e2eSkipVerifiedChange: verifiedChange };
|
|
702
|
-
return withCoveredPrs(
|
|
703
|
-
scoped,
|
|
704
|
-
scoped.prsHandled.filter((prUrl) => isPrOfSkipChange(scoped, prUrl))
|
|
705
|
-
);
|
|
713
|
+
return withCoveredPrs(scoped, prsOfSkipChange(scoped));
|
|
706
714
|
}
|
|
707
715
|
|
|
708
716
|
// src/pr-walkthrough/types.ts
|
|
@@ -1855,7 +1863,7 @@ function classificationGateDecision(state, input2) {
|
|
|
1855
1863
|
// src/guardrails/stateCommit.ts
|
|
1856
1864
|
function applyToolOutcome(state, outcome) {
|
|
1857
1865
|
const declared = applyE2eSkipDeclaration(applyRecordedRun(state, outcome.run), outcome.acceptedSkip);
|
|
1858
|
-
const recorded = applyE2eSkipScope(declared, outcome.skipVerifiedChange);
|
|
1866
|
+
const recorded = isEnvironmentSkip(outcome.acceptedSkip) ? applyE2eSkipScope(declared, outcome.skipVerifiedChange) : declared;
|
|
1859
1867
|
const withWatchSkip = applyWatchSkip(recorded, outcome.isWatchSkip);
|
|
1860
1868
|
const withBuildSkip = applyBuildSkip(withWatchSkip, outcome.isBuildSkip);
|
|
1861
1869
|
const withWalkthroughPost = applyWalkthroughPosted(
|
|
@@ -3130,7 +3138,7 @@ function recordTests() {
|
|
|
3130
3138
|
const judgedSkip = judgeE2eSkip(input, stateBefore);
|
|
3131
3139
|
const acceptedSkip = judgedSkip?.accepted ? judgedSkip.skip : null;
|
|
3132
3140
|
const walkthroughPosted = detectWalkthroughPost(input);
|
|
3133
|
-
const isDeclaredHere =
|
|
3141
|
+
const isDeclaredHere = isEnvironmentSkip(acceptedSkip);
|
|
3134
3142
|
const outcome = {
|
|
3135
3143
|
kind: "tool-outcome" /* ToolOutcome */,
|
|
3136
3144
|
run: {
|
|
@@ -3163,7 +3171,7 @@ function recordSkipReasonOnPrs(state, skip) {
|
|
|
3163
3171
|
startBackgroundJob("settle-walkthrough-no-app-logic-change" /* SettleWalkthroughNoAppLogicChange */, [skip.detail], input.cwd);
|
|
3164
3172
|
return;
|
|
3165
3173
|
}
|
|
3166
|
-
for (const prUrl of
|
|
3174
|
+
for (const prUrl of prsOfSkipChange(state)) {
|
|
3167
3175
|
startBackgroundJob("settle-walkthrough-skipped" /* SettleWalkthroughSkipped */, [prUrl, skip.code, skip.detail]);
|
|
3168
3176
|
}
|
|
3169
3177
|
startBackgroundJob("settle-branch-walkthrough-skipped" /* SettleBranchWalkthroughSkipped */, [skip.code, skip.detail], input.cwd);
|
|
@@ -3379,7 +3387,7 @@ function walkthroughGate() {
|
|
|
3379
3387
|
if (decision.action !== "block" /* Block */) return "{}";
|
|
3380
3388
|
recordGateBlock("walkthroughBlockCount", decision.blockCount);
|
|
3381
3389
|
const declaredSkip = state.e2eSkipDeclaration;
|
|
3382
|
-
const skipOwedPrUrls = declaredSkip ? decision.owed.filter((prUrl) =>
|
|
3390
|
+
const skipOwedPrUrls = declaredSkip ? decision.owed.filter((prUrl) => isPrOfSkipChange(state, prUrl)) : [];
|
|
3383
3391
|
if (declaredSkip && skipOwedPrUrls.length > 0) {
|
|
3384
3392
|
return blockOnUnsettledSkip(declaredSkip, skipOwedPrUrls, decision.blockCount);
|
|
3385
3393
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"release": "5.26.0",
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-10-
|
|
3
|
+
"buildId": "run-268-1",
|
|
4
|
+
"commitSha": "034abfdee292104743ae296d7c24aa85de74066e",
|
|
5
|
+
"buildTime": "2026-10-31T20:23:14Z",
|
|
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.
|
|
4
|
+
"version": "5.31.0-staging.268",
|
|
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",
|
|
@@ -62,21 +62,21 @@
|
|
|
62
62
|
"typecheck:browser-bench": "tsc --noEmit -p internal/browser-bench/tsconfig.json"
|
|
63
63
|
},
|
|
64
64
|
"muggleConfig": {
|
|
65
|
-
"electronAppVersion": "1.
|
|
65
|
+
"electronAppVersion": "1.22.3",
|
|
66
66
|
"downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
|
|
67
|
-
"runtimeTargetDefault": "
|
|
67
|
+
"runtimeTargetDefault": "staging",
|
|
68
68
|
"checksumsByStream": {
|
|
69
69
|
"production": {
|
|
70
|
-
"win32-x64": "
|
|
71
|
-
"linux-x64": "
|
|
72
|
-
"darwin-arm64": "
|
|
73
|
-
"darwin-x64": "
|
|
70
|
+
"win32-x64": "be2b541f81e40e74f084548dbb193d556e5c1a66107fe5b345c2b4408dd7febe",
|
|
71
|
+
"linux-x64": "e0810a4e62bd94f6935f5a216ede71725b64eaba576bb7a8cda6731c5fff8bf6",
|
|
72
|
+
"darwin-arm64": "12015e835605fb98a58e00ca78be0b4b854e0042de143e3546a29fb2eca49e40",
|
|
73
|
+
"darwin-x64": "14987dbe6e47ddab4aa09688def0f49cc65bce78398ab890059e9ff96ea8c417"
|
|
74
74
|
},
|
|
75
75
|
"staging": {
|
|
76
|
-
"darwin-arm64": "
|
|
77
|
-
"linux-x64": "
|
|
78
|
-
"win32-x64": "
|
|
79
|
-
"darwin-x64": "
|
|
76
|
+
"darwin-arm64": "e5fc6928ff29239e9eba3f34dea8080f1dba9953fc444e4af5387193b55d2416",
|
|
77
|
+
"linux-x64": "064a9dbcfb833e3d1e67613a681d9d390503bc38f877699ce434f48c390f5ac5",
|
|
78
|
+
"win32-x64": "8c83ba98faab1e89416088b42ff4bfb22f57a4de43598b881ef2cb6da7af58a8",
|
|
79
|
+
"darwin-x64": "56ac2d80c98057b8360c526734001ef0fd1c60f60e492f11e97aae04eee955c8"
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"electronAppSignedFromVersion": "1.10.0",
|
|
@@ -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.
|
|
4
|
+
"version": "5.31.0-staging.268",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
package/plugin/.codex-mcp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.31.0-staging.268",
|
|
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.
|
|
5
|
+
"version": "5.31.0-staging.268",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/plugin/.mcp.json
CHANGED
package/plugin/hooks/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Pre-filters are bash builtins only (`guardrail-lib.sh`), so an irrelevant event
|
|
|
37
37
|
| Hook event | Wrapper | Strength | Condition | Preference | Effect |
|
|
38
38
|
| :--------- | :------ | :------- | :-------- | :--------- | :----- |
|
|
39
39
|
| `PostToolUse` (Bash) | `guardrail-pr-opened.sh` | advise | a `gh pr create`/`gh pr ready` just succeeded | `autoWatchPR` | start a `muggle-pr-followup` watcher on the new PR |
|
|
40
|
-
| `PostToolUse` (Bash + muggle execute/replay MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable, or a walkthrough carrying the `build-pr-section` sentinel was posted / declared un-postable via `echo "MUGGLE_WALKTHROUGH_SKIP: <reason>"` | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` / `walkthroughPostedPrUrls` / `walkthroughSkipped` session state. A post settles only the PR its output names (`gh pr comment`/`create` print it; a `gh api` comment edit returns `html_url`); a post whose output names none is timestamped (`walkthroughPostedAt`), and a scan begun after it decides. An accepted skip records the change it was verified for — the working tree's origin repo and checked-out branch (`e2eSkipVerifiedChange`) — and settles only that change: the PRs opened for it (`prChanges`, tracked in `e2eSkipCoveredPrs`) and its checked-out branch's PR. A PR's change is its URL's repo plus the command's `--head`/`--source-branch`, or else the hook tree's branch when the command never changed directory and that tree is in the PR's repo; a PR whose change is unknown is never covered, so `Push-Location`/`cd`/`--repo` cannot hand it another change's skip |
|
|
40
|
+
| `PostToolUse` (Bash + muggle execute/replay MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable, or a walkthrough carrying the `build-pr-section` sentinel was posted / declared un-postable via `echo "MUGGLE_WALKTHROUGH_SKIP: <reason>"` | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` / `walkthroughPostedPrUrls` / `walkthroughSkipped` session state. A post settles only the PR its output names (`gh pr comment`/`create` print it; a `gh api` comment edit returns `html_url`); a post whose output names none is timestamped (`walkthroughPostedAt`), and a scan begun after it decides. An accepted skip records the change it was verified for — the working tree's origin repo and checked-out branch (`e2eSkipVerifiedChange`) — and settles only that change: the PRs opened for it (`prChanges`, tracked in `e2eSkipCoveredPrs`) and its checked-out branch's PR. One whose tree's change cannot be resolved records none, never the previous declaration's, and a PR an earlier skip covered keeps that skip's record. A PR's change is its URL's repo plus the command's `--head`/`--source-branch`, or else the hook tree's branch when the command never changed directory and that tree is in the PR's repo; a PR whose change is unknown is never covered, so `Push-Location`/`cd`/`--repo` cannot hand it another change's skip |
|
|
41
41
|
| `PostToolUse` (Bash) | `guardrail-pr-terminal.sh` | advise | a PR just went terminal — a `gh pr merge`/`gh pr close` success line or the `TERMINAL pr=N` line the watch scripts print in a shell result (never bare `"state":"MERGED"` metadata). Not registered on `Monitor`: a Monitor call returns before its script prints anything, so its result never carries that line | — | record `terminalPending`, direct the post-merge handoff: finalize the watcher slot, tear down per `autoCleanup`, offer next options via AskUserQuestion — or as text in the closing message while the session still owns an open PR's watch slot, since a pending AskUserQuestion holds the turn and that watcher with it |
|
|
42
42
|
| `PostToolUse` (AskUserQuestion) | `guardrail-offer-ran.sh` | record | a next-options offer ran while a terminal PR was pending | — | clear `terminalPending` — the only exit for the post-merge Stop gate |
|
|
43
43
|
| `PostToolUse` (Agent, Task) | `guardrail-record-delegation.sh` | record | a `muggle:acceptance-tester` was launched into the background — its response carries no finished report yet | — | append to `e2eDelegations` with the PR URLs its brief names; for a bounded window the E2E and debug-path gates hold off, and the walkthrough gate sets those PRs aside, so the session is never steered into a second browser run alongside the agent's. Shares the AskUserQuestion group's dispatch |
|
|
@@ -520,8 +520,11 @@ function withoutDelegatedPrs(prUrls, state, now) {
|
|
|
520
520
|
|
|
521
521
|
// src/guardrails/shouldRunE2E.ts
|
|
522
522
|
var MAX_E2E_BLOCKS = 3;
|
|
523
|
+
function isEnvironmentSkip(skip) {
|
|
524
|
+
return skip !== null && !CYCLE_SCOPED_SKIP_CODES.includes(skip.code);
|
|
525
|
+
}
|
|
523
526
|
function applyE2eSkipDeclaration(state, skip) {
|
|
524
|
-
if (!
|
|
527
|
+
if (!isEnvironmentSkip(skip)) return state;
|
|
525
528
|
return { ...state, e2eSkipDeclaration: { code: skip.code, detail: skip.detail } };
|
|
526
529
|
}
|
|
527
530
|
function shouldRunE2E(state) {
|
|
@@ -682,6 +685,9 @@ function isSameChange(left, right) {
|
|
|
682
685
|
function isPrOfSkipChange(state, prUrl) {
|
|
683
686
|
return isSameChange(state.prChanges?.[prUrl], state.e2eSkipVerifiedChange);
|
|
684
687
|
}
|
|
688
|
+
function prsOfSkipChange(state) {
|
|
689
|
+
return state.prsHandled.filter((prUrl) => isPrOfSkipChange(state, prUrl));
|
|
690
|
+
}
|
|
685
691
|
function withCoveredPrs(state, prUrls) {
|
|
686
692
|
const covered = new Set(state.e2eSkipCoveredPrs ?? []);
|
|
687
693
|
const uncovered = prUrls.filter((prUrl) => !covered.has(prUrl));
|
|
@@ -697,12 +703,14 @@ function applyPrOpenedToSkipScope(state, prUrl, prChange) {
|
|
|
697
703
|
return isPrOfSkipChange(recorded, prUrl) && recorded.e2eSkipDeclaration ? withCoveredPrs(recorded, [prUrl]) : recorded;
|
|
698
704
|
}
|
|
699
705
|
function applyE2eSkipScope(state, verifiedChange) {
|
|
700
|
-
if (!
|
|
706
|
+
if (!state.e2eSkipDeclaration) return state;
|
|
707
|
+
if (!verifiedChange) {
|
|
708
|
+
if (!state.e2eSkipVerifiedChange) return state;
|
|
709
|
+
const { e2eSkipVerifiedChange: _previousDeclarationChange, ...unscoped } = state;
|
|
710
|
+
return unscoped;
|
|
711
|
+
}
|
|
701
712
|
const scoped = { ...state, e2eSkipVerifiedChange: verifiedChange };
|
|
702
|
-
return withCoveredPrs(
|
|
703
|
-
scoped,
|
|
704
|
-
scoped.prsHandled.filter((prUrl) => isPrOfSkipChange(scoped, prUrl))
|
|
705
|
-
);
|
|
713
|
+
return withCoveredPrs(scoped, prsOfSkipChange(scoped));
|
|
706
714
|
}
|
|
707
715
|
|
|
708
716
|
// src/pr-walkthrough/types.ts
|
|
@@ -1855,7 +1863,7 @@ function classificationGateDecision(state, input2) {
|
|
|
1855
1863
|
// src/guardrails/stateCommit.ts
|
|
1856
1864
|
function applyToolOutcome(state, outcome) {
|
|
1857
1865
|
const declared = applyE2eSkipDeclaration(applyRecordedRun(state, outcome.run), outcome.acceptedSkip);
|
|
1858
|
-
const recorded = applyE2eSkipScope(declared, outcome.skipVerifiedChange);
|
|
1866
|
+
const recorded = isEnvironmentSkip(outcome.acceptedSkip) ? applyE2eSkipScope(declared, outcome.skipVerifiedChange) : declared;
|
|
1859
1867
|
const withWatchSkip = applyWatchSkip(recorded, outcome.isWatchSkip);
|
|
1860
1868
|
const withBuildSkip = applyBuildSkip(withWatchSkip, outcome.isBuildSkip);
|
|
1861
1869
|
const withWalkthroughPost = applyWalkthroughPosted(
|
|
@@ -3130,7 +3138,7 @@ function recordTests() {
|
|
|
3130
3138
|
const judgedSkip = judgeE2eSkip(input, stateBefore);
|
|
3131
3139
|
const acceptedSkip = judgedSkip?.accepted ? judgedSkip.skip : null;
|
|
3132
3140
|
const walkthroughPosted = detectWalkthroughPost(input);
|
|
3133
|
-
const isDeclaredHere =
|
|
3141
|
+
const isDeclaredHere = isEnvironmentSkip(acceptedSkip);
|
|
3134
3142
|
const outcome = {
|
|
3135
3143
|
kind: "tool-outcome" /* ToolOutcome */,
|
|
3136
3144
|
run: {
|
|
@@ -3163,7 +3171,7 @@ function recordSkipReasonOnPrs(state, skip) {
|
|
|
3163
3171
|
startBackgroundJob("settle-walkthrough-no-app-logic-change" /* SettleWalkthroughNoAppLogicChange */, [skip.detail], input.cwd);
|
|
3164
3172
|
return;
|
|
3165
3173
|
}
|
|
3166
|
-
for (const prUrl of
|
|
3174
|
+
for (const prUrl of prsOfSkipChange(state)) {
|
|
3167
3175
|
startBackgroundJob("settle-walkthrough-skipped" /* SettleWalkthroughSkipped */, [prUrl, skip.code, skip.detail]);
|
|
3168
3176
|
}
|
|
3169
3177
|
startBackgroundJob("settle-branch-walkthrough-skipped" /* SettleBranchWalkthroughSkipped */, [skip.code, skip.detail], input.cwd);
|
|
@@ -3379,7 +3387,7 @@ function walkthroughGate() {
|
|
|
3379
3387
|
if (decision.action !== "block" /* Block */) return "{}";
|
|
3380
3388
|
recordGateBlock("walkthroughBlockCount", decision.blockCount);
|
|
3381
3389
|
const declaredSkip = state.e2eSkipDeclaration;
|
|
3382
|
-
const skipOwedPrUrls = declaredSkip ? decision.owed.filter((prUrl) =>
|
|
3390
|
+
const skipOwedPrUrls = declaredSkip ? decision.owed.filter((prUrl) => isPrOfSkipChange(state, prUrl)) : [];
|
|
3383
3391
|
if (declaredSkip && skipOwedPrUrls.length > 0) {
|
|
3384
3392
|
return blockOnUnsettledSkip(declaredSkip, skipOwedPrUrls, decision.blockCount);
|
|
3385
3393
|
}
|