<p>There's a moment in every UI session that has bugged us for months. The agent writes the form handler, styles the page, screenshots it, even critiques its own spacing — and then turns to you and effectively says: <em>"Could you click submit and tell me if it worked?"</em> All that autonomy, and the last inch still needs a human finger.</p><p>Version 0.9.26 fixes the last inch. Plus: a way to pay half price when nobody's watching, and an extension browser that finally knows what you own.</p><h2>Why this release</h2><p>Elyra's verification story has been climbing a ladder, one release at a time:</p><ol><li><p><strong>Code</strong>: LSP auto-diagnostics — the agent sees the errors it just introduced (0.9.20)</p></li><li><p><strong>Looks</strong>: design-tools — the agent screenshots and critiques its own UI (0.9.15)</p></li><li><p><strong>Behavior</strong>: …you, clicking things, describing what happened</p></li></ol><p>That third rung was the expensive one. Visual bugs can be seen in a screenshot; <em>behavioral</em> bugs — the form that doesn't save, the button that navigates to the wrong page, the search that returns nothing — only show up when someone drives the page. Until now, that someone was you.</p><h2>1. <code>@elyracode/browser-tools</code>: Playwright, snapshot-first</h2><pre><code class="language-bash">elyra install npm:@elyracode/browser-tools
npx playwright install chromium   # once (installed Chrome works as fallback)
</code></pre><p>The agent gets one persistent headless tab per session, and six tools to drive it. But the design decision that makes it <em>work</em> is what the agent sees: not screenshots — <strong>accessibility snapshots</strong>.</p><pre><code class="language-text">&gt; Verify note capture works end to end on https://freddy.test

  browser_navigate https://freddy.test
  # Freddy
  - textbox "Note" [focused]
  - button "Save"
  - list "Recent notes": 3 items
  Console: clean

  browser_interact fill label="Note" value="Buy more RAM"
  browser_interact press label="Note" value="Enter"

  - list "Recent notes": 4 items
    - listitem: "Buy more RAM · just now"
  Console: clean

  ✓ Capture works. The note appears without a page reload.
</code></pre><p>A snapshot like that costs a <em>fraction</em> of a screenshot's tokens, and — crucially — it names every interactive element precisely. <code>role=button, name="Save"</code> is a selector the agent can act on, not a region of pixels it has to guess about. The discipline the skill teaches: <strong>snapshots for behavior, screenshots for looks.</strong> When looks matter, the pixels are one call away, including responsive checks:</p><pre><code class="language-text">browser_screenshot width=390 height=844    # how does capture feel on a phone?
</code></pre><p>And because the tab persists across tool calls, flows chain naturally — log in once, then test five pages. Console errors ride along with every result, because a visible bug is so often a JS error wearing a costume.</p><p>The last piece closes the loop with Chapter 7 energy: <code>browser_run_tests</code> runs your Playwright suite, which means this works:</p><pre><code class="language-text">/goal npx playwright test
</code></pre><p>The agent builds, the browser verifies, the loop runs until green. Nobody clicked anything.</p><h2>2. Goal economy mode: half price when nobody's watching</h2><p>Something interesting appeared in the model registry recently: <code>:batch</code> variants. Same models, same API, roughly <strong>half the price</strong> — <code>claude-sonnet-5</code> at $2/$10 becomes <code>claude-sonnet-5:batch</code> at $1/$5 — in exchange for relaxed latency. Slower answers.</p><p>Slower is a dealbreaker when you're watching the terminal. It's <em>irrelevant</em> when a <code>/goal</code> loop is grinding through a test suite at 2 AM. That asymmetry is the whole feature:</p><pre><code class="language-jsonc">// settings.json
{ "goalBatchModels": true }
</code></pre><p>Now, while a <strong>budgeted</strong> <code>/goal</code> runs, smart routing swaps the routed model for its batch sibling whenever one exists:</p><pre><code class="language-text">/goal composer test --budget 2

  ⤷ routing: balanced — default (batch variant: ~half price
    for unattended goal work)
</code></pre><p>Combined with the existing budget rationing (75% spent → capped at balanced, 90% → fast), a budgeted goal now stretches roughly twice as far. Your $2 budget behaves like $4.</p><p>Two guarantees, because economy must never become surprise:</p><ul><li><p><strong>Interactive turns are never swapped.</strong> No goal budget, no batch. Your conversation stays snappy.</p></li><li><p><strong>Pinned models are never overridden.</strong> A pin is your explicit choice; routing respects it.</p></li></ul><p><strong>And an honest confession, because it's the best part:</strong> writing the tests for this caught a real bug before it shipped. Batch variants are the <em>cheapest</em> models in their tier — so the normal routing heuristics, which prefer cheap, immediately started picking <code>:batch</code> models for ordinary interactive turns. Half price, double latency, zero consent. The fix: batch variants are now excluded from normal tier selection entirely — reachable only through the explicit economy swap or a deliberate pin. There's a regression test with a comment explaining why. Test-driven development occasionally earns its reputation.</p><h2>3. <code>/ext</code> knows what you own</h2><p>Small fix, daily value. The extension browser now shows what's already installed:</p><pre><code class="language-text">Install Extensions  (12 installed)

→ [ ]   stack-tall        TALL: Livewire 4, Flux UI, Alpine.js, Tailwind
  [ ] ✓ stack-silt        SILT: Svelte 5, Inertia.js, Laravel, Tailwind CSS
  [ ] ✓ lsp-php           PHP LSP: go-to-definition, find-references, ...
  [ ]   lsp-rust          Rust LSP: go-to-definition, find-references, ...

✓ = already installed · installed extensions activate after you run /reload.
</code></pre><p>Green checkmark, installed count in the title, dimmed names for what you have. And the matching is <em>exact</em> now — previously the check was sloppy enough that having <code>laravel-starters</code> made <code>laravel</code> look installed. Reinstalling stays possible (space still selects); the browser just stopped making you remember your own inventory.</p><h2>The thread</h2><p>Three features, one idea: <strong>trust is built at the edges.</strong> An agent you can trust with the mouse (browser-tools). An agent you can trust with the wallet (economy mode, with guardrails it wrote tests for). An agent you can trust to know what's already on the shelf (<code>/ext</code>). None of these make the model smarter — they make the <em>system</em> around it honest.</p><h2>Get it</h2><pre><code class="language-bash">npm install -g @elyracode/coding-agent@latest
elyra install npm:@elyracode/browser-tools
</code></pre><p>Then give it the keys:</p><pre><code class="language-text">/browser https://your-app.test
&gt; Now verify the signup flow actually works. Click things.
</code></pre>