@muggleai/works 5.12.0-staging.85 → 5.12.0-staging.86
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.
|
@@ -30,6 +30,15 @@ MUGGLE_PR_WATCH_POLL_INTERVAL="${MUGGLE_PR_WATCH_POLL_INTERVAL:-60}"
|
|
|
30
30
|
# revoked auth) exhausts it.
|
|
31
31
|
MUGGLE_PR_WATCH_MAX_FETCH_FAILURES="${MUGGLE_PR_WATCH_MAX_FETCH_FAILURES:-60}"
|
|
32
32
|
|
|
33
|
+
# Seconds a loop will wait for the arming session to write watch-watermark.env
|
|
34
|
+
# before giving up. The loop cannot evaluate a single wake condition without it,
|
|
35
|
+
# so an unseeded slot is a watcher that will never report anything — and one that
|
|
36
|
+
# looks perfectly healthy while it does nothing, because it still holds its PID
|
|
37
|
+
# lease and still touches its heartbeat. Arming writes the file in the same turn
|
|
38
|
+
# it starts the loop, so anything past a couple of minutes means the arming
|
|
39
|
+
# sequence was not followed and the watch is inert.
|
|
40
|
+
MUGGLE_PR_WATCH_MAX_UNSEEDED="${MUGGLE_PR_WATCH_MAX_UNSEEDED:-180}"
|
|
41
|
+
|
|
33
42
|
# Seconds to sleep after `fails` consecutive failed fetches: the poll interval,
|
|
34
43
|
# then a linear back-off capped at 5 minutes so a sustained outage is retried
|
|
35
44
|
# calmly rather than hammered every 60s.
|
|
@@ -59,6 +68,15 @@ watcher_lifetime_exceeded() {
|
|
|
59
68
|
[ $((now - started)) -ge "$max" ]
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
# True when the slot has gone unseeded longer than the cap allows. 0 is
|
|
72
|
+
# unbounded, matching `watcher_lifetime_exceeded`, for a caller that seeds the
|
|
73
|
+
# watermark out of band.
|
|
74
|
+
watcher_unseeded_too_long() {
|
|
75
|
+
local waited="$1" max="${2:-$MUGGLE_PR_WATCH_MAX_UNSEEDED}"
|
|
76
|
+
[ "$max" -eq 0 ] 2>/dev/null && return 1
|
|
77
|
+
[ "$waited" -ge "$max" ]
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
# True when pid names a running process. `kill -0` sends no signal; EPERM means
|
|
63
81
|
# the process exists but is foreign, which still counts as alive. Used by
|
|
64
82
|
# arm-watcher's pre-arm dedup to decide whether a watcher already owns the slot.
|
|
@@ -54,6 +54,7 @@ state_projection="$(cat "${script_dir}/pr-watch-state.jq")"
|
|
|
54
54
|
echo "$$" > "${slot}/watch.pid"
|
|
55
55
|
started=$(date +%s)
|
|
56
56
|
fails=0
|
|
57
|
+
unseeded=0
|
|
57
58
|
|
|
58
59
|
# In-memory floors, above the on-disk watermark. The watermark is advanced by
|
|
59
60
|
# the session after it handles a wave; these stop the loop re-reporting an event
|
|
@@ -137,11 +138,24 @@ while :; do
|
|
|
137
138
|
touch "${slot}/watch-heartbeat" 2>/dev/null
|
|
138
139
|
|
|
139
140
|
# No watermark yet means the arming session has not finished seeding. Wait
|
|
140
|
-
# rather than treat every floor as zero, which would fire the whole backlog
|
|
141
|
+
# rather than treat every floor as zero, which would fire the whole backlog —
|
|
142
|
+
# but not forever. Every wake condition below is evaluated against a floor
|
|
143
|
+
# read from this file, so a loop that never gets one polls nothing, reports
|
|
144
|
+
# nothing and never reaches the terminal check, while still holding its PID
|
|
145
|
+
# lease and touching its heartbeat. That combination is the worst kind of
|
|
146
|
+
# broken: reconcile reads the live lease and fresh beacon as healthy and
|
|
147
|
+
# declines to re-arm, so the PR is silently unwatched for as long as the
|
|
148
|
+
# session lives. Fail loudly instead.
|
|
141
149
|
if [ ! -f "${slot}/watch-watermark.env" ]; then
|
|
150
|
+
unseeded=$((unseeded + MUGGLE_PR_WATCH_POLL_INTERVAL))
|
|
151
|
+
if watcher_unseeded_too_long "$unseeded"; then
|
|
152
|
+
echo "WATCH-FAIL pr=$pr_number no watch-watermark.env after ${unseeded}s — arming never seeded it, so this watch can detect nothing (arm-watcher.md steps 1-2)"
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
142
155
|
sleep "$MUGGLE_PR_WATCH_POLL_INTERVAL"
|
|
143
156
|
continue
|
|
144
157
|
fi
|
|
158
|
+
unseeded=0
|
|
145
159
|
|
|
146
160
|
watermark_review=$(read_watermark_value REV)
|
|
147
161
|
watermark_comment=$(read_watermark_value COM)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"release": "5.11.0",
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-08-
|
|
3
|
+
"buildId": "run-86-1",
|
|
4
|
+
"commitSha": "795c9069663dd1ea34e9f2e58ac31ba832ec4137",
|
|
5
|
+
"buildTime": "2026-08-30T05:45:55Z",
|
|
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.12.0-staging.
|
|
4
|
+
"version": "5.12.0-staging.86",
|
|
5
5
|
"description": "Ship quality products with AI-powered E2E acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,15 @@ MUGGLE_PR_WATCH_POLL_INTERVAL="${MUGGLE_PR_WATCH_POLL_INTERVAL:-60}"
|
|
|
30
30
|
# revoked auth) exhausts it.
|
|
31
31
|
MUGGLE_PR_WATCH_MAX_FETCH_FAILURES="${MUGGLE_PR_WATCH_MAX_FETCH_FAILURES:-60}"
|
|
32
32
|
|
|
33
|
+
# Seconds a loop will wait for the arming session to write watch-watermark.env
|
|
34
|
+
# before giving up. The loop cannot evaluate a single wake condition without it,
|
|
35
|
+
# so an unseeded slot is a watcher that will never report anything — and one that
|
|
36
|
+
# looks perfectly healthy while it does nothing, because it still holds its PID
|
|
37
|
+
# lease and still touches its heartbeat. Arming writes the file in the same turn
|
|
38
|
+
# it starts the loop, so anything past a couple of minutes means the arming
|
|
39
|
+
# sequence was not followed and the watch is inert.
|
|
40
|
+
MUGGLE_PR_WATCH_MAX_UNSEEDED="${MUGGLE_PR_WATCH_MAX_UNSEEDED:-180}"
|
|
41
|
+
|
|
33
42
|
# Seconds to sleep after `fails` consecutive failed fetches: the poll interval,
|
|
34
43
|
# then a linear back-off capped at 5 minutes so a sustained outage is retried
|
|
35
44
|
# calmly rather than hammered every 60s.
|
|
@@ -59,6 +68,15 @@ watcher_lifetime_exceeded() {
|
|
|
59
68
|
[ $((now - started)) -ge "$max" ]
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
# True when the slot has gone unseeded longer than the cap allows. 0 is
|
|
72
|
+
# unbounded, matching `watcher_lifetime_exceeded`, for a caller that seeds the
|
|
73
|
+
# watermark out of band.
|
|
74
|
+
watcher_unseeded_too_long() {
|
|
75
|
+
local waited="$1" max="${2:-$MUGGLE_PR_WATCH_MAX_UNSEEDED}"
|
|
76
|
+
[ "$max" -eq 0 ] 2>/dev/null && return 1
|
|
77
|
+
[ "$waited" -ge "$max" ]
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
# True when pid names a running process. `kill -0` sends no signal; EPERM means
|
|
63
81
|
# the process exists but is foreign, which still counts as alive. Used by
|
|
64
82
|
# arm-watcher's pre-arm dedup to decide whether a watcher already owns the slot.
|
|
@@ -54,6 +54,7 @@ state_projection="$(cat "${script_dir}/pr-watch-state.jq")"
|
|
|
54
54
|
echo "$$" > "${slot}/watch.pid"
|
|
55
55
|
started=$(date +%s)
|
|
56
56
|
fails=0
|
|
57
|
+
unseeded=0
|
|
57
58
|
|
|
58
59
|
# In-memory floors, above the on-disk watermark. The watermark is advanced by
|
|
59
60
|
# the session after it handles a wave; these stop the loop re-reporting an event
|
|
@@ -137,11 +138,24 @@ while :; do
|
|
|
137
138
|
touch "${slot}/watch-heartbeat" 2>/dev/null
|
|
138
139
|
|
|
139
140
|
# No watermark yet means the arming session has not finished seeding. Wait
|
|
140
|
-
# rather than treat every floor as zero, which would fire the whole backlog
|
|
141
|
+
# rather than treat every floor as zero, which would fire the whole backlog —
|
|
142
|
+
# but not forever. Every wake condition below is evaluated against a floor
|
|
143
|
+
# read from this file, so a loop that never gets one polls nothing, reports
|
|
144
|
+
# nothing and never reaches the terminal check, while still holding its PID
|
|
145
|
+
# lease and touching its heartbeat. That combination is the worst kind of
|
|
146
|
+
# broken: reconcile reads the live lease and fresh beacon as healthy and
|
|
147
|
+
# declines to re-arm, so the PR is silently unwatched for as long as the
|
|
148
|
+
# session lives. Fail loudly instead.
|
|
141
149
|
if [ ! -f "${slot}/watch-watermark.env" ]; then
|
|
150
|
+
unseeded=$((unseeded + MUGGLE_PR_WATCH_POLL_INTERVAL))
|
|
151
|
+
if watcher_unseeded_too_long "$unseeded"; then
|
|
152
|
+
echo "WATCH-FAIL pr=$pr_number no watch-watermark.env after ${unseeded}s — arming never seeded it, so this watch can detect nothing (arm-watcher.md steps 1-2)"
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
142
155
|
sleep "$MUGGLE_PR_WATCH_POLL_INTERVAL"
|
|
143
156
|
continue
|
|
144
157
|
fi
|
|
158
|
+
unseeded=0
|
|
145
159
|
|
|
146
160
|
watermark_review=$(read_watermark_value REV)
|
|
147
161
|
watermark_comment=$(read_watermark_value COM)
|