@muggleai/works 5.8.1 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/{chunk-5PYK7DWB.js → chunk-7NC2DJTK.js} +10 -4
- package/dist/{chunk-LVUJ4E62.js → chunk-OCLMXAVJ.js} +245 -167
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/README.md +19 -13
- package/dist/plugin/agents/test-prepare-runner.md +1 -0
- package/dist/plugin/hooks/hooks.json +6 -0
- package/dist/plugin/scripts/ensure-electron-app.sh +62 -47
- package/dist/plugin/scripts/guardrail-watch-gate.sh +40 -0
- package/dist/plugin/scripts/guardrails.mjs +88 -5
- package/dist/plugin/scripts/pr-watch-guards.sh +17 -0
- package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/dist/plugin/skills/muggle/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +5 -3
- package/dist/plugin/skills/muggle-pr-followup/blocked-tick.md +2 -2
- package/dist/plugin/skills/muggle-pr-followup/cancel-cron.md +5 -3
- package/dist/plugin/skills/muggle-pr-followup/contract.md +8 -2
- package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +30 -3
- package/dist/plugin/skills/muggle-pr-followup/finalize.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +3 -1
- package/dist/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/dist/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/dist/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/dist/plugin/skills/muggle-test/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-test/execute-local.md +2 -0
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +3 -1
- package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +10 -4
- package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +100 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/readiness-report.md +9 -4
- package/dist/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +5 -4
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/release-manifest.json +4 -4
- package/dist/src-AWUORQZZ.js +1 -0
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/README.md +19 -13
- package/plugin/agents/test-prepare-runner.md +1 -0
- package/plugin/hooks/hooks.json +6 -0
- package/plugin/scripts/ensure-electron-app.sh +62 -47
- package/plugin/scripts/guardrail-watch-gate.sh +40 -0
- package/plugin/scripts/guardrails.mjs +88 -5
- package/plugin/scripts/pr-watch-guards.sh +17 -0
- package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/plugin/skills/muggle/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/arm-watcher.md +5 -3
- package/plugin/skills/muggle-pr-followup/blocked-tick.md +2 -2
- package/plugin/skills/muggle-pr-followup/cancel-cron.md +5 -3
- package/plugin/skills/muggle-pr-followup/contract.md +8 -2
- package/plugin/skills/muggle-pr-followup/evals/evals.json +30 -3
- package/plugin/skills/muggle-pr-followup/finalize.md +1 -1
- package/plugin/skills/muggle-pr-followup/state-schemas.md +3 -1
- package/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/plugin/skills/muggle-test/SKILL.md +1 -1
- package/plugin/skills/muggle-test/execute-local.md +2 -0
- package/plugin/skills/muggle-test-feature-local/SKILL.md +3 -1
- package/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/plugin/skills/muggle-test-prepare/SKILL.md +10 -4
- package/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +100 -0
- package/plugin/skills/muggle-test-prepare/steps/readiness-report.md +9 -4
- package/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +5 -4
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/src-FDK2KQSZ.js +0 -1
|
@@ -33,9 +33,15 @@ Every `increment`/`reset` this procedure applies to `last_seen.json`, and the `p
|
|
|
33
33
|
|
|
34
34
|
**Stopped-slot absorb — before anything else, including the disk reads above.** If the kill file `~/.muggle-ai/muggle-do/polling.disabled` exists, or the slot dir is absent but `~/.muggle-ai/muggle-do/sessions/<slug>.stopped/` exists (the owner ran [`stop.md`](stop.md)), absorb: no fetch, no state write, no log line — output one line (`stopped: <slug> — absorbed`) and exit. This gate is what makes an **unreachable orphaned cron** harmless: a cron whose handle a compaction severed can neither be enumerated by `CronList` nor deleted by its recorded id, so it fires until its session dies — the absorb caps each fire at one line of output and nothing else. Never "fix" an orphan by deleting ids `CronList` does surface; those belong to other live watchers.
|
|
35
35
|
|
|
36
|
-
If `prs.json[0].state` on disk is already `merged` or `closed`, this slot was finalized by a prior tick and this is a stale (queued) fire — per-minute cron fires enqueued while the session was busy still drain after the cron is cancelled. Defensively cancel any lingering cron for this slug per [`cancel-cron.md`](cancel-cron.md) (
|
|
36
|
+
If `prs.json[0].state` on disk is already `merged` or `closed`, this slot was finalized by a prior tick and this is a stale (queued) fire — per-minute cron fires enqueued while the session was busy still drain after the cron is cancelled. Defensively cancel any lingering cron for this slug per [`cancel-cron.md`](cancel-cron.md), which reports **found** (a reachable cron was cancelled) or **not-found** (neither lookup matched a live cron). Append a `stale-tick` line to `followup.log` that records the result — `stale-tick cancel=<found|not-found>` — and exit. Do not re-fetch or re-finalize.
|
|
37
37
|
|
|
38
|
-
**Orphan escalation —
|
|
38
|
+
**Orphan escalation — when no cancel ever reaches the cron, yet stale fires persist.** The signal is *reachability*, not a fire count. Once any cancel of this slug's cron has reported **found** — finalize's teardown cancel ([`finalize.md`](finalize.md) Step 4) or an earlier stale fire's defensive cancel — the cron was deletable in its runtime, so the fires that follow are the finite queued-drain backlog emptying: never escalate. The orphan is the opposite: the cron lives in a runtime neither the recorded id nor `CronList` can reach (a session continue / compaction severed every handle), so **every** cancel reports **not-found** and the fires keep arriving until the 7-day expiry. Escalate only when all three hold:
|
|
39
|
+
|
|
40
|
+
- this fire's defensive cancel reported **not-found**, **and**
|
|
41
|
+
- a prior `stale-tick` line for this slot already exists in `followup.log` — this is not the first stale fire, so it is not a lone drain, a manually-run tick, or a just-expired cron, **and**
|
|
42
|
+
- no `cancel=found` has ever been recorded for this slot (neither finalize's teardown line nor any earlier `stale-tick` line) — the cron was never reachable, so these fires are not a drained backlog.
|
|
43
|
+
|
|
44
|
+
Escalate to the owner **once**: if `followup.log` has no `stale-orphan-escalated` line, print one line — `orphaned cron: <slug> keeps firing after finalize; no in-session cancel can reach it — restarting the Claude session is the only clean clear` — and append a `stale-orphan-escalated` line so every later stale fire absorbs silently again. Never respond to an unreachable cron by guess-deleting ids `CronList` does surface: those belong to other live watchers, and deleting one silently kills a wanted loop.
|
|
39
45
|
|
|
40
46
|
Otherwise, self-record this watcher's cron id per [`record-cron-id.md`](record-cron-id.md) before proceeding. Recording every tick — while `CronList` can still see the cron — is what keeps the id a valid `CronDelete` target after a session continue / compaction blinds `CronList` to it, so teardown ([`finalize.md`](finalize.md), [`reconcile.md`](reconcile.md)) can always kill the orphan.
|
|
41
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"skill_name": "muggle-pr-followup",
|
|
3
|
-
"notes": "These evals test the TICK DECISION behavior plus the recovery/stop substrate. A real tick needs live GitHub state and the Cron tools, so each prompt hands the model a concrete slot + provider state and asks for the step-by-step plan it would execute; assertions check the plan takes the branch the contract mandates. Cases 0–10 cover per-tick dispatch/idle decisions; cases 11–17 cover cron-delivered ticks handing back to the token-free monitor, the owner stop switch, and the stopped-slot / kill-file absorb that makes an unreachable orphaned cron inert. Run via skill-creator's eval runner — CI does not execute these (ci.yml gates on vitest/check-skill-deps; skill-eval.yml runs gate scenarios sourced from muggle-ai-brain and the routing eval).",
|
|
3
|
+
"notes": "These evals test the TICK DECISION behavior plus the recovery/stop substrate. A real tick needs live GitHub state and the Cron tools, so each prompt hands the model a concrete slot + provider state and asks for the step-by-step plan it would execute; assertions check the plan takes the branch the contract mandates. Cases 0–10 cover per-tick dispatch/idle decisions; cases 11–17 cover cron-delivered ticks handing back to the token-free monitor, the owner stop switch, and the stopped-slot / kill-file absorb that makes an unreachable orphaned cron inert; cases 8 and 18–19 cover the monitor's own wake signals (settled-red CI, branch-behind/conflicting, and blocked-resume on any CI-digest move). Run via skill-creator's eval runner — CI does not execute these (ci.yml gates on vitest/check-skill-deps; skill-eval.yml runs gate scenarios sourced from muggle-ai-brain and the routing eval).",
|
|
4
4
|
"evals": [
|
|
5
5
|
{
|
|
6
6
|
"id": 0,
|
|
@@ -88,10 +88,12 @@
|
|
|
88
88
|
{
|
|
89
89
|
"id": 7,
|
|
90
90
|
"eval_name": "repeated-stale-fires-escalate-orphan-once",
|
|
91
|
-
"prompt": "Tick muggle-ai-works-pr507 507. prs.json records state merged and result.md exists. followup.log
|
|
91
|
+
"prompt": "Tick muggle-ai-works-pr507 507. prs.json records state merged and result.md exists. The watcher's session was continued, so neither the recorded cron id nor CronList can reach this slug's cron. followup.log shows finalize recorded cancel=not-found and one earlier stale-tick line also recorded cancel=not-found — there is no cancel=found line anywhere for this slot — and no stale-orphan-escalated line. This fire's defensive cancel per cancel-cron.md again reports not-found. Walk me through the plan for this tick.",
|
|
92
92
|
"files": [],
|
|
93
93
|
"assertions": [
|
|
94
|
-
{ "name": "detects_orphaned_cron", "text": "Plan concludes the cron is orphaned —
|
|
94
|
+
{ "name": "detects_orphaned_cron", "text": "Plan concludes the cron is orphaned because no cancel has ever reached it — finalize's teardown cancel and every stale fire's defensive cancel all report not-found — and the stale fires persist (a prior stale-tick line already exists). It reaches this from the reachability signal, not from counting stale fires to a threshold." },
|
|
95
|
+
{ "name": "requires_persistence_not_a_lone_fire", "text": "Plan escalates because this is not the first stale fire (a prior stale-tick line exists); it would treat a single not-found fire as a possible manually-run tick or just-expired cron, not an orphan." },
|
|
96
|
+
{ "name": "would_not_escalate_a_drained_backlog", "text": "Plan would NOT escalate had any cancel reported found — finalize's teardown cancel or an earlier stale fire's defensive cancel — because a reachable cron's later fires are a finite queued-drain backlog, not an orphan." },
|
|
95
97
|
{ "name": "escalates_to_owner_once", "text": "Plan surfaces a one-line owner notice that only a session restart clears the orphan, and appends a stale-orphan-escalated marker line so the notice never repeats." },
|
|
96
98
|
{ "name": "later_fires_absorb_silently", "text": "Plan treats any stale fire after the marker exists as a silent absorb — log stale-tick and exit, no repeated notice." },
|
|
97
99
|
{ "name": "never_guess_deletes_other_ids", "text": "Plan does NOT delete cron ids that CronList surfaces for other slugs — those belong to other live watchers." }
|
|
@@ -216,6 +218,31 @@
|
|
|
216
218
|
{ "name": "then_seeds_and_arms", "text": "Plan then seeds the slot and arms the watch normally — leaving the kill file in place would make every tick of the new watch absorb itself stillborn." },
|
|
217
219
|
{ "name": "only_bootstrap_clears_it", "text": "Plan notes only bootstrap (an explicit URL) clears the kill file; auto-track and reconcile never do." }
|
|
218
220
|
]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"id": 18,
|
|
224
|
+
"eval_name": "arm-monitor-wakes-on-branch-behind",
|
|
225
|
+
"prompt": "Arm the watch for muggle-ai-works-pr525 525 after a clean drain (no reviews, no unresolved threads, CI green, branch level with master at seed time). Describe the watch.sh monitor loop you would write and specifically what makes it wake the session when the base branch later advances and the branch falls behind.",
|
|
226
|
+
"files": [],
|
|
227
|
+
"assertions": [
|
|
228
|
+
{ "name": "polls_branch_standing", "text": "The loop polls the branch's standing against its base each iteration (behind_by via the compare call and the conflict signal per branch-standing.md), not just reviews, threads, and CI." },
|
|
229
|
+
{ "name": "wakes_on_behind_or_conflict", "text": "The loop wakes the session when behind_by > 0 or the branch conflicts with its base, so a silently-stale branch triggers a tick without waiting for an unrelated wake." },
|
|
230
|
+
{ "name": "dedups_on_rebase_key_pair", "text": "The loop dedups the rebase wake on the rebase_key pair (head_sha..base_tip_sha), firing once per newly-due pair and re-arming when the head or base moves — not on a head-only key, which would wedge permanently once the base advances." },
|
|
231
|
+
{ "name": "not_a_wake_when_unknown_and_level", "text": "The loop does NOT wake when behind_by == 0 and mergeable is UNKNOWN (conflict state still computing) — it waits, matching the tick's Step 5." },
|
|
232
|
+
{ "name": "stays_a_dumb_pipe", "text": "The loop only signals a rebase is due and hands off to a tick; it does NOT apply the conflict_resolve_attempts budget, resolve conflicts, or dispatch the rebase itself — those belong to the tick and /muggle-do." }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": 19,
|
|
237
|
+
"eval_name": "arm-monitor-wakes-on-cidigest-change-while-blocked",
|
|
238
|
+
"prompt": "Arm the watch for muggle-ai-works-pr526 526 while it is blocked pending a human (last_seen.blocked present, reason ci_escalated). Describe the watch.sh monitor loop and specifically what makes it resume the block when the head's CI later moves to green, or a rerun/external deploy check posts — i.e. a non-red CI change.",
|
|
239
|
+
"files": [],
|
|
240
|
+
"assertions": [
|
|
241
|
+
{ "name": "blocked_probe_is_conditional", "text": "The loop runs the blocked-resume CI probe only while the watch is blocked — it keys off BLOCKED_CIDIGEST being non-empty, and the probe stays dormant when the watch is not blocked." },
|
|
242
|
+
{ "name": "wakes_on_any_digest_move_not_just_red", "text": "While blocked, the loop wakes on ANY change to the head's CI digest away from BLOCKED_CIDIGEST — a green pass, a rerun, or an external deploy check — not only on a settled-red transition." },
|
|
243
|
+
{ "name": "digest_matches_fingerprint_definition", "text": "The digest the loop computes is the same bucket-plus-sorted-name/conclusion signature the blocked fingerprint's ci_digest uses, so the loop and the tick's Step 2.5 resume gate agree by construction." },
|
|
244
|
+
{ "name": "resume_is_the_tick_job", "text": "The loop only wakes on the digest move; clearing last_seen.blocked and re-evaluating belongs to the tick's Step 2.5 fingerprint gate, and the blocked-tick flag/resume path is what maintains BLOCKED_CIDIGEST — the loop never writes it." }
|
|
245
|
+
]
|
|
219
246
|
}
|
|
220
247
|
]
|
|
221
248
|
}
|
|
@@ -26,4 +26,4 @@ Append the terminal line per [`output-templates/watcher-log.md`](output-template
|
|
|
26
26
|
|
|
27
27
|
### Step 4 — Unschedule the cron
|
|
28
28
|
|
|
29
|
-
Cancel this slot's cron per [`cancel-cron.md`](cancel-cron.md).
|
|
29
|
+
Cancel this slot's cron per [`cancel-cron.md`](cancel-cron.md), which reports **found** or **not-found**. Record the result in `followup.log` — `finalize cancel=<found|not-found>` — so a later stale fire's orphan gate ([`contract.md`](contract.md) Step 0) can tell a reachable cron whose queued fires merely drain (**found** here) from one it could never reach (**not-found** here, the orphan case). A **not-found** is otherwise benign — a manually-run tick, or a cron that already expired. Recurring `/loop` crons auto-expire after 7 days; that lapse is the gap [`reconcile.md`](reconcile.md) exists to catch.
|
|
@@ -176,8 +176,10 @@ The watch loop's comparison floor — plain `KEY=VALUE` lines, one file per slot
|
|
|
176
176
|
- `COM` — highest thread-comment id already handled
|
|
177
177
|
- `THREADS` — semicolon-joined ids of threads already known unresolved
|
|
178
178
|
- `CIRED` — head SHA whose settled-red CI the drain already handled; empty when the checks are green, still pending, or unseen. The CI floor is a SHA rather than a monotonic id because the check rollup flips green↔red and resets on each push — keying on the head SHA fires the loop once per red head and re-arms on the next push ([`arm-watcher.md`](arm-watcher.md)).
|
|
179
|
+
- `REBASED` — the `rebase_key` (`<head_sha>..<base_tip_sha>`) whose behind/conflicting state the drain already handled; empty when the branch is current with its base or unseen. Keyed on the pair, not a monotonic id, because staleness depends on both sides — the loop fires once per newly-due pair and re-arms when the head or the base moves. Same key [`contract.md`](contract.md) Step 5 dedups rebase dispatch on; the monitor only signals a due rebase, the tick applies the `conflict_resolve_attempts` budget.
|
|
180
|
+
- `BLOCKED_CIDIGEST` — the head's `ci_digest` at the moment a block was flagged (the same signature `blocked.fingerprint.ci_digest` carries); empty whenever the watch is not blocked. Non-empty arms the loop's blocked-resume probe: while set, the loop wakes on any move of the live digest away from this value, so a block resumes on a green pass, a rerun, or an external deploy check — not only on red. Written by [`blocked-tick.md`](blocked-tick.md) when a block is flagged and cleared to empty when it resumes.
|
|
179
181
|
|
|
180
|
-
Written whole-file by the orchestrating session — seeded at arm time to the **drain's own observed max
|
|
182
|
+
Written whole-file by the orchestrating session — the review/comment/CI/rebase floors seeded at arm time (`REV`/`COM` to the **drain's own observed max**, `CIRED`/`REBASED` to what the drain handled) and advanced after every cycle to the **handled wave's snapshot** (the max review-id and comment-id the cycle read at the start of handling, captured before replies were posted); `BLOCKED_CIDIGEST` is maintained by the blocked-tick flag/resume path rather than the arm/cycle path. **Never** derived from a fresh fetch taken after the drain or after replies land: a later fetch includes ids that arrived in the handling window and were never read, and marking them seen swallows them ([`arm-watcher.md`](arm-watcher.md)). The advance also never moves any floor past an unresolved thread the cycle left unaddressed. Read by the watch loop each iteration; the loop never writes it. A stale watermark makes the next reported event the loop's own reply.
|
|
181
183
|
|
|
182
184
|
**Never `source` this file, and quote or extract values.** `THREADS` holds bare semicolons: sourced unquoted, the shell splits the line at the first `;` and silently drops every id after it — the loop then re-reports known threads as new. The watch loop must extract values verbatim (e.g. `grep '^THREADS=' | cut -d= -f2- | tr -d '"\r'`), tolerating quotes and CRLF; writers should quote the value anyway.
|
|
183
185
|
|
|
@@ -29,4 +29,4 @@ Pick the operation, then read its op file for the procedure.
|
|
|
29
29
|
- **Current values**: session-context line `Muggle Test Preferences key=value …`. Default `ask`.
|
|
30
30
|
- **Per-key files**: `preference-gates/<key>.md`. Key list = `ls preference-gates/*.md` minus `README.md`.
|
|
31
31
|
- **Allowed values**: `always`/`never`/`ask` (or `local`/`remote`/`ask` for `defaultExecutionMode`).
|
|
32
|
-
- **Scope**:
|
|
32
|
+
- **Scope**: preferences are user-level. Every write lands in `~/.muggle-ai/preferences.json` and applies to every repo. If the user asks for a per-project setting, say it isn't supported and confirm before setting it everywhere.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "muggle-preferences",
|
|
3
|
+
"notes": "These evals test the PLAN behavior after per-project preferences were removed. Preferences resolve from defaults overlaid by ~/.muggle-ai/preferences.json only; muggle-local-preferences-set rejects a `scope` argument. The prompts ask for a step-by-step plan rather than execution, since writing preferences mutates the user's home directory. Assertions check that the plan never offers a per-project scope, never passes `scope`, and confirms before applying a setting the user framed as project-specific.",
|
|
4
|
+
"evals": [
|
|
5
|
+
{
|
|
6
|
+
"id": 0,
|
|
7
|
+
"eval_name": "per-project-request-must-not-be-honoured-silently",
|
|
8
|
+
"prompt": "For this repo only, I want the Electron browser to actually show up when tests run — it's a UI-heavy project and I like watching it. Leave my other projects alone. Walk me through what you'd do.",
|
|
9
|
+
"files": [],
|
|
10
|
+
"assertions": [
|
|
11
|
+
{ "name": "states_preferences_are_user_level", "text": "Plan states that preferences are user-level and apply to every repo, so a per-repo setting is not supported." },
|
|
12
|
+
{ "name": "does_not_offer_project_scope", "text": "Plan does NOT offer a 'this project only' option, and does not claim a project-scoped preferences file can be written." },
|
|
13
|
+
{ "name": "no_scope_argument", "text": "Plan does not pass a `scope` argument to muggle-local-preferences-set; it calls the tool with key and value only." },
|
|
14
|
+
{ "name": "confirms_before_global_write", "text": "Plan explicitly confirms with the user before writing, because the user asked for one repo and the write will affect all of them." },
|
|
15
|
+
{ "name": "names_the_correct_key", "text": "Plan identifies showElectronBrowser as the key to set." },
|
|
16
|
+
{ "name": "does_not_invent_a_workaround", "text": "Plan does not invent a per-repo mechanism (env var, local override file, wrapper script) to simulate project scope." }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 1,
|
|
21
|
+
"eval_name": "list-shows-user-level-resolution",
|
|
22
|
+
"prompt": "show me my muggle settings and where they're stored",
|
|
23
|
+
"files": [],
|
|
24
|
+
"assertions": [
|
|
25
|
+
{ "name": "resolution_is_two_layer", "text": "Plan describes resolution as defaults overlaid by ~/.muggle-ai/preferences.json, with no third per-project layer." },
|
|
26
|
+
{ "name": "names_the_global_path", "text": "Plan names ~/.muggle-ai/preferences.json as the storage location." },
|
|
27
|
+
{ "name": "no_project_file_mentioned", "text": "Plan does not tell the user that a preferences file inside the repo is read or honoured." },
|
|
28
|
+
{ "name": "reads_from_session_context_or_tool", "text": "Plan reads current values from the session-context preferences line or the preferences tool rather than guessing." }
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": 2,
|
|
33
|
+
"eval_name": "stale-in-repo-file-explained",
|
|
34
|
+
"prompt": "I've got a .muggle-ai/preferences.json checked into my repo with autoLogin set to never, but muggle keeps logging me in automatically. Why isn't it being picked up?",
|
|
35
|
+
"files": [],
|
|
36
|
+
"assertions": [
|
|
37
|
+
{ "name": "explains_file_is_not_read", "text": "Plan explains that a preferences file inside the project is no longer read — preferences are user-level." },
|
|
38
|
+
{ "name": "points_at_global_file", "text": "Plan points the user at ~/.muggle-ai/preferences.json as the file that actually takes effect." },
|
|
39
|
+
{ "name": "offers_to_reapply_globally", "text": "Plan offers to re-apply the affected key globally, noting it will then apply to every repo." },
|
|
40
|
+
{ "name": "does_not_delete_user_file", "text": "Plan does not delete or rewrite the user's in-repo preferences file without asking." },
|
|
41
|
+
{ "name": "no_scope_argument", "text": "Plan does not attempt to pass a `scope` argument to make the project file work." }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
1. Verify `preference-gates/<key>.md` exists. If not, list `preference-gates/*.md` and ask.
|
|
4
4
|
2. Read it. Run **Picker 1** with the options defined in the gate file, plus an additional `Ask me each time` (sub: `Prompt me at decision time.`) → `ask`.
|
|
5
|
-
3. `muggle-local-preferences-set` with the mapped value
|
|
5
|
+
3. `muggle-local-preferences-set` with the mapped value.
|
|
6
6
|
4. Confirm: `Set <key> to <value>.`
|
|
7
7
|
|
|
8
8
|
Skip Picker 2 — user explicitly asked to change.
|
|
@@ -33,18 +33,17 @@ For each option: label = key name, description = first paragraph of `preference-
|
|
|
33
33
|
- `multiSelect: true`, `header: "Branch hygiene"` — `autoUseWorktree`, `autoRebase`, `autoCleanup`
|
|
34
34
|
- `multiSelect: false`, `header: "E2E acceptance"` — `autoE2ETest`. Options: `Always run Stage 6 at the end` (`always` — default), `Ask each cycle` (`ask`). No `never` option.
|
|
35
35
|
- `multiSelect: false`, `header: "Default mode"` — `defaultExecutionMode`. Options: `Local — run on my computer` (`local`), `Remote — run in the Muggle Test cloud` (`remote`), `Ask each time` (don't change).
|
|
36
|
-
- `multiSelect: false`, `header: "Scope"` — final scope question. Options: `Global (all repos)` (~/.muggle-ai/), `This project only` (.muggle-ai/ in repo).
|
|
37
36
|
|
|
38
|
-
`AskUserQuestion` accepts up to 4 questions per call — split
|
|
37
|
+
`AskUserQuestion` accepts up to 4 questions per call — split across calls.
|
|
39
38
|
|
|
40
39
|
## Step 4 — apply selections
|
|
41
40
|
|
|
42
|
-
For each toggled key (multi-select questions): `muggle-local-preferences-set` with `value: "always"`. For `defaultExecutionMode`: only set if user picked Local/Remote (skip "Ask each time").
|
|
41
|
+
For each toggled key (multi-select questions): `muggle-local-preferences-set` with `value: "always"`. For `defaultExecutionMode`: only set if user picked Local/Remote (skip "Ask each time").
|
|
43
42
|
|
|
44
43
|
## Step 5 — `never` follow-up
|
|
45
44
|
|
|
46
|
-
Ask: `Want any of these set to "never" (auto-skip without asking)? Name them, e.g. "never on showElectronBrowser", or say "no".`. For named keys, call `muggle-local-preferences-set` with `value: "never"
|
|
45
|
+
Ask: `Want any of these set to "never" (auto-skip without asking)? Name them, e.g. "never on showElectronBrowser", or say "no".`. For named keys, call `muggle-local-preferences-set` with `value: "never"`.
|
|
47
46
|
|
|
48
47
|
## Step 6 — confirm
|
|
49
48
|
|
|
50
|
-
One-liner summary: `Set autoLogin=always, openTestResultsAfterRun=always
|
|
49
|
+
One-liner summary: `Set autoLogin=always, openTestResultsAfterRun=always.`
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Set — direct (key + value)
|
|
2
2
|
|
|
3
|
-
Trigger: user names both key and value (e.g. "set autoLogin to always", "make showElectronBrowser never
|
|
3
|
+
Trigger: user names both key and value (e.g. "set autoLogin to always", "make showElectronBrowser never").
|
|
4
4
|
|
|
5
5
|
1. Parse `key` and `value`.
|
|
6
6
|
2. Verify `preference-gates/<key>.md` exists. If not, list `preference-gates/*.md` and ask.
|
|
7
7
|
3. Validate `value` per Shared context.
|
|
8
|
-
4.
|
|
9
|
-
5. `
|
|
10
|
-
6. Confirm: `Set {key} to {value} ({scope}).`
|
|
8
|
+
4. `muggle-local-preferences-set`.
|
|
9
|
+
5. Confirm: `Set {key} to {value}.`
|
|
@@ -11,8 +11,8 @@ which uses `local` / `remote` / `ask`).
|
|
|
11
11
|
## Resolution
|
|
12
12
|
|
|
13
13
|
`SessionStart` injects a `Muggle Test Preferences` line (`key=value` pairs) from
|
|
14
|
-
`~/.muggle-ai/preferences.json
|
|
15
|
-
|
|
14
|
+
`~/.muggle-ai/preferences.json`. Preferences are user-level — there is no
|
|
15
|
+
per-project layer. Absent → treat as `ask`.
|
|
16
16
|
|
|
17
17
|
## Gate behavior
|
|
18
18
|
|
|
@@ -48,7 +48,7 @@ Header `Remember this choice?`. Question: `"Always <restate Picker 1 choice in
|
|
|
48
48
|
plain language> from now on, without asking?"`. Never put the raw key or
|
|
49
49
|
`=` in the user-visible text.
|
|
50
50
|
|
|
51
|
-
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → `muggle-local-preferences-set` with the value Picker 1 mapped to
|
|
51
|
+
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → `muggle-local-preferences-set` with the value Picker 1 mapped to.
|
|
52
52
|
- `Just this once` (sub: `I'll ask again next time.`) → don't save.
|
|
53
53
|
|
|
54
54
|
A few keys override this template (their per-key file says how).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Reuse the saved local dev server URL for this repo, or pick one each run. Substitute `{lastHost}` (the URL used in the previous run for this repo — omit the option entirely when no cache exists) and `{suggestedHost}` (auto-detected from running ports, e.g. `http://localhost:3000`).
|
|
4
4
|
|
|
5
|
-
Cache lives
|
|
5
|
+
Cache lives in `~/.muggle-ai/last-host.json`, in the entry keyed on `<cwd>`. The calling skill **always** updates the cache after the user picks/types a URL — independent of Picker 2 — so `Use {lastHost}` reflects the most recent run.
|
|
6
6
|
|
|
7
7
|
**Picker 1** — header `Local server`, question `"Which local URL should the test target?"`
|
|
8
8
|
- `Use {lastHost}` — `From your last run in this repo.` → reuse cached URL. *Skip this option when no cache exists.*
|
|
@@ -11,7 +11,7 @@ Cache lives at `<cwd>/.muggle-ai/last-host.json`. The calling skill **always** u
|
|
|
11
11
|
|
|
12
12
|
**Picker 2 — overrides shared template.** Fires after the user picks a URL.
|
|
13
13
|
- Header `Remember this URL?`, question `"Always use {chosenHost} for this repo from now on, without asking?"`
|
|
14
|
-
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → `muggle-local-preferences-set` (`autoSelectLocalHost=always
|
|
14
|
+
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → `muggle-local-preferences-set` (`autoSelectLocalHost=always`). The cache is already up to date.
|
|
15
15
|
- `Just this once` (sub: `I'll ask again next time.`) → don't save the preference. The cache still updates.
|
|
16
16
|
|
|
17
17
|
**Silent action**
|
|
@@ -8,7 +8,7 @@ tail options like "Show full list" / "Create new project" are skill-defined).
|
|
|
8
8
|
**Picker 2 — overrides shared template.** Fires only after picking an
|
|
9
9
|
*existing* project; skip if user picked "Create new project".
|
|
10
10
|
- Header `Reuse this project next time?`, question `"Always reuse {projectName} for this repo from now on, without asking?"`
|
|
11
|
-
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → call BOTH `muggle-local-preferences-set` (`autoSelectProject=always
|
|
11
|
+
- `Yes, always` (sub: `You can change this later in muggle preferences.`) → call BOTH `muggle-local-preferences-set` (`autoSelectProject=always`) AND `muggle-local-last-project-set` (`cwd`, `projectId`, `projectUrl`, `projectName`).
|
|
12
12
|
- `Just this once` (sub: `I'll ask again next time.`) → don't save.
|
|
13
13
|
|
|
14
14
|
**Silent action**
|
|
@@ -128,7 +128,7 @@ If auth fails repeatedly, suggest: `muggle logout && muggle login` from terminal
|
|
|
128
128
|
|
|
129
129
|
A **project** is where all your test results, use cases, and test scripts are grouped on the Muggle AI dashboard. Pick the project that matches what you're working on.
|
|
130
130
|
|
|
131
|
-
The per-repo cache lives
|
|
131
|
+
The per-repo cache lives in `~/.muggle-ai/last-project.json`, in the entry keyed on `<cwd>` (managed via the `muggle-local-last-project-get` / `muggle-local-last-project-set` MCP tools). Look for the `Muggle Test Last Project: id=… url=… name="…"` line in session context — if present, that's this repo's cached pick.
|
|
132
132
|
|
|
133
133
|
Gate `autoSelectProject` (per `preference-gates/README.md`). Cache: `Muggle Test Last Project` session line.
|
|
134
134
|
- `always` + cache → use cached `projectId`, skip to Step 5. No cache → fall through to `ask`.
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
Before anything else, invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) — the readiness/service-start owner (idempotent; halt on what it surfaces). The URL gate below only *selects* the target; prepare is what guarantees something is listening and compiled.
|
|
14
14
|
|
|
15
|
+
Then read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists — this stack's recorded startup order, manual steps, and local gotchas. Use it to interpret what you see: a gotcha listed there explains a symptom that would otherwise read as a failure. Absent is normal.
|
|
16
|
+
|
|
15
17
|
## Pre-flight question — Local URL (gated by `autoSelectLocalHost`)
|
|
16
18
|
|
|
17
19
|
Skill responsibilities (the rest is in `preference-gates/autoSelectLocalHost.md`):
|
|
@@ -29,6 +29,8 @@ Three gates apply, each per the standard procedure in [`preference-gates/README.
|
|
|
29
29
|
|
|
30
30
|
Before any workflow step, invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md). Halt on what it surfaces.
|
|
31
31
|
|
|
32
|
+
Then read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists — this stack's recorded startup order, manual steps, and local gotchas. A gotcha listed there explains a symptom that would otherwise read as a failure. Absent is normal.
|
|
33
|
+
|
|
32
34
|
## UX Guidelines — Minimize Typing
|
|
33
35
|
|
|
34
36
|
**Every selection-based question MUST use the `AskUserQuestion` tool** (or the platform's equivalent structured selection tool). Never ask the user to "reply with a number" in a plain text message — always present clickable options.
|
|
@@ -67,7 +69,7 @@ Gates run per `preference-gates/README.md`.
|
|
|
67
69
|
|
|
68
70
|
### 2. Targets (user must confirm)
|
|
69
71
|
|
|
70
|
-
The per-repo project cache lives
|
|
72
|
+
The per-repo project cache lives in `~/.muggle-ai/last-project.json`, in the entry keyed on `<cwd>` (via the `muggle-local-last-project-get` / `muggle-local-last-project-set` MCP tools). Look for `Muggle Test Last Project: id=… url=… name="…"` in session context.
|
|
71
73
|
|
|
72
74
|
Gate `autoSelectProject` (per `preference-gates/README.md`). Cache: `Muggle Test Last Project` session line.
|
|
73
75
|
- `always` + cache → use cached `projectId`, skip to use case selection. No cache → fall through to `ask`.
|
|
@@ -151,7 +151,7 @@ If **not authenticated**:
|
|
|
151
151
|
|
|
152
152
|
A **project** is where all your imported use cases, test cases, and future test results are grouped on the Muggle AI dashboard.
|
|
153
153
|
|
|
154
|
-
The per-repo project cache lives
|
|
154
|
+
The per-repo project cache lives in `~/.muggle-ai/last-project.json`, in the entry keyed on `<cwd>` (via the `muggle-local-last-project-get` / `muggle-local-last-project-set` MCP tools). Look for `Muggle Test Last Project: id=… url=… name="…"` in session context.
|
|
155
155
|
|
|
156
156
|
Gate `autoSelectProject` (per `preference-gates/README.md`). Cache: `Muggle Test Last Project` session line.
|
|
157
157
|
- `always` + cache → use cached `projectId`, skip to Step 6. No cache → fall through to `ask`.
|
|
@@ -48,7 +48,11 @@ All launched processes are tracked in `/tmp/muggle-test-prepare.json`:
|
|
|
48
48
|
|
|
49
49
|
`testing_scope` records what the user is testing (from [scope](./steps/scope.md)). `excluded_services` records services the user said can't run locally (from [viability-check](./steps/viability-check.md)).
|
|
50
50
|
|
|
51
|
-
This file is **ephemeral runtime state**, not the saved recipe. The durable plan lives
|
|
51
|
+
This file is **ephemeral runtime state**, not the saved recipe. The durable plan lives in `~/.muggle-ai/prepare-plans.json`, under the entry keyed on this stack, and is consulted in [reuse-plan](./steps/reuse-plan.md) before any other stage. The two files never merge. The `test-prepare-runner` agent writes this file during execution; the triage below and Cleanup read it.
|
|
52
|
+
|
|
53
|
+
The prose companion to the plan is `~/.muggle-ai/e2e-instructions/<key>.md`, written by [e2e-instructions](./steps/e2e-instructions.md), holding startup order, manual steps, and local gotchas. The plan remains the single source of truth for each service's start command; the markdown never restates one.
|
|
54
|
+
|
|
55
|
+
**Everything this skill saves is machine-local, user-level data and lives under the Muggle home directory — never inside the user's project.** A project directory is shared, versioned, and cloned onto machines set up differently; a local run recipe is none of those things. Both files are keyed on the same stack identity, so they stay in lockstep.
|
|
52
56
|
|
|
53
57
|
**On every invocation**, check this file first. If it exists with live PIDs (verify with `kill -0`), `AskUserQuestion`:
|
|
54
58
|
- Option 1: "Keep them running — skip to testing"
|
|
@@ -64,7 +68,7 @@ Gates run per [`preference-gates/README.md`](../muggle-preferences/preference-ga
|
|
|
64
68
|
| Preference | Gates |
|
|
65
69
|
|------------|-------|
|
|
66
70
|
| `autoRebase` | [rebase-check](./steps/rebase-check.md) — rebase onto `origin/<default>` before starting dev servers |
|
|
67
|
-
| `reusePreparePlan` | [reuse-plan](./steps/reuse-plan.md) — reuse the saved prepare plan for this stack, or rediscover |
|
|
71
|
+
| `reusePreparePlan` | [reuse-plan](./steps/reuse-plan.md) — reuse the saved prepare plan for this stack, or rediscover; also [e2e-instructions](./steps/e2e-instructions.md), which goes stale for the same reason |
|
|
68
72
|
| `autoSelectLocalHost` | [check-running](./steps/check-running.md) — reuse the recorded dev-server URL silently, or confirm it each run |
|
|
69
73
|
|
|
70
74
|
## Workflow
|
|
@@ -78,8 +82,9 @@ Gates run per [`preference-gates/README.md`](../muggle-preferences/preference-ga
|
|
|
78
82
|
| 2 | [scope](./steps/scope.md) | Frontend / backend / full stack |
|
|
79
83
|
| 3 | [viability-check](./steps/viability-check.md) | Exclude services that can't run locally |
|
|
80
84
|
| 4 | [identify-services](./steps/identify-services.md) | Pick required services + startup mode |
|
|
85
|
+
| 5 | [e2e-instructions](./steps/e2e-instructions.md) | Capture startup order, manual steps, local gotchas (gated); persisted for reuse |
|
|
81
86
|
|
|
82
|
-
The Decide phase's output is the **resolved prepare plan**: `services[]` (name, dir, start command, expected port, `external` flag, approval granted), `testingScope`, `excludedServices[]`, the recorded dev-server URL, and resolved gate outcomes.
|
|
87
|
+
The Decide phase's output is the **resolved prepare plan**: `services[]` (name, dir, start command, expected port, `external` flag, approval granted), `testingScope`, `excludedServices[]`, the recorded dev-server URL, the E2E run instructions, and resolved gate outcomes.
|
|
83
88
|
|
|
84
89
|
**Execute (agent).** Dispatch the `test-prepare-runner` agent (subagent type `muggle:test-prepare-runner`; bare `test-prepare-runner` where the plugin namespace is absent), synchronously, passing the resolved plan; it returns `READY` / `DEGRADED` plus the readiness table. The agent's own definition lists its stage files; in a harness with no agent/subagent facility, run the execute-phase stages ([check-running](./steps/check-running.md) through [readiness-report](./steps/readiness-report.md)) inline instead.
|
|
85
90
|
|
|
@@ -114,12 +119,13 @@ After a test run, the caller can re-invoke for cleanup or leave services running
|
|
|
114
119
|
|
|
115
120
|
## Guardrails
|
|
116
121
|
|
|
117
|
-
- **Never invent or default a host/port** — the dev-server URL is a recorded value, not a guess.
|
|
122
|
+
- **Never invent or default a host/port** — the dev-server URL is a recorded value, not a guess. Read it from the last-host cache via `muggle-local-last-host-get` (the [`autoSelectLocalHost`](../muggle-preferences/preference-gates/autoSelectLocalHost.md) cache) before probing ports; a framework default like `:3000` is never a fallback. The cache's own storage location belongs to that tool — don't restate it here. See [check-running](./steps/check-running.md).
|
|
118
123
|
- **No silent auto-selection without a gate** — when no preference authorizes a silent choice (host, restart, kill), confirm with the user. A gate set to `always` is the only license to skip the question; absent that, ask.
|
|
119
124
|
- **Verify first, offer to start second** — check what's already running before proposing to start anything.
|
|
120
125
|
- **The user may prefer to start services themselves** — always offer that option.
|
|
121
126
|
- **Never start a process the user didn't approve** — approvals are granted in Decide and travel in the plan; the agent starts nothing outside it.
|
|
122
127
|
- **Never read file contents outside confirmed directories** — folder names are discoverable; file contents require explicit user selection.
|
|
128
|
+
- **Never write a credential into the E2E run instructions** — plaintext notes are not a secret store, and this is the file a user pastes when asking why their stack won't start. Record the env-var or secret name, never its value.
|
|
123
129
|
- **Never leave orphan processes untracked** — every background PID goes into the tracking file.
|
|
124
130
|
- **Never kill a process the user started independently** — `external: true` survives cleanup.
|
|
125
131
|
- **Never assume start commands** — verify via indicator file; confirm with user.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
The dev-server URL the tests will hit is a **recorded value, not a guess** — resolve it before probing anything.
|
|
6
6
|
|
|
7
|
-
1. Read the cached host with `muggle-local-last-host-get`. It reads `<cwd
|
|
7
|
+
1. Read the cached host with `muggle-local-last-host-get`. It reads the entry keyed on `<cwd>` in `~/.muggle-ai/last-host.json`; a worktree usually has **no cache of its own**, so when the worktree returns nothing, pass the **main** working-tree root as `cwd` — `git rev-parse --git-common-dir`, then its parent directory.
|
|
8
8
|
2. Apply the [`autoSelectLocalHost`](../../muggle-preferences/preference-gates/autoSelectLocalHost.md) gate (read its value from the `Muggle Test Preferences` session-context line; absent → `ask`):
|
|
9
9
|
- `always` **and** a cache exists → use it silently: `Using saved local URL {lastHost}`.
|
|
10
10
|
- otherwise (`ask` / `never`, or no cache) → **confirm before using any host.** Run the gate's Picker 1 with `{lastHost}` (cached URL, omitted when absent) and `{suggestedHost}` (a port you actually detect listening). Never auto-pick, and never fall back to a framework default like `:3000`; if nothing is cached or detected, ask the user to type the URL.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Stage 5 — E2E run instructions
|
|
2
|
+
|
|
3
|
+
Capture what `prepare-plan.json` cannot express: the order services must come up in, steps that aren't a single command, and the local gotchas that make a healthy stack look broken. Persisted under `~/.muggle-ai/e2e-instructions/` and reused on later runs.
|
|
4
|
+
|
|
5
|
+
Runs after [identify-services](./identify-services.md) — startup order and per-service gotchas are unanswerable until the service set is known.
|
|
6
|
+
|
|
7
|
+
## Scope boundary
|
|
8
|
+
|
|
9
|
+
`prepare-plan.json` owns the per-service start command. Never restate a command here; reference the plan. This file holds only what the plan has no field for:
|
|
10
|
+
|
|
11
|
+
| Belongs here | Belongs in `prepare-plan.json` |
|
|
12
|
+
|:-------------|:-------------------------------|
|
|
13
|
+
| Order and dependencies between services | Each service's `name`, `dir`, `command`, `port` |
|
|
14
|
+
| Steps that aren't one command (a migration to run first, a tunnel to open, a container to bring up by hand) | — |
|
|
15
|
+
| Gotchas — slow first build, a port that isn't the framework default, a rate limit, a warning that is safe to ignore | — |
|
|
16
|
+
|
|
17
|
+
## Resolve the saved file
|
|
18
|
+
|
|
19
|
+
This is machine-local, user-level data. It lives under the Muggle home directory and never inside the user's project — a project directory is shared, versioned, and cloned by people whose machines are set up differently, and none of that is true of a local run recipe.
|
|
20
|
+
|
|
21
|
+
One file per stack:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
~/.muggle-ai/e2e-instructions/<key>.md
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`<key>` is the absolute path of the working directory's parent — the same stack identity the global prepare plan keys its entries on — reduced to a filename-safe token by replacing every path separator, and any drive-letter colon, with `-`. Derive it from the resolved absolute path rather than assuming a separator character; they differ per platform.
|
|
28
|
+
|
|
29
|
+
Missing → no saved instructions; run the capture below.
|
|
30
|
+
|
|
31
|
+
## Gate `reusePreparePlan`
|
|
32
|
+
|
|
33
|
+
Same gate as [reuse-plan](./reuse-plan.md) — this content goes stale for the same reason the service plan does, so one answer governs both. Only fires when a saved file was loaded.
|
|
34
|
+
|
|
35
|
+
- `always` → reuse silently. Print `Reusing saved E2E run instructions`.
|
|
36
|
+
- `never` → discard and run the capture.
|
|
37
|
+
- `ask` → print the saved file, then Picker 1 from the gate contract. Reuse on `Reuse this plan`, capture on `Rediscover from scratch`.
|
|
38
|
+
|
|
39
|
+
On the [reuse-plan](./reuse-plan.md) short-circuit path this stage is skipped along with the rest of the Decide phase; the saved file is loaded there and carried forward unchanged.
|
|
40
|
+
|
|
41
|
+
## Capture
|
|
42
|
+
|
|
43
|
+
Print the resolved service list first so the user answers against concrete names, then ask one `AskUserQuestion`, multi-select:
|
|
44
|
+
|
|
45
|
+
> "Anything Muggle should know about running these locally?"
|
|
46
|
+
|
|
47
|
+
- `Startup order matters` — `One service must be up before another, or something fails.`
|
|
48
|
+
- `Manual steps` — `Something has to happen that isn't one of the start commands.`
|
|
49
|
+
- `Known gotchas` — `Behaviour that looks like a failure but isn't, or a trap to avoid.`
|
|
50
|
+
- `Nothing special` — `They start independently and just work.`
|
|
51
|
+
|
|
52
|
+
`Nothing special` (or no selection) → write the sentinel from [Sentinel](#sentinel) and return. Do not re-ask on later runs; the sentinel is a recorded answer, not an empty file.
|
|
53
|
+
|
|
54
|
+
Otherwise ask once more, free-text, naming only the selected categories. One turn — never a question per category.
|
|
55
|
+
|
|
56
|
+
## Written format
|
|
57
|
+
|
|
58
|
+
Fixed headings. Omit a section the user had nothing for; never emit an empty one.
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
# E2E run instructions
|
|
62
|
+
|
|
63
|
+
<!-- Managed by muggle-test-prepare. Hand-edits are preserved — re-run the skill to revise. -->
|
|
64
|
+
|
|
65
|
+
**Updated:** 2026-08-04T12:00:00Z
|
|
66
|
+
|
|
67
|
+
## Startup order
|
|
68
|
+
|
|
69
|
+
api → worker → ui. The UI 500s on /dashboard when api isn't listening yet.
|
|
70
|
+
|
|
71
|
+
## Manual steps
|
|
72
|
+
|
|
73
|
+
Run `pnpm db:migrate` once after a fresh clone; the dev servers don't migrate on boot.
|
|
74
|
+
|
|
75
|
+
## Local gotchas
|
|
76
|
+
|
|
77
|
+
- First build after a clean install takes ~4 min. It is not hung.
|
|
78
|
+
- UI is on :3999, not the Next.js default :3000.
|
|
79
|
+
- Auth0 dev tenant rate-limits past ~20 logins/hour.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Sentinel
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
# E2E run instructions
|
|
86
|
+
|
|
87
|
+
<!-- Managed by muggle-test-prepare. Hand-edits are preserved — re-run the skill to revise. -->
|
|
88
|
+
|
|
89
|
+
**Updated:** 2026-08-04T12:00:00Z
|
|
90
|
+
|
|
91
|
+
Nothing special — services start independently.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Secrets
|
|
95
|
+
|
|
96
|
+
Never write a credential value. A password, token, API key, or connection string with embedded credentials belongs in a secret store or an env file — not in plaintext notes. Living outside the project keeps this file out of version control, but it is still readable on disk and is exactly the kind of file a user pastes into an issue when asking why their stack won't come up.
|
|
97
|
+
|
|
98
|
+
Record a **pointer** instead: the env-var name, or the name of the Muggle secret. `Set LOCAL_TEST_PASSWORD before running` is fine; the password is not.
|
|
99
|
+
|
|
100
|
+
If the user's free text contains something that looks like a credential, drop it, write the pointer form, and say so in one line.
|
|
@@ -41,10 +41,7 @@ jq '{
|
|
|
41
41
|
}' /tmp/muggle-test-prepare.json
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- If `git rev-parse --show-toplevel` succeeds (call the result `$REPO`) → write `$REPO/.muggle-ai/prepare-plan.json`. Create `$REPO/.muggle-ai/` if missing.
|
|
47
|
-
- Else → upsert the entry under key `$(dirname "$PWD")` (absolute) in `~/.muggle-ai/prepare-plans.json`. Create the file as `{}` if missing.
|
|
44
|
+
Upsert it under the stack's key — the absolute path of the working directory's parent — in `~/.muggle-ai/prepare-plans.json`, creating the file as `{}` if missing. The plan is machine-local, user-level data: it never goes inside the user's project, and resolving it requires no version-control tool.
|
|
48
45
|
|
|
49
46
|
Then print, once:
|
|
50
47
|
|
|
@@ -54,3 +51,11 @@ Then print, once:
|
|
|
54
51
|
```
|
|
55
52
|
|
|
56
53
|
If this run short-circuited via [reuse-plan](./reuse-plan.md), don't rewrite — but **do** refresh `updated` and any `command` that was re-derived during validation. Skip the announcement on the refresh path.
|
|
54
|
+
|
|
55
|
+
## Save the E2E run instructions
|
|
56
|
+
|
|
57
|
+
The instructions arrive resolved in the dispatch prompt — captured while the user was present, in [e2e-instructions](./e2e-instructions.md). Write them verbatim, in that stage's format, to the location it resolves: `~/.muggle-ai/e2e-instructions/<key>.md`. Create the directory if missing. This file is machine-local and never goes inside the user's project.
|
|
58
|
+
|
|
59
|
+
Nothing resolved in the plan → write nothing. An absent file is a stage that has not run yet; an empty one would read as "nothing to say" and suppress the question forever.
|
|
60
|
+
|
|
61
|
+
Never write a credential value here — pointers only, per that stage's Secrets rule.
|
|
@@ -6,12 +6,13 @@ A previously saved **prepare plan** is the durable recipe for this stack. Distin
|
|
|
6
6
|
|
|
7
7
|
In order; first hit wins.
|
|
8
8
|
|
|
9
|
-
1. **
|
|
10
|
-
2. **
|
|
11
|
-
3. **No plan found** → exit this step; the workflow continues at [rebase-check](./rebase-check.md).
|
|
9
|
+
1. **Saved plan.** If `~/.muggle-ai/prepare-plans.json` exists, read the entry keyed on this stack — the absolute path of the working directory's parent. If present → load that entry's value. The plan is machine-local and is never read from, or written to, the user's project.
|
|
10
|
+
2. **No plan found** → exit this step; the workflow continues at [rebase-check](./rebase-check.md).
|
|
12
11
|
|
|
13
12
|
A loaded plan is a JSON object with `version`, `updated`, `testing_scope`, `excluded_services`, `services`. Reject and treat as "no plan" if `version != 1` or `services` is empty.
|
|
14
13
|
|
|
14
|
+
Load the prose companion per [e2e-instructions](./e2e-instructions.md) — `~/.muggle-ai/e2e-instructions/<key>.md`, keyed on the same stack identity as the global plan entry. It is independent of the plan: a missing companion is not a missing plan, and vice versa.
|
|
15
|
+
|
|
15
16
|
## Gate `reusePreparePlan`
|
|
16
17
|
|
|
17
18
|
Per [`muggle-preferences/preference-gates/README.md`](../../muggle-preferences/preference-gates/README.md). Read the current value from the `Muggle Test Preferences` session-context line; absent → `ask`.
|
|
@@ -37,7 +38,7 @@ Per [`muggle-preferences/preference-gates/README.md`](../../muggle-preferences/p
|
|
|
37
38
|
- The indicator file that produced `command` still exists in `dir` (e.g. `package.json` for an `npm`/`node` command; see the indicator table in [start-commands](./start-commands.md)) → keep. Else re-derive **just that one entry** by running the indicator-detection from [start-commands](./start-commands.md) against `dir`, and replace its `command`. Log `"Re-derived <name>: <old> → <new>"`.
|
|
38
39
|
2. **All entries dropped** → discard the plan; continue at [rebase-check](./rebase-check.md). Otherwise proceed with surviving + re-derived entries.
|
|
39
40
|
3. **Hydrate** `/tmp/muggle-test-prepare.json` with the surviving entries (no PIDs yet, `testing_scope` from the plan, `excluded_services` from the plan).
|
|
40
|
-
4. **Short-circuit** to [check-running](./check-running.md). The skipped stages are [scope](./scope.md), [viability-check](./viability-check.md), [identify-services](./identify-services.md), [start-commands](./start-commands.md) — the reused plan
|
|
41
|
+
4. **Short-circuit** to [check-running](./check-running.md). The skipped stages are [scope](./scope.md), [viability-check](./viability-check.md), [identify-services](./identify-services.md), [start-commands](./start-commands.md), [e2e-instructions](./e2e-instructions.md) — the reused plan and its companion supply their answers. Carry the loaded instructions forward unchanged; the user is not re-asked. When the companion is absent, run [e2e-instructions](./e2e-instructions.md) once to capture it, then continue the short-circuit. The remaining stages run normally: [env-file](./env-file.md), [fresh-install](./fresh-install.md), [start-services](./start-services.md) (only for entries not already listening), [smoke-test](./smoke-test.md), [readiness-report](./readiness-report.md).
|
|
41
42
|
|
|
42
43
|
## Rediscover path
|
|
43
44
|
|
|
@@ -65,7 +65,7 @@ If auth keeps failing, suggest the user run `muggle logout && muggle login` from
|
|
|
65
65
|
|
|
66
66
|
A **project** is the unit on the Muggle AI dashboard that groups test cases, scripts, and runs. The user must pick the one to scan — never auto-select from repo name, branch, or URL heuristics.
|
|
67
67
|
|
|
68
|
-
The per-repo project cache lives
|
|
68
|
+
The per-repo project cache lives in `~/.muggle-ai/last-project.json`, in the entry keyed on `<cwd>` (via the `muggle-local-last-project-get` / `muggle-local-last-project-set` MCP tools). Look for `Muggle Test Last Project: id=… url=… name="…"` in session context.
|
|
69
69
|
|
|
70
70
|
Gate `autoSelectProject` (per `preference-gates/README.md`). Cache: `Muggle Test Last Project` session line.
|
|
71
71
|
- `always` + cache → use cached `projectId`, proceed to Step 3. No cache → fall through to `ask`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release": "5.
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-08-
|
|
2
|
+
"release": "5.10.0",
|
|
3
|
+
"buildId": "run-70-1",
|
|
4
|
+
"commitSha": "a26c16cabb82b9edddb41ec8569f103d7ec5ab1b",
|
|
5
|
+
"buildTime": "2026-08-07T07:38:27Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DEFAULT_PREFERENCES, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports as e2e, formatPreferencesOneLiner, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports as localQa, mcp_exports as mcp, openBrowserUrl, performLogin, performLogout, pollDeviceCode, e2e_exports as qa, reconcileProjectPreferences, resetConfig, resetLogger, resetPreference, resolveElectronAppPathOrNull, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, startDeviceCodeFlow, toolRequiresAuth, validatePreference, verifyFileChecksum, writePreferences } from './chunk-OCLMXAVJ.js';
|