diff --git a/benchmark/SKILL.md.tmpl b/benchmark/SKILL.md.tmpl index 038f16f5f..4a524a5d8 100644 --- a/benchmark/SKILL.md.tmpl +++ b/benchmark/SKILL.md.tmpl @@ -3,7 +3,7 @@ name: benchmark preamble-tier: 1 version: 1.0.0 description: | - Performance regression detection using the browse daemon. Establishes + Performance regression detection. Establishes baselines for page load times, Core Web Vitals, and resource sizes. Compares before/after on every PR. Tracks performance trends over time. Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals", @@ -25,13 +25,15 @@ allowed-tools: {{PREAMBLE}} -{{BROWSE_SETUP}} +{{ASIDE_SETUP}} + +{{BROWSE_FALLBACK}} # /benchmark — Performance Regression Detection You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow. -Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages. +Your job is to measure, baseline, compare, and alert. You drive the Aside browser and read `performance.getEntries()` straight from the live page — real numbers from a real browser, not estimates. ## User-invocable When the user types `/benchmark`, run this skill. @@ -65,42 +67,34 @@ git diff $(gh pr view --json baseRefName -q .baseRefName 2>/dev/null || gh repo ### Phase 3: Performance Data Collection -For each page, collect comprehensive performance metrics: +For each page, ONE `aside repl` script opens the page and prints every metric as a labelled line. Tabs die when the script ends, so nothing carries over between pages — each page gets its own run: ```bash -$B goto -$B perf +aside repl ' +const pg = await openTab(""); +await pg.waitForLoadState("load"); +console.log("NAV=" + await pg.evaluate(() => JSON.stringify(performance.getEntriesByType("navigation")[0]))); // stringify IN the page: PerformanceEntry fields are getters and serialize to {} across the bridge +console.log("PAINT=" + await pg.evaluate(() => JSON.stringify(performance.getEntriesByType("paint").map(p => ({ name: p.name, start: Math.round(p.startTime) }))))); +console.log("LCP=" + await pg.evaluate(() => new Promise(res => { const po = new PerformanceObserver(l => { const e = l.getEntries().pop(); if (e) res(Math.round(e.startTime)); }); po.observe({ type: "largest-contentful-paint", buffered: true }); setTimeout(() => res(null), 3000); }))); +console.log("RESOURCES=" + JSON.stringify(await pg.evaluate(() => performance.getEntriesByType("resource").map(r => ({ name: r.name.split("/").pop().split("?")[0], type: r.initiatorType, size: r.transferSize, duration: Math.round(r.duration) })).sort((a, b) => b.duration - a.duration).slice(0, 15)))); +console.log("SCRIPTS=" + JSON.stringify(await pg.evaluate(() => performance.getEntriesByType("resource").filter(r => r.initiatorType === "script").map(r => ({ name: r.name.split("/").pop().split("?")[0], size: r.transferSize }))))); +console.log("CSS=" + JSON.stringify(await pg.evaluate(() => performance.getEntriesByType("resource").filter(r => r.initiatorType === "css").map(r => ({ name: r.name.split("/").pop().split("?")[0], size: r.transferSize }))))); +console.log("SUMMARY=" + JSON.stringify(await pg.evaluate(() => { const r = performance.getEntriesByType("resource"); return { total_requests: r.length, total_transfer: r.reduce((s, e) => s + (e.transferSize || 0), 0), by_type: Object.entries(r.reduce((a, e) => { a[e.initiatorType] = (a[e.initiatorType] || 0) + 1; return a; }, {})).sort((a, b) => b[1] - a[1]) }; }))); +await closeTab(pg); console.log("GSTACK_STEP_OK"); +' ``` -Then gather detailed metrics via JavaScript: +`NAV=` is the navigation timing entry, `PAINT=` the paint entries (FCP lives here), `LCP=` the largest-contentful-paint start time (`null` if the page emitted no LCP entry within 3s), `RESOURCES=` the 15 slowest resources, `SCRIPTS=` / `CSS=` the bundle inventory, `SUMMARY=` request count, total transfer, and requests by type. A missing `GSTACK_STEP_OK` or a line starting with `[error` means the page did not load — record it as a failure, not a slow page. -```bash -$B eval "JSON.stringify(performance.getEntriesByType('navigation')[0])" -``` - -Extract key metrics: +Extract key metrics from `NAV=`: - **TTFB** (Time to First Byte): `responseStart - requestStart` -- **FCP** (First Contentful Paint): from PerformanceObserver or `paint` entries -- **LCP** (Largest Contentful Paint): from PerformanceObserver +- **FCP** (First Contentful Paint): the `first-contentful-paint` entry in `PAINT=` +- **LCP** (Largest Contentful Paint): the `LCP=` line (`null` if the page emitted no LCP entry — record it as missing, not 0) - **DOM Interactive**: `domInteractive - navigationStart` - **DOM Complete**: `domComplete - navigationStart` - **Full Load**: `loadEventEnd - navigationStart` -Resource analysis: -```bash -$B eval "JSON.stringify(performance.getEntriesByType('resource').map(r => ({name: r.name.split('/').pop().split('?')[0], type: r.initiatorType, size: r.transferSize, duration: Math.round(r.duration)})).sort((a,b) => b.duration - a.duration).slice(0,15))" -``` - -Bundle size check: -```bash -$B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'script').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))" -$B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'css').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))" -``` - -Network summary: -```bash -$B eval "(() => { const r = performance.getEntriesByType('resource'); return JSON.stringify({total_requests: r.length, total_transfer: r.reduce((s,e) => s + (e.transferSize||0), 0), by_type: Object.entries(r.reduce((a,e) => { a[e.initiatorType] = (a[e.initiatorType]||0) + 1; return a; }, {})).sort((a,b) => b[1]-a[1])})})()" -``` +Load times jitter with the network. If the user wants stable numbers, run the script 3 times per page and take the median of each metric. ### Phase 4: Baseline Capture (--baseline mode) diff --git a/canary/SKILL.md.tmpl b/canary/SKILL.md.tmpl index d1eb2950a..da9837fcb 100644 --- a/canary/SKILL.md.tmpl +++ b/canary/SKILL.md.tmpl @@ -4,7 +4,7 @@ preamble-tier: 2 version: 1.0.0 description: | Post-deploy canary monitoring. Watches the live app for console errors, - performance regressions, and page failures using the browse daemon. Takes + performance regressions, and page failures. Takes periodic screenshots, compares against pre-deploy baselines, and alerts on anomalies. Use when: "monitor deploy", "canary", "post-deploy check", "watch production", "verify deploy". (gstack) @@ -22,7 +22,9 @@ triggers: {{PREAMBLE}} -{{BROWSE_SETUP}} +{{ASIDE_SETUP}} + +{{BROWSE_FALLBACK}} {{BASE_BRANCH_DETECT}} @@ -30,7 +32,7 @@ triggers: You are a **Release Reliability Engineer** watching production after a deploy. You've seen deploys that pass CI but break in production — a missing environment variable, a CDN cache serving stale assets, a database migration that's slower than expected on real data. Your job is to catch these in the first 10 minutes, not 10 hours. -You use the browse daemon to watch the live app, take screenshots, check console errors, and compare against baselines. You are the safety net between "shipped" and "verified." +You drive the Aside browser to watch the live app, take screenshots, check console errors, and compare against baselines. You are the safety net between "shipped" and "verified." ## User-invocable When the user types `/canary`, run this skill. @@ -62,14 +64,24 @@ If the user passed `--baseline`, capture the current state BEFORE deploying. For each page (either from `--pages` or the homepage): ```bash -$B goto -$B snapshot -i -a -o ".gstack/canary-reports/baselines/.png" -$B console --errors -$B perf -$B text +aside repl ' +const HOOK = `(() => { window.__gstackErrs = window.__gstackErrs || []; const oe = console.error; console.error = (...a) => { window.__gstackErrs.push(a.map(String).join(" ")); oe.apply(console, a); }; window.addEventListener("error", e => window.__gstackErrs.push("uncaught: " + e.message)); window.addEventListener("unhandledrejection", e => window.__gstackErrs.push("unhandledrejection: " + (e.reason && e.reason.message || e.reason))); })()`; +const pg = await openTab("about:blank"); +await pg._sendToTarget("Page.addScriptToEvaluateOnNewDocument", { source: HOOK }); +await pg.goto(""); +console.log("CONSOLE_ERRORS=" + JSON.stringify(await pg.evaluate(() => window.__gstackErrs))); +console.log("NAV=" + await pg.evaluate(() => JSON.stringify(performance.getEntriesByType("navigation")[0]))); +console.log("TEXT_START"); console.log((await pg.evaluate(() => document.body.innerText)).slice(0, 20000)); console.log("TEXT_END"); +await pg.screenshot({ path: ".jpg", type: "jpeg", quality: 60, fullPage: true }); +console.log("ASIDE_DIR=" + pwd); +await closeTab(pg); +console.log("GSTACK_STEP_OK"); +' ``` -Collect for each page: screenshot path, console error count, page load time from `perf`, and a text content snapshot. +Then copy the screenshot out of the printed session directory: `cp "/.jpg" .gstack/canary-reports/baselines/.jpg` + +Collect for each page: screenshot path, console error count (`CONSOLE_ERRORS=`), load time (`loadEventEnd` in `NAV=`), and the text snapshot between `TEXT_START` / `TEXT_END`. Save the baseline manifest to `.gstack/canary-reports/baseline.json`: @@ -80,7 +92,7 @@ Save the baseline manifest to `.gstack/canary-reports/baseline.json`: "branch": "", "pages": { "/": { - "screenshot": "baselines/home.png", + "screenshot": "baselines/home.jpg", "console_errors": 0, "load_time_ms": 450 } @@ -95,12 +107,15 @@ Then STOP and tell the user: "Baseline captured. Deploy your changes, then run ` If no `--pages` were specified, auto-discover pages to monitor: ```bash -$B goto -$B links -$B snapshot -i +aside repl ' +const pg = await openTab(""); +const links = await pg.evaluate(() => [...new Set([...document.querySelectorAll("a[href]")].map(a => a.href))].filter(h => h.startsWith(location.origin) && !/logout|signout|delete|remove|cancel|unsubscribe/i.test(h))); +for (const l of links) { const r = await fetch(l, { method: "HEAD" }).catch(e => ({ status: "ERR " + e.message })); console.log("LINK", r.status, l); } +await closeTab(pg); console.log("GSTACK_STEP_OK"); +' ``` -Extract the top 5 internal navigation links from the `links` output. Always include the homepage. Present the page list via AskUserQuestion: +Extract the top 5 internal navigation links from the `LINK` lines (same-origin only — the script already filters). Always include the homepage. Present the page list via AskUserQuestion: - **Context:** Monitoring the production site at the given URL after a deploy. - **Question:** Which pages should the canary monitor? @@ -115,29 +130,35 @@ If no `baseline.json` exists, take a quick snapshot now as a reference point. For each page to monitor: -```bash -$B goto -$B snapshot -i -a -o ".gstack/canary-reports/screenshots/pre-.png" -$B console --errors -$B perf -``` +Run the Phase 2 read script for each page with the screenshot saved as `pre-.jpg`, then `cp "/pre-.jpg" .gstack/canary-reports/screenshots/`. Record the console error count and load time for each page. These become the reference for detecting regressions during monitoring. ### Phase 5: Continuous Monitoring Loop -Monitor for the specified duration. Every 60 seconds, check each page: +Monitor for the specified duration. Every 60 seconds, check each page. Nothing persists between scripts — every check re-opens the page from its URL and captures fresh evidence: ```bash -$B goto -$B snapshot -i -a -o ".gstack/canary-reports/screenshots/-.png" -$B console --errors -$B perf +aside repl ' +const HOOK = `(() => { window.__gstackErrs = window.__gstackErrs || []; const oe = console.error; console.error = (...a) => { window.__gstackErrs.push(a.map(String).join(" ")); oe.apply(console, a); }; window.addEventListener("error", e => window.__gstackErrs.push("uncaught: " + e.message)); window.addEventListener("unhandledrejection", e => window.__gstackErrs.push("unhandledrejection: " + (e.reason && e.reason.message || e.reason))); })()`; +const pg = await openTab("about:blank"); +await pg._sendToTarget("Page.addScriptToEvaluateOnNewDocument", { source: HOOK }); +await pg.goto(""); +console.log("CONSOLE_ERRORS=" + JSON.stringify(await pg.evaluate(() => window.__gstackErrs))); +console.log("NAV=" + await pg.evaluate(() => JSON.stringify(performance.getEntriesByType("navigation")[0]))); +console.log("TEXT_START"); console.log((await pg.evaluate(() => document.body.innerText)).slice(0, 20000)); console.log("TEXT_END"); +await pg.screenshot({ path: "-.jpg", type: "jpeg", quality: 60, fullPage: true }); +console.log("ASIDE_DIR=" + pwd); +await closeTab(pg); +console.log("GSTACK_STEP_OK"); +' ``` +Then `cp "/-.jpg" .gstack/canary-reports/screenshots/`. + After each check, compare results against the baseline (or pre-deploy snapshot): -1. **Page load failure** — `goto` returns error or timeout → CRITICAL ALERT +1. **Page load failure** — the script prints a line starting with `[error` or never prints `GSTACK_STEP_OK` → CRITICAL ALERT 2. **New console errors** — errors not present in baseline → HIGH ALERT 3. **Performance regression** — load time exceeds 2x baseline → MEDIUM ALERT 4. **Broken links** — new 404s not in baseline → LOW ALERT diff --git a/devex-review/SKILL.md.tmpl b/devex-review/SKILL.md.tmpl index 081d4f35b..0340c69bd 100644 --- a/devex-review/SKILL.md.tmpl +++ b/devex-review/SKILL.md.tmpl @@ -3,8 +3,8 @@ name: devex-review preamble-tier: 3 version: 1.0.0 description: | - Live developer experience audit. Uses the browse tool to actually TEST the - developer experience: navigates docs, tries the getting started flow, times + Live developer experience audit. Actually TESTS the developer experience + in the Aside browser: navigates docs, tries the getting started flow, times TTHW, screenshots error messages, evaluates CLI help text. Produces a DX scorecard with evidence. Compares against /plan-devex-review scores if they exist (the boomerang: plan said 3 minutes, reality says 8). Use when asked to @@ -33,26 +33,31 @@ allowed-tools: {{BASE_BRANCH_DETECT}} -{{BROWSE_SETUP}} +{{ASIDE_SETUP}} + +{{BROWSE_FALLBACK}} + +{{ASIDE_COOKBOOK}} # /devex-review: Live Developer Experience Audit You are a DX engineer dogfooding a live developer product. Not reviewing a plan. Not reading about the experience. TESTING it. -Use the browse tool to navigate docs, try the getting started flow, and screenshot -what developers actually see. Use bash to try CLI commands. Measure, don't guess. +Drive the Aside browser to navigate docs, try the getting started flow, and screenshot +what developers actually see. One `aside repl` script per flow, each re-opening from the URL. Use bash to try CLI commands. Measure, don't guess. {{DX_FRAMEWORK}} ## Scope Declaration -Browse can test web-accessible surfaces: docs pages, API playgrounds, web dashboards, -signup flows, interactive tutorials, error pages. +Aside can test web-accessible surfaces: docs pages, API playgrounds, web dashboards, +signup flows, interactive tutorials, error pages — with the user's real logged-in +sessions. -Browse CANNOT test: CLI install friction, terminal output quality, local environment -setup, email verification flows, auth requiring real credentials, offline behavior, -build times, IDE integration. +Aside CANNOT test: CLI install friction, terminal output quality, local environment +setup, email verification flows, credential entry (the user signs in themselves; you +never type passwords), offline behavior, build times, IDE integration. For untestable dimensions, use bash (for CLI --help, README, CHANGELOG) or mark as INFERRED from artifacts. Never guess. State your evidence source for every score. @@ -78,7 +83,8 @@ If prior scores exist, display them. These are your baseline for the boomerang c ## Step 1: Getting Started Audit -Navigate to the docs/landing page via browse. Screenshot it. +Open the docs/landing page with the Aside read script from the cookbook (console errors, +snapshot, screenshot, text). Copy the screenshot out of the printed ASIDE_DIR and Read it. ``` GETTING STARTED AUDIT @@ -95,7 +101,7 @@ Score 0-10. Load "## Pass 1" from dx-hall-of-fame.md for calibration. Test what you can: - CLI: Run `--help` via bash. Evaluate output quality, flag design, discoverability. -- API playground: Navigate via browse if one exists. Screenshot. +- API playground: Open it in Aside if one exists. Screenshot. - Naming: Check consistency across the API surface. Score 0-10. Load "## Pass 2" from dx-hall-of-fame.md for calibration. @@ -103,7 +109,8 @@ Score 0-10. Load "## Pass 2" from dx-hall-of-fame.md for calibration. ## Step 3: Error Message Audit Trigger common error scenarios: -- Browse: Navigate to 404 pages, submit invalid forms, try unauthenticated access +- Aside: Open a 404 URL, submit an invalid form (on a non-LOCAL target that is a mutating + action — one AskUserQuestion per run first, per the browser rules), open a protected URL - CLI: Run with missing args, invalid flags, bad input Screenshot each error. Score against the Elm/Rust/Stripe three-tier model. @@ -112,7 +119,9 @@ Score 0-10. Load "## Pass 3" from dx-hall-of-fame.md for calibration. ## Step 4: Documentation Audit -Navigate the docs structure via browse: +Navigate the docs structure in Aside (search is `pg.fill(, )`, +then `pg.locator().press("Enter")` — or `pg.getByRole("searchbox").press("Enter")`, +or a click — then `snapshot`): - Check search functionality (try 3 common queries) - Verify code examples are copy-paste-complete - Check language switcher behavior @@ -141,12 +150,15 @@ Score 0-10. Evidence: INFERRED from files. Load "## Pass 6" from dx-hall-of-fame ## Step 7: Community & Ecosystem Audit -Browse: +Check the community links the docs point to. Aside stays on the docs origin (browser +rule 2): confirm the links are PRESENT in the Step 1 snapshot or with the same-origin links +script from the cookbook, and audit GitHub via `gh` in bash. Do not open Discord, Stack +Overflow, or any other third-party site — mark those INFERRED (link present, not followed): - Community links (GitHub Discussions, Discord, Stack Overflow) - GitHub issues (response time, templates, labels) - Contributing guide -Score 0-10. Evidence: TESTED where web-accessible, INFERRED otherwise. +Score 0-10. Evidence: TESTED for the docs page and GitHub, INFERRED otherwise. ## Step 8: DX Measurement Audit diff --git a/land-and-deploy/SKILL.md.tmpl b/land-and-deploy/SKILL.md.tmpl index b78eae6c1..f91d530de 100644 --- a/land-and-deploy/SKILL.md.tmpl +++ b/land-and-deploy/SKILL.md.tmpl @@ -24,7 +24,9 @@ triggers: {{THIRD_PARTY_ACTIONS}} -{{BROWSE_SETUP}} +{{ASIDE_SETUP}} + +{{BROWSE_FALLBACK}} {{BASE_BRANCH_DETECT}} @@ -308,45 +310,48 @@ Use the diff-scope classification from Step 5 to determine canary depth: | Diff Scope | Canary Depth | |------------|-------------| | SCOPE_DOCS only | Already skipped in Step 5 | -| SCOPE_CONFIG only | Smoke: `$B goto` + verify 200 status | +| SCOPE_CONFIG only | Smoke: the Aside script below; `responseStatus` in `NAV=` must be 200 | | SCOPE_BACKEND only | Console errors + perf check | | SCOPE_FRONTEND (any) | Full: console + perf + screenshot | | Mixed scopes | Full canary | -**Full canary sequence:** +**Full canary sequence** — one `aside repl` script does the whole check (console hook first, then load, then evidence): ```bash -$B goto +aside repl ' +const HOOK = `(() => { window.__gstackErrs = window.__gstackErrs || []; const oe = console.error; console.error = (...a) => { window.__gstackErrs.push(a.map(String).join(" ")); oe.apply(console, a); }; window.addEventListener("error", e => window.__gstackErrs.push("uncaught: " + e.message)); window.addEventListener("unhandledrejection", e => window.__gstackErrs.push("unhandledrejection: " + (e.reason && e.reason.message || e.reason))); })()`; +const pg = await openTab("about:blank"); +await pg._sendToTarget("Page.addScriptToEvaluateOnNewDocument", { source: HOOK }); +await pg.goto(""); +console.log("URL=" + pg.url()); +console.log("CONSOLE_ERRORS=" + JSON.stringify(await pg.evaluate(() => window.__gstackErrs))); +console.log("NAV=" + await pg.evaluate(() => JSON.stringify(performance.getEntriesByType("navigation")[0]))); +console.log("TEXT_START"); console.log((await pg.evaluate(() => document.body.innerText)).slice(0, 20000)); console.log("TEXT_END"); +await pg.screenshot({ path: "post-deploy.jpg", type: "jpeg", quality: 60, fullPage: true }); +const a = await annotatedScreenshot(pg); +await fs.writeFile(path.join(pwd, "post-deploy-annotated.png"), Buffer.from(a.base64Image, "base64")); +console.log("ASIDE_DIR=" + pwd); +await closeTab(pg); +console.log("GSTACK_STEP_OK"); +' ``` -Check that the page loaded successfully (200, not an error page). +Then copy the evidence out of the printed session directory: ```bash -$B console --errors +mkdir -p .gstack/deploy-reports && cp "/post-deploy.jpg" "/post-deploy-annotated.png" .gstack/deploy-reports/ ``` -Check for critical console errors: lines containing `Error`, `Uncaught`, `Failed to load`, `TypeError`, `ReferenceError`. Ignore warnings. +Read the output line by line: -```bash -$B perf -``` - -Check that page load time is under 10 seconds. - -```bash -$B text -``` - -Verify the page has content (not blank, not a generic error page). - -```bash -$B snapshot -i -a -o ".gstack/deploy-reports/post-deploy.png" -``` - -Take an annotated screenshot as evidence. +- `URL=` — the page loaded and stayed on the site (not a redirect to an error page). A line starting with `[error` or a missing `GSTACK_STEP_OK` means the load failed. +- `CONSOLE_ERRORS=` — check for critical errors: entries containing `Error`, `Uncaught`, `Failed to load`, `TypeError`, `ReferenceError`. Ignore warnings. +- `NAV=` — `responseStatus` is the HTTP status of the document (Chromium PerformanceNavigationTiming) — must be 200. `loadEventEnd` is the page load time. Check that it is under 10 seconds. +- `TEXT_START` / `TEXT_END` — verify the page has real content (not blank, not a generic error page). +- `post-deploy.jpg` and the annotated `post-deploy-annotated.png` are the evidence. Read the copied screenshot so the user sees it. **Health assessment:** -- Page loads successfully with 200 status → PASS +- Page loads successfully with 200 status (`responseStatus` in `NAV=`) → PASS - No critical console errors → PASS - Page has real content (not blank or error screen) → PASS - Loads in under 10 seconds → PASS diff --git a/test/skill-e2e-deploy.test.ts b/test/skill-e2e-deploy.test.ts index 77ef962d7..d9c657386 100644 --- a/test/skill-e2e-deploy.test.ts +++ b/test/skill-e2e-deploy.test.ts @@ -279,7 +279,7 @@ describeIfSelected('Canary skill E2E', ['canary-workflow'], () => { const result = await runSkillTest({ prompt: `Read canary/SKILL.md for the /canary skill instructions. -You are simulating a canary check. There is NO browse daemon available and NO production URL. +You are simulating a canary check. No browser is available on this machine (no Aside, no browse daemon) and there is NO production URL. Instead, demonstrate you understand the workflow: 1. Create the .gstack/canary-reports/ directory structure @@ -290,7 +290,7 @@ Instead, demonstrate you understand the workflow: the Phase 6 Health Report format (CANARY REPORT header, duration, pages, status, per-page results table, verdict) -Do NOT use AskUserQuestion. Do NOT run browse ($B) commands. +Do NOT use AskUserQuestion. Do NOT run aside or browse ($B) commands. Just create the directory structure and report files showing the correct schema.`, workingDirectory: canaryDir, maxTurns: 15, @@ -340,7 +340,7 @@ describeIfSelected('Benchmark skill E2E', ['benchmark-workflow'], () => { const result = await runSkillTest({ prompt: `Read benchmark/SKILL.md for the /benchmark skill instructions. -You are simulating a benchmark run. There is NO browse daemon available and NO production URL. +You are simulating a benchmark run. No browser is available on this machine (no Aside, no browse daemon) and there is NO production URL. Instead, demonstrate you understand the workflow: 1. Create the .gstack/benchmark-reports/ directory structure including baselines/ @@ -353,7 +353,7 @@ Instead, demonstrate you understand the workflow: table with Baseline/Current/Delta/Status columns, regression thresholds applied) 4. Include the Phase 7 Performance Budget section in the report -Do NOT use AskUserQuestion. Do NOT run browse ($B) commands. +Do NOT use AskUserQuestion. Do NOT run aside or browse ($B) commands. Just create the files showing the correct schema and report format.`, workingDirectory: benchDir, maxTurns: 15,