The browser, readable by your agent
Install the extension, wire the MCP bridge into your coding agent, and let it read what actually happened in the tab (findings, selectors, failing responses) instead of your description of it.
What Qiksy is
A manual-QA sidekick that lives in the browser you already test in, and hands everything it sees to your coding agent.
Qiksy watches the page while you use it: console and JavaScript errors, failed requests with the server’s actual error body, accessibility and markup problems, form submits that go nowhere. It fills forms with realistic data, keeps isolated logins side by side in one window, and turns a session into a paste-ready report.
The part that matters for developers is the bridge: an MCP server that exposes that live state to Claude Code, Cursor, Cline, Windsurf, Zed or VS Code. You say “fix what Qiksy found on this page” and the agent has the findings, the selectors and the failing responses, all from the session you are already logged into.
What it does not do
By default it senses and reports; it does not click, type, submit or navigate the app under test. That boundary is deliberate: it is what makes a tool that watches other people’s applications trustworthy. There are exactly two knowing exceptions, and both start with a press of yours: a scenario you recorded yourself, which replays that flow and can follow it across pages and sites (see Scenarios), and Agent control, which hands the clicking to your own coding agent and is off until you turn it on (see Agent control).
Installing the extension
- 1Install from the Chrome Web Store.
- 2Pin it to the toolbar, because you will open the panel often.
- 3Open the site you are testing. The toolbar icon opens the popup: settings, the per-site switch, the licence and the bridge toggle. The working panel is opened from the page itself: the round Q button Qiksy injects, or Alt+Shift+Q.
Nothing to configure, no account, no key. Everything you do by hand is free: the panel, audits, form filling, isolated logins, the session recorder, scenarios, mocks and the client report, all switched on from the first minute. Pro is one thing: the MCP bridge, where your coding agent reads this tab and drives it (see Plans).
Permissions, and why each exists
| Permission | Why |
|---|---|
Access to the sites you visit (<all_urls>) | Qiksy has to work on whatever you are testing, such as a staging host nobody can enumerate in advance. The per-site switch in the popup decides where it actually runs. |
scripting | Injects the panel and reads the DOM: selectors, form fields, ARIA state. |
webRequest | A failed request is only useful with its status and response body, which is the difference between “something broke” and “the API says the token expired”. |
cookies | Isolated logins only. Keeping several sessions apart means intercepting the site’s cookies, session cookies included. This one is serious enough that the extension asks for its own explicit confirmation the first time, naming exactly what it touches. It is never silently on. |
declarativeNetRequest | Keeps each isolated login’s requests carrying its own identity instead of the browser’s shared one. |
storage, unlimitedStorage | Your settings, the licence and the current session’s findings, all kept locally. |
contextMenus, alarms | The right-click entry points and the timers behind licence checks and session upkeep. |
The panel
Everything Qiksy noticed on this page, ranked, each with the selector you need to act on it.
Audit
One pass over the live page: console errors, uncaught exceptions, failed and slow requests, accessibility problems, markup that will bite (duplicate ids, labels pointing nowhere, inputs with no name), and forms whose submit does not go anywhere.
Every finding carries a severity, a CSS selector and the detail behind it. Selectors are the whole point, because a finding you cannot locate is trivia.
Smart form fill
Qiksy reads the form (types, labels, patterns, required flags) and fills it with data that is plausible rather than random, so validation behaves the way it will for a real user. Fill one field or the whole form; custom widgets and multi-step flows are handled field by field.
Isolated logins
Open the same application as several users in one window, each tab with its own cookies, localStorage, IndexedDB and Cache Storage. Admin next to customer, seller next to buyer, with no incognito windows, no separate browser profiles and no logging out. They live in the Logins tab of the panel: + Isolated tab for a fresh empty session, or ⧉ on a saved login to open that one isolated. The first time, it asks for its own confirmation: cookies are what isolation moves, so that prompt is separate from every other switch.
Your language
The panel and the popup speak seven languages: English, Deutsch, Español, Polski, Português, Русский, Українська. On the first run Qiksy takes the one your browser is in; Language in the popup overrides that, which is the case in QA more often than elsewhere, because the machine is set up in one language and the team works in another.
Screenshot and recording
The Screenshot tab takes the page as a picture: the WHOLE page, top to bottom, however long it is. It scrolls, photographs a screenful at a time and stitches the parts into one PNG, keeping a sticky header once at the top instead of printing it down the side. Password and card fields are covered on every screenful, and the panel is out of the frame.
On top of that picture you can draw: boxes, arrows and captions in five colours, with undo. The marks are burned into the file when you save, so what you attach to the ticket is what you drew, not a layer only Qiksy can see. Save sits inside the editor.
Two of the marks do more than draw. Blur puts a pane over an address, a customer name or a token in a staging header. Drag it, stretch it by its handles, set how hard it hides, and what is under it is taken apart rather than softened: the pixels are thrown away, not averaged. It applies to the file you save, copy or put in a report, while the shot itself stays whole, which is what lets the pane be moved or removed afterwards. And a caption ties itself to what it is about: put one down beside a box and a leader line joins the two, carry it away and the line lets go, bring it back on another side and the line comes from that side instead.
Beside it, Screen or window takes one frame of whatever is on screen (the address bar, a terminal, a second window next to the page), and screen recording writes an MP4 you can attach anywhere: it plays in Slack, Jira, Teams, QuickTime and Windows’ own player with nothing installed. Two minutes maximum, pause and resume, your voice through the microphone if you want it. While a take is running the Qiksy button becomes the stop button, with the clock and the file’s size on it.
Your agent can take the full-page picture too, with qa_screenshot and full: true. Recording deliberately cannot be driven by an agent: both cameras open the browser’s own picker, and a person answers it.
Exploratory tour
A checklist generated from what the page actually contains: the flows worth trying here, not a generic list. Useful when you inherit a feature and have no idea where the soft spots are.
Report
The session as something a human can read and a ticket can hold: what was tested, what broke, the repro steps, the selectors, the failing responses. Paste-ready.
Scenarios: record a flow once, replay it later
The form you fill twenty times a week, done by hand once and pressed once thereafter.
Press Record in the Scenarios tab and fill the form the way you normally would: by hand, with ⚡ Fill, or both. Press Stop, give it a name, and it is saved for this site. Afterwards ▶ replays it: the values go back into the fields they came from, the step’s own Next is pressed, and the run stops where you would stop.
It follows the route you walked
A flow is rarely one page. A recording keeps the page each step happened on, so a replay follows the same route in the same tab: the next step of a wizard, a sign-in hand-off, a payment page on somebody else’s domain. At every page boundary it stops and waits for Continue: the page has just loaded, and typing into an application because storage said so is the one thing this product does not do.
Editing the data, not the recording
{ } opens the run’s data as a plain list of fields: change an email, a date, a company name and run it again; 🎲 refills the lot with fresh plausible values. So one recording covers “the same flow with different data”, which is most of what a regression pass actually is. An upload field can be given a file of your own, kept in the browser and used on every run.
A set, not a pile
Scenarios group by project and tags, and a whole project runs as one set with a single answer about the group. Export writes them to a file a colleague can import.
What is deliberately NOT recorded
Passwords and card numbers are recorded as fields, never as values, because a scenario is a document that can be exported, mailed and put in a repository, and a saved password would ride along. The replay stops at such a field and asks you to type it.
Where it sits
The Scenarios tab of the panel, and it is free: recording a flow and replaying it is your own hands on your own browser. Your agent can read, run and write them through the bridge as well, and the bridge is the part that needs Pro.
The MCP bridge
Switch the bridge on in the extension, paste one block into your agent’s config. No clone, no local path, no version to pin.
agent ⟷ (MCP over stdio) ⟷ qiksy-mcp ⟷ (WebSocket @ 127.0.0.1) ⟷ extensionThe extension is the WebSocket client, because a service worker cannot listen on a socket, and the bridge is the server, bound to loopback only. Nothing is reachable from outside your machine.
The short way: one command
Run this in the project you work in. It picks a free port, mints the token, writes the qiksy server into every agent config that is actually on the machine (Claude Code, Cursor, VS Code, Windsurf, Claude Desktop) and leaves the token on your clipboard. Editing a JSON file by hand is the step people stop at, so this does it for you.
npx qiksy-mcp init- 1Run it in your project.
initalso writes the agent guide, which is what stops your assistant reaching for another browser driver;npx qiksy-mcp connectdoes the wiring alone. - 2Open the toolbar popup → Agent bridge · MCP → On, accept the one-time consent, and paste the token, which is already on your clipboard.
- 3Restart your agent and ask it to call
qa_status. It should answer with the page you are looking at.
cmd /c npx …, the form that actually starts there.Or by hand: switch the bridge on in the extension
The extension side is off until you turn it on, and the two halves authenticate to each other with a shared token. Do this before touching your agent’s config, or the bridge will sit silent with nothing to explain why.
- 1Open the toolbar popup → Agent bridge · MCP and switch it On.
- 2Accept the one-time consent prompt. Until you do, the bridge reports
blockedand refuses to connect. - 3Press Generate to mint a token, then 📋 Copy agent config, which gives you the exact block for your editor, token and port already filled in.
off, connecting, waiting, rejected, lapsed, blocked, replaced.Two ways in, pick one
Either hand the whole job to your assistant, or place the config yourself. Both end in the same place; neither needs the other.
A. Let Claude do it
Press 📋 Copy agent config (JSON) in the popup, then paste this into Claude (Claude Code, Cursor, whatever you use) together with the copied block. It knows where its own config lives.
Set up the Qiksy browser bridge for me. Below is the MCP server config from the
Qiksy extension popup. Do this, and tell me if a step needs me:
1. Work out which MCP client I am using and where ITS config file is (Claude Code:
.mcp.json in the project or "claude mcp add"; Cursor: ~/.cursor/mcp.json;
Windsurf: ~/.codeium/windsurf/mcp_config.json; VS Code: .vscode/mcp.json;
Claude Desktop: the claude_desktop_config.json for this OS; Zed: settings.json
under "context_servers").
2. Merge the "qiksy" server into it WITHOUT dropping servers that are already there.
Keep the token exactly as given: it must match the extension.
3. Tell me to restart / reload the client, then verify by calling qa_status and show
me the result. If it says the extension is not connected, tell me to switch the
bridge On in the Qiksy popup. Do not try another browser tool, there is none.
<paste the JSON from the popup here>B. Place it yourself
The config below is the same for every client; only the file it goes in differs. Merge it in; do not overwrite servers you already have.
| Client | Where the file lives |
|---|---|
| Claude Code | Run the command below, or edit .mcp.json in the project root (shared with the repo) / ~/.claude.json (just you). |
| Cursor | ~/.cursor/mcp.json for every project, or .cursor/mcp.json inside one. |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| VS Code | .vscode/mcp.json in the workspace. |
| Claude Desktop | macOS: ~/Library/Application Support/Claude/claude_desktop_config.json · Windows: %APPDATA%\Claude\claude_desktop_config.json |
| Zed | ~/.config/zed/settings.json, under context_servers. |
qa_status: a URL and three counts means it worked.Claude Code
claude mcp add --transport stdio qiksy \
--env QIKSY_MCP_TOKEN=YOUR_TOKEN \
-- npx qiksy-mcp --port 7333`) at each line end.Claude Desktop, Cursor, Windsurf
{ "mcpServers": { "qiksy": {
"command": "npx",
"args": ["-y", "qiksy-mcp", "--port", "7333"],
"env": { "QIKSY_MCP_TOKEN": "YOUR_TOKEN" }
} } }Zed
{ "context_servers": { "qiksy": {
"source": "custom",
"command": "npx",
"args": ["-y", "qiksy-mcp", "--port", "7333"],
"env": { "QIKSY_MCP_TOKEN": "YOUR_TOKEN" }
} } }VS Code
{ "servers": { "qiksy": {
"type": "stdio",
"command": "npx",
"args": ["-y", "qiksy-mcp", "--port", "7333"],
"env": { "QIKSY_MCP_TOKEN": "YOUR_TOKEN" }
} } }QIKSY_MCP_TOKEN the bridge closes every connection with auth failed before any data moves, and the extension will not dial out either, so you get silence rather than an error naming the cause. Use the same value the popup generated, and the same port on both sides.The package is qiksy-mcp on npm, versioned in lockstep with the extension. npx always fetches the current build, so there is no version to keep in step by hand. Node 18 or newer.
qa_export included. Do not design a free-tier integration around the read-only tools.Several agent windows, one browser
Open a second editor window on the same project, or a third, and they all reach the same browser through the same toggle. There is nothing to configure: no second port, no second token, no switching in the popup.
Each window starts its own copy of qiksy-mcp, but only one process can own the loopback port. The first to take it holds the extension’s socket; the others notice, dial it with the same token and relay through it. Close that window and the rest take the port over on their own, and the extension reconnects without being told.
tabId from qa_tabs, or let each open its own isolated login with qa_open_isolated. Two agents clicking in the same active tab will fight over it.Your agent is briefed for you
You do not have to explain Qiksy to your agent, or paste rules into a project file. The bridge hands it a short operating manual the moment it connects: which tools read and which act on the page, that a snapshot is an accessibility tree rather than a screenshot, how to address a specific tab, and which two failures are yours to fix rather than its to retry. Nothing to configure: it arrives with the connection.
Checking it works
Open a page with the extension active, then ask the agent to call qa_status. You should get the URL and the current error, warning and form counts. If it reports no connection, see Troubleshooting.
Brief your agent, once
Paste one prompt. Your assistant sets the bridge up and writes a short guide into the project, so every session after this one already knows what Qiksy can and cannot do.
An assistant that has never met Qiksy will guess: it will look for screenshots that do not exist, try to run JavaScript that is refused by design, and give up on "not connected" instead of telling you to flip a switch. One paste fixes that for the whole project: the guide lands in your repo and is read on every future session.
The prompt
Set up the Qiksy browser bridge for this project, then leave a guide behind for future sessions.
1. Check whether the Qiksy MCP server is already configured for you. If it is not, add it:
claude mcp add --transport stdio qiksy --env QIKSY_MCP_TOKEN=<TOKEN> -- npx qiksy-mcp --port 7333
I will give you the token. It comes from the Qiksy extension popup (Agent bridge · MCP → On → Generate),
or from pressing "Connect this browser" in a Qiksy web app. Ask me for it; never invent one.
2. Verify the connection by calling qa_status. If it answers "Qiksy extension is not connected",
tell me to switch the bridge on in the extension popup and stop. Do not substitute another
browser tool, there is none.
3. Write .claude/qiksy-bridge.md in this project (create the folder if needed) describing, for
whoever works here next:
- what the bridge is: it drives the browser I already have open, in the tab I am looking at,
and never launches, focuses or steals a window;
- the tools you actually have, grouped: read-only (qa_tabs, qa_status, qa_snapshot, qa_read,
qa_styles, qa_storage, qa_findings, qa_export, qa_screenshot, qa_wait_for), Qiksy's own surfaces
(qa_open_panel, qa_run_audit, qa_tour, qa_report, qa_spotlight), and agent control which needs
Qiksy Pro plus a one-time consent
(qa_click, qa_hover, which holds the pointer on something and reads what appeared, qa_type, qa_type_many, qa_press, qa_upload, qa_wait_ready, qa_navigate, qa_reload,
qa_open_isolated, qa_close_tab, qa_mock, which answers a request from a rule instead of the
server for the failure paths a live backend will not produce, and qa_storage's write ops);
- how to WAIT: qa_wait_for takes one condition (appears / disappears / enabled / text / textGone /
countOf / announced / urlContains) and returns the moment it comes true, or says what it saw at
the deadline. Use it instead of polling qa_snapshot, which renumbers every ref each time;
- how to target things: refs from a fresh qa_snapshot beat CSS selectors, and refs go stale after
a re-render, so snapshot again after qa_wait_ready;
- what it cannot do, so nobody wastes a turn trying: no arbitrary JavaScript, because every verb is a
closed vocabulary, you name an element and never a program. SCREENSHOTS ARE AVAILABLE now
(qa_screenshot, page or one element, with the panel hidden and password and card fields covered),
with one real condition: the browser photographs the window's ACTIVE tab, so a background tab
cannot be photographed and Qiksy refuses rather than handing over a page you did not ask about.
Styles, element geometry and web storage are available too
(qa_styles, qa_snapshot geometry:true, qa_storage); they were once listed here as impossible,
which was a wrong inference from the no-eval rule. Still out of reach: anything inside an iframe
(payment forms included), captchas, and qa_navigate stays on the site under test;
- that a click ANSWERS with what changed once the page settles, so an action does not need a
follow-up snapshot to find out whether it did anything;
- that qa_report takes a `path` and should always be given one, because the report is a self-contained
HTML document with embedded screenshots and belongs in a file, not in the conversation;
- one line on what to do when it says it is not connected: tell the human, do not switch tools.
4. Show me the file when it is written, and keep it updated if the bridge gains or loses tools.mcp/AGENT-GUIDE.md from the package straight into your project: it is the same guide, already written.Why a file in the repo, not just a chat message
A chat message lasts one session. Project files are read at the start of every session by Claude Code, Cursor and the rest, so the next person to open the repo gets an assistant that already knows a snapshot is a tree, that acting needs consent, and that "not connected" is a switch in the popup rather than a dead end.
The five switches, and which ones are per-tab
Everything Qiksy does is off until you turn it on, and the switches answer different questions. Two of them are global, one is per site, one is PER TAB, and the per-tab one is the step people miss.
They all live in the toolbar popup, top to bottom, in the order you actually turn them on. Nothing here is a preference: each one widens what Qiksy is allowed to do, which is why none of them is on by default.
| Switch | Scope | What it decides |
|---|---|---|
| Show Qiksy | Everywhere | Whether you SEE Qiksy: the round Q button and the panel. It hides them and nothing else: the page is still watched and the agent bridge still answers, on every tab. Since 1.8.1. |
| On this site | Per site | Whether Qiksy runs on this origin. This is the real off switch, and the one that makes a disabled Qiksy observe nothing. Changing it reloads the tab. Use it to keep Qiksy out of a site you are not testing. |
| Agent drives this tab | Per tab | Whether this tab is in the agent’s session. This is the one to turn on for every tab you want driven; see below. |
| Agent control | Once, globally | Whether the agent may ACT at all: click, type, submit, write storage, attach files. Shows the full disclosure and asks you to confirm. Turning it off drops the consent, so turning it back on asks again. |
| Agent bridge · MCP | Once, globally | The loopback connection to the program you run (your coding agent). Generates the token you paste into its config. |
Working with Qiksy out of sight
Turn Show Qiksy off and the round Q button and the panel disappear from every page, while findings keep being collected, the session keeps recording, and your agent keeps reading and driving through the bridge exactly as before. Nothing to re-enable per tab, and a tab opened afterwards is in the same state: connected, invisible. It is the setup for demos, screenshots, screen shares, and any page whose own bottom-right corner you need free. Flip it back on and the button is there again, no reload.
Why “Agent drives this tab” is per tab
The agent’s session is a SET of tabs you opt into, not “whatever tab is in front”. That is deliberate, and it buys three things at once: the agent keeps a page while you look at something else, work continues in a background tab, and several tabs can be driven at once: a form here, the mail with the sign-in code there.
It is also the stricter of the two designs. Before, merely focusing a tab put it in reach; now a tab is in reach only if you deliberately put it there. “Qiksy acts only where you enabled it” stops being a description of a default and becomes a gesture with a record.
- Turn it on for each tab you want the agent to work in: the popup switch, or right-click the page → “Qiksy · test this tab”.
- A tab opened BY an attached tab joins automatically. That is how a sign-in hand-off to Microsoft or Google stays in the flow: the provider’s tab appears on its own, mid-run, and would otherwise be outside the session at exactly the moment it matters.
- Attach nothing at all and Qiksy falls back to the tab you are on, so a quick one-tab job needs no setup.
- Ask the agent
qa_tabsat any time to see the session as it sees it.
The order that works
- 1Install and pin. Show Qiksy is already on.
- 2Open the app you are testing, and leave On this site on.
- 3Turn on Agent bridge · MCP, press Generate, copy the config block, paste it into your agent, restart it. The popup line goes green when the agent connects.
- 4Turn on Agent control and read the disclosure: this is the switch that lets a program act under your login.
- 5Turn on Agent drives this tab on each tab you want driven. Ask the agent to run
qa_tabsand confirm it sees them.
One file you can attach to the ticket
Qiksy records the session as you test it, then hands you a single self-contained HTML document: screenshots embedded, nothing to unzip, nothing that phones home. It does not have to be about defects: the same verbs write a review, a handover note or a description of how something behaves.
The hard part of manual QA is rarely the finding. It is the twenty minutes afterwards spent turning “it broke when I saved” into something a developer can act on: which build, which steps, what the server actually answered, a screenshot that shows the state rather than the aftermath. Qiksy is watching all of that anyway, so the report is assembly, not authorship.
What is in it
- Coverage: the pages visited, and what was exercised on each.
- Every finding with its selector, grouped by severity: console and JS errors, failed requests with the server's own error body, accessibility and markup defects, and speed & stability measurements.
- The steps, in order, with a screenshot per step. Password and card fields are masked before capture.
- Environment: browser, viewport, the build under test.
Not only a bug report
A document assembled from blocks takes points as well as findings: a thing that works, carrying a green mark and no “Expected” line, because something that behaves has nothing to be measured against. There are also sections to group them under your own headings. Its kind says what it is: issues, verified or notes, and the title is yours. That is what makes it usable by somebody who is not filing a defect: a designer describing what a screen does, a developer handing work over, an analyst recording how a flow behaves today.
And the extension keeps what it builds. A saved report is listed in the panel’s Shots tab under Reports, with open, save and delete beside it, and your agent can read it back by name, so a document written an hour ago is findable without hunting through Downloads.
Make it answer the ticket
Give the report a spec (the story and your verdict on each acceptance criterion) and it leads with those, rendered next to the evidence Qiksy gathered itself. You supply the judgement, because you are the one who read the requirement; Qiksy supplies the proof. That is the difference between a log and a deliverable.
path: the file routinely runs to hundreds of kilobytes because the screenshots are inside it, and the agent gets back a receipt rather than the bytes. From the panel it downloads, and opens in a tab from the download bar.Tool reference
Sixty-six tools. Every one accepts an optional tabId (from qa_tabs); without it they act on the tabs you attached to the agent session, or the active tab if you attached none.
Reading the page
| Tool | What it returns |
|---|---|
qa_tabs | Every tab Qiksy can read, including which are isolated logins (isolatedSession = the login name). The tabId you pass to everything else comes from here. |
qa_status | URL, error / warning / form counts, and the isolated-login name if any. |
qa_read | The RENDERED text of the page or one element. Use it whenever the answer is longer than a label: qa_snapshot caps names at 80 characters, so a one-time code in a mail tab or a full error paragraph is invisible to it. |
qa_styles | Computed styles, the element's box in CSS pixels, and the CSS custom properties in scope. “color: rgb(37,99,235)” says what is drawn; “--accent: #2563EB” says which token it came from, and only the second one is a bug report. This is how a build gets checked against a design without a screenshot. |
qa_storage | The app's localStorage / sessionStorage. Reading is free; op: set | remove | clear needs Agent control, because changing a stored token changes what the app does. |
qa_screenshot | A real picture of the page, or of one element (ref / selector). The Qiksy panel is hidden and password and card fields are covered before the frame is taken, so a shot pasted into a ticket carries no secret; path writes the PNG to a file instead of spending context on base64, and a file is written at FULL resolution, because the downscale exists to protect the conversation and a file is not the conversation. On a long page that is the difference between a readable picture and a blurred one. One condition, and it is the browser's, not ours: it photographs the window's active tab, so a background tab cannot be photographed: Qiksy refuses rather than quietly handing you a different page. The same refusal covers a buried WINDOW: with another window in front, Chrome hands back the last frame it drew: right size, right page, the content as it WAS, with no error of any kind. Measured: a page was changed and two shots eighteen seconds apart came back byte-identical. Qiksy now says so and asks for the window to be brought forward, because a screenshot that is quietly out of date is worse than no screenshot at all. |
qa_wait_for | Wait for one named condition and return the moment it is true: appears / disappears / enabled / text / textGone / countOf / announced / urlContains. Use it for anything whose answer lands later than a click settles: a slow save, a confirmation, a button that ungreys when validation finishes. On timeout it reports what it saw (how many matched, the nearest names on the page), so a wait that failed on a typo says so. Waiting is reading: no Pro, no consent. |
qa_findings | Findings with selectors and detail. severity = all | error | warning. |
qa_snapshot | Whole-page accessibility snapshot with stable ref handles: the map an agent needs before acting. Narrow it on a big app: fields_only for the form controls, roles, match by name, within for one dialog or wizard step, format: "tree" for half the bytes. Filtering never invalidates a ref. |
qa_collect | A TABLE instead of a page. Name which nodes (a CSS selector, a role, a fragment of the name, scoped by within) and which properties to take: text, value, name, role, tag, selector, href, state, geometry, plus attr:<name> and style:<property>. “Every row with its price”, “every input with what it holds” is one call here and a dozen with a snapshot plus a read per element. value is what a control HOLDS, which a snapshot cannot tell you. A property nobody can supply is named back rather than dropped, so a missing column is never mistaken for a missing feature. |
qa_annotate | Mark the page by NAMING elements. A mark points at a control, not at a coordinate, so the same instruction draws the correct picture after a re-render or at another width, which is the whole difference between describing a page and decorating an image. Shapes: a box, a numbered pin, an arrow or a line between two elements, and a blur pane that makes an element unreadable: an address, a customer's name, a token in a staging header. A label is a callout, not a sticker: it is placed in free space beside its element, clear of every other mark, and joined to the outline by a leader line ending in a dot, so “outline the payment block and say what is wrong with it” comes back as one readable statement instead of words stamped over the thing they are about. Pass fromFindings and the page's own findings draw themselves, coloured by severity. A mark whose element is not there is reported, never silently dropped, and a blur hides the data in the PICTURE, never on the page, so the next capture shows it again. |
qa_focus_proof | Is focus visible on this control? Two measurements instead of an argument: which of the properties that can draw a ring changed, and what fraction of the PIXELS differ between the unfocused and focused frames. “Nothing visible changes, 0% of the pixels differ” is a sentence nobody disputes, and a screenshot of a missing ring looks exactly like a screenshot of a faint one. The crop is widened by however far the ring could reach, and a ring that IS there but faint is reported as present: that is a contrast question, and this verb refuses to answer it. |
qa_wiring | Which error message belongs to which field, and which belongs to nothing. A form with three red messages under three fields looks correct to everyone who can see it; assistive technology reads the attachment, not the proximity. Reports the real associations, the messages attached to nothing, and the fields marked invalid with no reason given. Deliberately under-reports: a message inside its label and an error summary that links to its fields are both correct by another route. |
qa_diff | What changed on this page, by control rather than by pixel. Take the state, do the thing, compare: what appeared, what disappeared, what moved, resized, changed state or changed a style. Text is not compared on purpose, because a name or a total differs between two healthy runs, and reporting those is the noise that gets visual diffing switched off everywhere else. When the two states cannot be matched at all, it says so instead of reporting hundreds of false appearances. |
qa_failure_gallery | How this screen looks when everything goes wrong: empty, 500 with the server's own words, a held request, 403, a dropped connection, all in one self-contained HTML sheet with every picture inside it. Every other capture tool can only photograph a failure that happens; this one causes it, because the response mocks and the camera are the same product. The rules are always removed afterwards, including when something throws. |
qa_shot_list · qa_shot_read | The shelf: every picture the panel is holding, by name, and what is drawn on each. qa_shot_read returns the MARKS as data (kind, position, colour, the caption somebody typed) and not the pixels. That is what makes one picture workable by two pairs of hands: the tester marks the broken total by hand and the assistant reads the words beside the arrow, instead of either of them describing a picture to the other. |
qa_shot_mark · qa_shot_crop · qa_shot_drop | Draw on a shelved picture, or keep only the part that matters. Both are stored beside the clean image rather than applied to it, so the tester can move the marks afterwards and pull the crop back out: “a bit more on the left” never means “take the screenshot again”. Coordinates are image pixels, the same ones qa_screenshot maps. Shapes: box, arrow, text and blur. A caption takes an optional to (the point it is ABOUT) and a leader line is then drawn from the plate to that point, in the editor as well as in the exported file; give a box a caption pointing at its middle and you get the callout a person would draw by hand, which the tester can then drag anywhere with the line following. qa_shot_drop forgets one picture when the shelf gets noisy. |
qa_reports · qa_report_get · qa_report_drop | The reports the extension is holding, in the same shape as the shot shelf. A report built with save gets a name, appears in the panel under Reports with open, save and delete beside it, and can be read back here by that name. It is the answer to “where did that document go an hour later”: a file that only ever left through a download, or through a path the agent chose, was unfindable by the person it was written for. |
qa_report_build | A self-contained HTML report, assembled by NAMING BLOCKS (title, summary, section, finding, point, shot, steps, facts, note) with plain text fields. A point is a thing that WORKS: it carries a verdict (good · info · warn · bad) and no “Expected” line, because something that behaves has nothing to be measured against; a finding still carries one. kind says what the document is (issues, verified or notes) and save keeps it inside the extension under a name you choose. No template is accepted and none can be: a verb here names an element or a value from a closed list, never a program. Print it for a PDF. It refuses when there are no blocks, when a block names a picture that is not on the shelf, or when the whole document contains not one picture: a page of assertions about an application showing none of it is the artefact this replaces. The conditions of the run are added automatically and cannot be left out by the caller. |
qa_gap | What the bridge genuinely could not do, in one sentence, written to a file you read. The sanctioned way out of a dead end; the unsanctioned one is a quiet switch to another browser driver, which has none of your logins and ends the run's meaning. |
qa_watch | Watches for a few seconds, which is the one question the other verbs cannot answer. Everything else reports NOW, so a toast that appears and leaves, a request fired by a timer, a late row or a screen-reader announcement are invisible to a snapshot taken before or after them, and polling misses the gaps between looks. Watch mutations, network, console or announcements; do the thing; read the buffer. Every watch expires by itself: ttlMs is capped at two minutes and there is no “until stopped”, because a watch left running is silent and the readings taken after it are the ones that go wrong. It observes and never acts, so it needs no Agent control. |
qa_export | The full qa-export/v1 bundle: findings, forms, network, repro steps. |
qa_tour | The exploratory checklist for this page. |
qa_report | Generates the session report; spec shapes what goes in. |
Asking a reply for the fields you want
The big reads take select (keep only these field paths) and omit (drop these): qa_snapshot { select: ["nodes[].name", "nodes[].ref"] }. Dots walk into objects and a path through a list applies to every entry, keeping it a list, so anything you already knew about the reply stays true. A path that matches nothing is reported back together with the field names that are there, so a mistyped field costs one call instead of a bigger request. Paths only: no expressions, no code: the caller names a field exactly as every other verb here names an element.
When the bridge genuinely cannot
qa_gap takes one sentence saying what was needed and writes it to ~/.qiksy/gaps.jsonl, a file you read. It exists because the usual way out of a dead end is worse: an agent that hits a wall quietly switches to another browser driver, and since that browser has none of your logins, the run stops being the test you asked for. A gap is not a bug report about the page; it is a request for a verb, and it is answered by shipping one with a name and a stand rather than by letting an agent rewrite the tool at runtime.
Driving Qiksy’s own surfaces
| Tool | What it does |
|---|---|
qa_open_panel | Opens or closes the panel, optionally on a given tab of it. |
qa_done | Say that the run has FINISHED: once, at the end, and never as a progress mark. The mark on each driven tab then pulses slowly and a sound plays, so somebody in the next room knows the agent is waiting for them rather than still working. The pulse stops the moment that tab is looked at, and never starts on a tab already in front. Three refusals keep it honest: nothing sounds if no work was done, nothing sounds twice within half a minute, and turning the sound off in the popup leaves the pulse. It is the one verb whose whole purpose is the human, so it is worth calling even when the run went perfectly. Any MCP client can call it; for those that have a finish hook, npx qiksy-mcp done does the same thing without the model having to remember. |
qa_run_audit | Re-runs the audit on the page as it is right now. |
qa_axe | Runs the bundled axe-core engine (the same rule set that powers Lighthouse) on the live DOM: hundreds of WCAG A/AA checks on top of the audit's own, narrowed to WCAG-tagged rules so every violation maps to a criterion, each with impact, tags, a help link and the offending selectors. Entirely local: the engine ships inside the extension and nothing leaves the page. |
qa_spotlight | Highlights an element in the page by selector or ref: the fastest way for an agent to show you what it means. |
qa_focus_tab | Brings a tab in front of you (activates it and raises its window) so a walkthrough happens where you are actually looking. The one verb that deliberately takes your attention, so it is scoped to the app under test and its isolated logins, and it is meant for “show me”, not for the agent's own convenience. Pair it with qa_spotlight: the label is what turns a run of clicks into “step 3 of 6”. |
qa_open_tab | Opens an address the driving verbs will not navigate to, as a new tab beside the ones you already have. The boundary is untouched: the agent still cannot steer one of your tabs onto another site, and a new tab costs you nothing if it was wrong about needing one. It lands in the right browser by construction rather than by care: your machine has several Chrome profiles, the extension lives in exactly one, and naming a profile does not work at all (a running Chrome ignores the flag, reports success, and puts the tab where nobody can see it), so it anchors on a tab the bridge already sees and inserts the new one into that window. If the new tab is not in the agent's session, the answer says so and names the switch instead of leaving the agent measuring a tab it cannot see. macOS for now. |
qa_frames | A page is often several documents, and the interesting one is usually not the outer one. A payment form, a sign-in step, a chat widget, a Stripe or 3-D Secure field: each lives in a frame of its own, and the page around it cannot read inside — that is the browser's same-origin rule, and no amount of JavaScript gets past it. Qiksy goes into the frame instead and answers about that document. This lists what your page is really made of: the frame id, the address, the title and how many controls each one holds, with the page itself first. Pass that id to qa_read or qa_styles and you are reading inside. Reach for it the moment a control you can see on screen is missing from the page tree — nine times out of ten it is not missing, it is in a frame nobody named. Nothing is loaded into a frame until you ask about one, so an ordinary page costs exactly what it did before, and no new permission is involved. |
qa_width_law | Answers “the mockup caps the container at 1400 and ours is wider” without measuring anything. A measured width is a fact about the window it was taken in (true on your laptop, false one drag of the mouse later) so this reads what the CSS declared instead: how many width ceilings exist on the layout spine, which element sets the tightest one, and what content band follows at 1440, 1920 and 2560. A declaration does not move when the window does, so it is the thing that can actually be compared with a design. Give it figmaWidth and it prints both declarations side by side plus the consequence, and it never says which of the two is wrong: that is your call, not a checker's. Two refusals are on purpose: a value that moves with the window (clamp(), vw, a percentage) declares no single law, so it is listed as not measured rather than resolved into a number that would be right on one screen and wrong on the next; and narrow boxes are not judged at all, because a sidebar is narrow by design. The report opens with what it did not look at, so a clean page never reads the same as an unexamined one. |
qa_wait_ready | Waits for the page to settle before the next step. |
Agent control (Pro, opt-in)
| Tool | What it does |
|---|---|
qa_click | Clicks an element by selector or ref. |
qa_hover | Puts the pointer on an element and leaves it there, then reports what that revealed: a menu that opens on hover, a tooltip, the actions a row only shows under the pointer. qa_click cannot answer any of that, because it presses a millisecond after it hovers, and the press closes what the hover opened. It holds for half a second before measuring, because a tooltip that waits does not exist to an instant measurement, and off: true takes the pointer away again. One field to read carefully: cssHover says whether the browser’s own :hover matched: dispatched events run every menu and tooltip library, but they do not move the real cursor, so an effect written purely in CSS will not appear. |
qa_type | Sets a field to a value, any kind of field. It is the extension’s own fill engine, not a keystroke emulator, so the same call handles a text input, a native <select>, a checkbox, a slider, rich text, and the controls that look like they need clicking: Radix/shadcn, MUI, Ant and react-select dropdowns, typeahead comboboxes, popup date and time pickers. It opens the popover, picks the option matching your value, and verifies the trigger changed. |
qa_type_many | Fills a whole form (or one step of a wizard) in one call: a list of {ref | selector, value}. Every widget kind above goes in the same batch, which is the normal use and not an edge case. Prefer ref: a form built from a repeated component gives several inputs the identical selector, and a batch of selectors would pour every value into the first match. |
qa_fill_json | Fills a form from a plain { "Field label": "value" } object, the same JSON the panel’s { } editor and ⚡Fill button use. It takes its own snapshot, resolves the labels and hands everything to the batch filler, so an agent that already knows the data spends one call and never thinks in refs. Keys that match nothing come back with the page’s real field names. dry_run checks a dataset against a page without filling it. |
qa_pick_date | Sets an exact date in one call, whatever component library owns the calendar. It types the date first, and only if that does not stick does it open the picker and steer it (MUI’s year view, Ant’s header year, react-datepicker’s dropdowns, bare arrows otherwise) and then clicks the day. Needed because some date fields cannot be written to at all: MUI X’s is three sections the component rebuilds from its own state. |
qa_pick_path | One call for a value spread across levels: a cascader (country → region → city), a drill-down, a chain of submenus. Each level is clicked on a fresh snapshot, because opening a level is what re-renders the popup. |
qa_pick_time | A time on a column-based picker (Ant’s TimePicker). It exists because those cells are bare list items with no role at all, invisible to the snapshot, and the ordinary fill path misreads the control as a date field, types an ISO date into it and leaves it empty while reporting success. This reads each column’s text, finds which entry is your value, clicks that child, then presses the panel’s own OK, which is not optional: without it the value stays provisional. |
qa_pick_range | Both ends of a date range that live in ONE control (Ant’s RangePicker). Not two calls to qa_pick_date: the control walks from the start panel to the end panel by itself, and approaching it cold a second time restarts the range on the start input. When the panel closes after the start it says so, plainly, and then the end is its own field. |
qa_pick_tags | A whole list of tags on a control that CREATES its values: Ant’s Select in tags mode, and every chips input built the same way. Per tag it types, verifies the text is still in the field, then presses the key, on a handle re-resolved each time. That verification is the feature: when a tag commits, the component clears its own search input, a re-render can wipe a value written a moment earlier, and the key then re-submits the PREVIOUS text, which in tags mode toggles that tag back off, so the form quietly loses the tag you added first. |
qa_dialogs | What the page’s native dialogs do: “Leave site?”, confirm, alert, prompt. Nothing can click those: they are browser chrome, outside the document, unreachable from any script. So this answers them instead, per tab, off by default, and every suppressed dialog is reported as a finding carrying the question the page asked and the answer given. Without that line the feature would manufacture exactly the kind of wrong bug report it exists to prevent. Listing is free; setting one takes Agent control, because an auto-accepted confirm() can be the one in front of Delete. |
qa_probe | Answers what kind of widget is this, and how do I drive it, for the one control that refused a value. It judges by shape first (role, tag, name, state, neighbours), which settles a sectioned date field, a file input, a switch or a slider without touching the page; when the shape says nothing (a widget hand-rolled from <div>s has no role, no name and no state) it clicks once, reads what the page did, and presses Escape to put it back. The verdict that saves the most time is “it opened and the accessibility tree gained nothing”: that means role-less markup in a portal, and the parts have to be addressed by CSS selector. |
qa_upload | Attaches a file to an upload field: a real file by path, bytes as base64, or a hostile fixture built in the page (empty, 12MB oversize, wrong-type, corrupt, unicode name, unsanitised SVG…). files takes several at once, one round trip for the step. |
qa_press | Sends a key, optionally to a specific element. The reply carries focus, the element that ACTUALLY holds focus after the key (name, role, selector, box), and announced: what the page's live regions spoke, or null if they stayed silent. |
qa_focus_walk | The keyboard route as one call: presses Tab repeatedly, records where focus really lands, and compares the trail against what the accessibility tree claims is interactive. Reports the controls a keyboard user can never reach (the classic div-button), suspected focus traps, and stops where focus jumps against the layout: the 2.4.3 candidates. |
qa_navigate | Navigates the tab, within the site under test only. A hop to another domain (an external SSO, a payment provider, a docs link) is refused as out-of-scope. |
qa_open_isolated | Opens a URL as a fresh isolated login: same-site only, and behind a separate consent of its own (see Agent control). |
qa_close_tab | Closes a tab it opened. |
qa_reload | Reloads the tab as it stands (no URL to pass) and waits until the page is loaded and Qiksy is live in the new document. The browser performs it, not the page's own script, so it works even when that script is already dead, which is exactly the case a reload is for. bypassCache gives the hard reload. |
qa_mock | Answers one of the page’s requests from a rule instead of the server: a 500, an empty list, a payload missing a field, a 30-second delay, a dropped connection. This is how the failure branches get tested at all, because a working backend will not produce them on demand. url is a substring or /regex/; times: 1 mocks the first call and lets the retry through, which is the only way to prove a retry retries. A rule belongs to one tab, survives its reloads, and is gone when it closes. It covers the page’s own fetch/XHR in the top frame, not the document, subresources, WebSocket or iframes. Every mocked answer is marked (mocked) in the session, the HAR and the report, so an invented response is never reported as the server’s. op: "list" is free; setting one needs Agent control. |
selector or a ref from qa_snapshot. Prefer ref: it survives the re-render that invalidates a hand-written selector, which is the usual reason a scripted step fails on the second run.Recipes · the second run is the cheap one
A long form is expensive to work out once and should not be worked out twice. These three keep the flow on your machine as a JSON file, written in the field names a person would read, because refs die with their snapshot and names do not.
| Tool | What it does |
|---|---|
qa_recipe_save | Writes what the agent just filled, uploaded and clicked to ~/.qiksy/recipes/<name>.json, folded into steps (a click after a group of fills is what ends a step). Local only: no browser, no Pro. |
qa_recipe_list | The saved flows for this host: steps, field count, the first step’s field names. One call at the start of a form-heavy task. |
qa_recipe_apply | Replays a recipe against the page as it is now: one call per step, matching by name, with values to swap the data and dry_run to check the recipe still fits before touching anything. Needs Agent control, since it fills and clicks. |
qa_recipe_save returns. Nothing about it is uploaded, and QIKSY_RECIPES_DIR moves the folder if you would rather keep them with the project.Agent control
Letting the agent act inside the session you are already logged into, off by default and deliberately hard to enable by accident.
Everything above senses. Agent control acts: click, type, fill a form, press a key, navigate, open an isolated login. It exists because the valuable session is the one you are already authenticated in: the state a cold automated browser cannot reach without you handing it credentials.
Scenarios · the ones the tester recorded
Recipes above are the agent’s own journal, folded into steps. Scenarios are the person’s: recorded by hand in the panel, kept per project with tags. The agent can see them, which is the whole point, because otherwise somebody has to read names out of a panel into a chat window.
| Tool | What it does |
|---|---|
qa_scenarios | The saved scenarios with their projects and tags. Listing is a read, so it is free: an agent that can run one but cannot find out which exist is the state that sends people back to the panel. |
qa_scenario_get | One scenario’s steps, as data: what it fills, what it presses, which page each step happened on. |
qa_scenario_save | Writes or corrects one. It stores data and touches no page, so it sits with the reads. |
qa_scenario_run | Replays one against the page as it is now. This one types, presses and follows the recording across pages, so it takes Agent control, and it stops at the first field the page no longer has instead of filling the wrong thing. |
qa_suite_run | A whole project of scenarios as ONE set, with a single answer about the lot: which passed, which stopped and where. |
Turning it on
- 1Requires Pro.
- 2Open the panel → Tools → Agent control. It is a card there, not a setting in the popup: off on a fresh install and off until you turn it on.
- 3Confirm the prompt it shows. It names what you are enabling; it is not a checkbox you can pass over.
qa_open_isolated) needs a third confirmation that Agent control does not cover. Multi-login intercepts the site’s cookies, session cookies included, so it asks separately. With Pro and Agent control both on, that call still refuses until you accept it: the call answers consent-required-isolation and the switch is panel → Logins → + Isolated tab, accepted once. You never have to open an isolated login to let an agent work: a normal tab with Agent drives this tab on is enough, and isolation is only for running a second identity beside the first.The rules it operates under
- It acts on one active tab at a time. Isolated logins keep separate identities, but a browser renders one tab actively, so parallel driving of several sessions is not offered, which is the honest limit, stated rather than worked around.
- It never opens arbitrary HTML or evaluates code you did not ship. Actions come from a closed vocabulary: the tools above and nothing else.
- It is scoped to the tab you point it at. There is no “go find the page” mode.
A working session
What this looks like in practice, from opening a page to a fix in the editor.
- 1Open the feature you are testing and use it like a person would. Qiksy watches while you do.
- 2Open the panel. Run the audit. What broke is already listed with its selector.
- 3Fill the form with smart fill instead of typing test data by hand, and validation then behaves like it will for a real user.
- 4Ask your agent: “read what Qiksy found on this tab and fix it.” It calls
qa_findings, gets the selectors and the failing responses, and works from the real state instead of your description of it. - 5Generate the report for the ticket: repro steps, findings and responses, paste-ready.
Prompts that work well
Read the Qiksy findings on the active tab and fix the errors in this repo.
Take a qa_snapshot, then fill the checkout form with valid data and tell me
what validation fires.
Compare qa_status across my isolated logins: does the admin see something
the customer should not?The pattern that pays off: let the agent read the browser state rather than describing it yourself. Everything you would have paraphrased (the exact error, the selector, the response body) it can fetch precisely.
Free and Pro
| Free | Pro | |
|---|---|---|
| Panel, audits, findings with selectors | Yes | Yes |
| Smart form fill | Yes | Yes |
| Isolated logins | Yes | Yes |
| Session recorder, History, HAR | Yes | Yes |
| Client report · write it, mark it up, print it | Yes | Yes |
| Bug Capsules, personas, scenarios, mocks, Follow mode | Yes | Yes |
Export (qa-export/v1 bundle) | Yes | Yes |
| MCP bridge · every tool your agent has | No | Yes |
| Agent control | No | Yes, opt-in |
Since 1.53.0 the line is drawn in one place. Everything you do with your own hands is free, with no meter and no cap, reports included, however many you deliver. Pro buys the bridge: the socket your coding agent reads this browser through, and drives it with once you switch Agent control on.
Pro is $49 for a year, one payment, with no stored card and no automatic renewal. Every new install gets a 28-day trial of the bridge; when it lapses the extension keeps working exactly as before and only the bridge stops, and the agent is told so in those words, so nobody spends an afternoon thinking the connection broke.
The licence is an offline key: the signature validates on your machine, so losing your network never loses your Pro. One thing is asked of our server, and only for a paid key: a key opens three installations, which no single installation can count by itself: when you paste a key, and once a day after, Qiksy sends the key and a random number it made up for this installation, and reads back one answer. No page data goes with it, and if the answer never arrives you keep working.
Troubleshooting
Read the message · each failure names its own cause
| What the agent reports | What it actually means |
|---|---|
| “Qiksy extension is not connected” | The bridge toggle in the popup is off, or its token/port differ from the server’s. This is the only failure that is fixed in the popup: switch it on and the agent reconnects by itself. |
| “Port … is held by something this bridge cannot share it with” | Another process owns the loopback port: a bridge running a different token, or a qiksy-mcp older than 1.1.0. The extension is fine and the popup is not the place to look: restart or update whatever holds the port, or give this window its own --port. |
| “The agent window that owned this bridge just dropped” | The editor window that held the port closed. The remaining ones take it over within a second or two, so retry the call once. |
| Pro / consent required | A tool that acts on the page needs a licence (or trial) and the one-time “Agent control” confirmation in the popup. Read-only tools are unaffected. |
- The bridge talks to a running extension in an open tab, not to a background service.
- Restart the MCP client after adding the server; most only read their configuration at startup.
- Both halves should be 1.1.0 or newer. Nothing to pin by hand:
npx qiksy-mcpfetches the current build, and the extension updates itself from the Web Store.
Findings look empty on a page that is clearly broken
Errors are collected as they happen. If the extension was installed or the panel opened after the failure, reload the page and reproduce, and then it is captured with its request and response.
A selector that worked once now fails
The page re-rendered. Take a fresh qa_snapshot and target by ref instead: refs are handles into the current snapshot rather than guesses about the markup.
Nothing happens on chrome:// pages
Chrome does not let any extension read its own internal pages, the Web Store, or another extension’s pages. That is a browser rule, not a Qiksy limitation.
What’s new
Every shipped version, newest first: what changed and whether it needs anything from you.
chrome-extension:// pages: people pressed “What’s new” and got a blank ERR_BLOCKED_BY_CLIENT. A normal page cannot be blocked that way, and it can be corrected without shipping a new build.1.65.0 ·the agent can open the page it needs
- The dead end had no way out written down anywhere in the product. The bridge refuses to steer one of your tabs onto a different site, and that refusal is right: it is what keeps an agent out of your mail and your bank. But it ended at «open it in a tab yourself», and the only reliable way to do that lived in one person’s notes, so every fresh agent rediscovered the dead end and stopped there, and what you saw was the bridge giving up.
qa_open_tab { url }opens the address as a NEW tab, beside the ones you already have, losing nothing. The boundary is untouched: it still cannot steer an existing tab off-site, and a new tab costs you nothing if the agent was wrong about needing it.- It lands in the right browser by construction, not by care. A machine has several Chrome profiles and the extension lives in exactly one; a tab opened into any other is invisible to the bridge, and every reading afterwards is about a tab nobody can see. Naming the profile does not work: a running Chrome ignores the flag, the command still exits 0, and there is no error to diagnose. So this names no profile at all: it takes a tab the bridge can already see, finds the window holding it, and inserts the new tab beside it.
- The refusal now names the verb. A rule an agent reads when it is calm does not survive the call where it is stuck, so the way out rides the answer that says no.
- And it tells the truth about two different outcomes. «In the session» means the tab is the agent’s to drive and the reply carries its id. «Not in the session» means it is open but the agent still cannot act on it (once any tab has been switched on for the agent, the session is that explicit set) and the reply names the switch instead of leaving the agent to measure a tab it cannot see.
1.64.0 ·the finish signal stopped guessing
- The sound was going off in the middle of a run. Since 1.46 the bridge announced a finish by itself once nothing had touched the browser for ninety seconds, on the reasoning that an instruction to call
qa_doneis a request rather than a mechanism. But an agent that leaves the browser to read files, edit code or think is silent for minutes while working flat out, so the signal arrived mid-run, and again at the next lull, and again. - The defence of it only ever covered half. It was called safe to be wrong because any later call cancels the pulse. A pulse can be withdrawn; a sound that has been heard cannot. And a signal that is right most of the time is worse than none, because it is learned as noise and switched off, taking the true ones with it.
- Guessing is off by default now. The mark pulses and the sound plays at a real full stop: your agent calling
qa_done, or the finish hook thatnpx qiksy-mcp initwrites into your client. Once either of them has been seen, the process stops estimating for the rest of its life: a setup that reports its own endings is never guessed at, even if somebody switched both on. - The estimate is still there for a client that has neither:
QIKSY_MCP_DONE_AFTER, in seconds, twenty at the lowest. Below that it would fire inside an ordinary pause for thought, so a smaller number is refused rather than honoured. - A report now names the time zone beside the moment, as «Europe/Kyiv (UTC+03:00)». The conditions were already collected for the agent bundle and were simply not reaching the document a person reads, which carried the browser string instead. The browser string is identical for the two people arguing about a bug; the zone is what settles «works on my machine», because it is what puts a deadline or a booking on a different day.
1.63.0 ·reports land in the project they are about
- They used to land in the download folder, where they belong to nobody. A week later nothing says which report was about which product, and a report is the one artefact here that somebody else reads: it gets attached to a ticket, sent to a client, referenced from a commit. It belongs beside the code it describes.
- The bridge already knew the answer. The agent process is started from the folder you work in, and it already tests that folder for the marks of a project (
.git,package.json,CLAUDE.md,AGENTS.md) before writing its own guide there. The same test decides this.qa_report_buildwith nooutnow writesqiksy-reports/<date>-<subject>.htmlunder the project and hands back the path. - The panel’s own Report button goes there too, while the bridge is connected. Without it, the ordinary download, exactly as before. The toast says which of the two happened and names the folder: a file that went somewhere you were not told about is a file you have lost.
- That needed a direction this protocol did not have. The bridge asked and the extension answered; the extension could not ask for anything. One frame each way was added, and deliberately kept narrow: the browser side sends a file name and the bytes, and the agent side chooses the folder. A name that tries to climb out of it (
../../etc/passwd) is reduced to a plain file name, because a directory chosen by the browser side is a directory chosen by a web page one bug later. - Yours to move.
QIKSY_REPORTS_DIRputs the folder anywhere;offmeans nothing is written unless a path is named. Outside a project nothing is written either, because your home directory is not a project.
1.62.0 ·a long release note fits in the popup
- The note ran off the bottom of the window. A release note is as long as the release was, and the toolbar popup is a narrow strip, so there is no length that can be assumed. The text is now capped at about ten lines and scrolls.
- The cap is on the TEXT, not on the note. «What’s new» and the × stay above the scrolling part, because the way OUT of a message must never be the first thing to scroll away. The foot of the text fades rather than being cut: a line stopped dead reads as the end of the paragraph, a line that dissolves reads as «there is more».
- The first attempt made it worse, and only a screenshot showed it. Capping the text while leaving the controls floating beside it turned the note into a column nine characters wide: a scrolling box is a block formatting context, and such a box does not flow around a float, it stands beside it for its whole height. Every measurement still said «bounded and scrollable», and every one of them was true. The version and the two controls now take a row of their own.
- The check that covers this layout was not in the build. It exists, it describes the note’s shape on screen, and nothing was running it, so it said nothing about any of the above. It runs on every build now.
1.61.0 ·the button is back in its corner
- The same gap below as beside. The setting added in 1.60 that remembers where the button was dragged to read an absent value as zero, so on a browser where nobody had ever moved it, the button was parked at the very bottom edge, with the 20px gap only on its right. «Never set» now leaves the stylesheet’s own corner alone, and the check compares the two gaps against each other rather than against a number, so the next person to change one cannot quietly break the other.
1.60.0 ·the button moves out of the way
- Drag it anywhere; it lands on an edge. On release it flies to the nearer side and keeps the height you left it at. The corner it used to own is the busiest one on the web (chat widget, cart badge, cookie bar, «back to top» arrow) and a control you have to work around is one that covers the thing you came to test.
- Never in the middle. Free vertically, magnetic horizontally: the centre of the screen is where the form under test is, and «which side am I on» is a fact the panel, the callout, the recording frame and the update notice all read. A button at an arbitrary x would leave every one of them guessing.
- It is still a button. A press that travels less than five pixels opens the panel as before, and the click that ends a drag does not. The side and the height survive a reload, and Settings → Appearance keeps the plain left/right choice for anyone who would rather not drag.
- The update notice came out on the wrong side, for everybody. After an update the «Qiksy updated itself» strip appeared bottom-LEFT whatever side the button was on: a second logo on the far side of the screen, which reads as a stray panel rather than as your own button speaking. It was measuring the extension’s container, which is a zero-sized box pinned to the corner of the window on purpose, so the answer was a constant and not a reading. It now measures the button.
1.59.0 ·following a link no longer throws the agent out of the session
- The agent stopped at the first search result. Switch it on for a search page, ask it to find a product, and the click that opened the shop was also the click that ended the run: the tab had left the site you switched the agent on for, so it dropped out of the session. Everything after that answered «this tab is not in the session», which reads as a broken bridge and is not one.
- The exception that existed was decided by somebody else’s markup. A link with
target="_blank"opens a NEW tab, and a new tab inherits its place from the one that opened it, so that link survived. The same link reusing the tab did not. Nothing about that difference is visible to the person, and nothing about it is about safety. - A hop the agent itself made now keeps its place, as a guest. Only a tab that was already in the session, and only when the agent ACTED on it a moment before: a page you typed an address into yourself still drops out, exactly as before. Reading never extends anything; only acting does.
- A guest seat is narrower than a switched-on site, and it says so. The site is not remembered: no other tab on it joins, and nothing survives the browser closing. The seat is named in the toolbar popup («Guest: the agent followed a link from …») and in
qa_tabs, and the tab’s own switch takes it back. - Also from 1.58.0: closing a message no longer takes away the way out of it. «Reload the page» lived inside the dismissible «updated to» note; it now has a row of its own with no ×, and Settings → Appearance carries the version and a «What’s new» button.
1.58.0 ·closing a message no longer takes away the way out of it
- «Reload the page» was inside a note you are meant to dismiss. The «updated to» note is NEWS: you close it and it never comes back. «This tab is still on the old version» is a STATE, true until the page is reloaded. Putting the second inside the first meant one accidental × and the way out was gone, with no hint that it had ever been there. The reload now has a row of its own in the toolbar popup, with no ×, and it goes away by being fixed rather than by being dismissed.
- Settings → Appearance carries the version and a «What’s new» button. Dismissing the news should not lose the news.
- Two sentences were English in every language, and the check that should have caught them was looking the other way. One under multi-login, one under the tab-order overlay, both spelled out in the panel’s source while their translations sat unused in the dictionary. The hardcoded-text gate skipped them because it treated a bare «=» as a sign of code, and «each Ctrl+T tab = its own login» contains one. The rule is now narrowed to what code actually looks like, and it found the second sentence itself.
- Spacing. The report shelf had a bottom margin and none on top, so it sat flush against the row with Build in it; the multi-login heading’s icon touched its own text and rode below the line. Both reported from screenshots.
1.57.0 ·the shortcuts are yours to change
- All three keys are declared to the browser, so they live in
chrome://extensions/shortcutsand can be given any combination you like. Before this they were caught by a listener inside the page: that works, and nobody could change it. It matters most on Windows, where Alt+Shift is the system’s own keyboard-layout switch: a fixed default there is a default that somebody with two languages cannot use, and no amount of guessing from here tells us which combination is free on their machine. They also fire now when the focus is in the address bar rather than on the page. - The defaults are unchanged: ⌥⇧S / Alt+Shift+S photographs the page and opens the markup editor, Alt+Shift+Q opens the panel, Alt+Shift+K opens the command palette.
- What was checked, and what could not be. The labels and modifiers on a non-Mac are checked by a stand that overrides the platform at the browser level: Alt+Shift+S rather than ⌥⇧S, Ctrl rather than ⌘, the tool letters unchanged. Whether Windows itself steals Alt+Shift cannot be reproduced anywhere but on Windows, which is exactly why the repair is «make the key changeable» rather than «pick a better default».
1.56.0 ·a screenshot of the page is two keys
- ⌥⇧S on a Mac, Alt+Shift+S elsewhere, pressed on the page, with no panel to open first. The shot is taken whole and lands straight in the markup editor, which is where a picture was going anyway. The key is written on the button, because a shortcut nobody can see is a shortcut nobody uses.
- A capture that could not be taken used to say nothing at all. The rule was «put the panel back if it was open», right while the only way to start a capture was a button inside it, and wrong the moment a key could start one with the panel shut. Pressing it then looked like a dead key, whatever had actually gone wrong. The reason is always shown now; only cancelling the screen picker stays silent, because nothing went wrong there.
- Shortcuts are read by the key’s POSITION, not by the letter it produces. Hold Option on a Mac and the Q key produces «Œ», so a check written against the letter is a shortcut that works on one platform. Alt+Shift+Q and Alt+Shift+K were written that way and are now read by position too.
- In the markup editor the tool letters are drawn as keys, and the row says «one key». The actions on the same bar wear ⌘Z, ⌘S and ⌘C, so a lone grey «B» beside them read as half of a combination whose first half had gone missing. Nothing about the behaviour changed, because a bare B always selected Box; only whether that was legible.
- The session recorder no longer starts by itself, and it asks before it does. It was tied to the History block, and since 1.53.0 every block is on from the start, so it began for everybody, unasked, with a blinking red REC over their employer’s live application. That is the one thing this product says it never does: nothing acts on its own initiative. Switching it on now opens the same consent box that guards roles and the bridge, naming what is written down (every page visited, what was found, which requests failed, and a small screenshot of the tab at each step), where it stays (this machine, last 60 steps) and how long (a day). The mark that shows it is running is still there; it just no longer blinks in red, and it says «Recording» instead of «REC», because REC is what a video camera says.
1.55.0 ·the bridge waits while you sign in
- Walking into a login was reported as a missing permission, and it had never worked otherwise. The rule that recognises a sign-in was asked about the registrable domain («google.com») while it parses what it is handed as a URL. A bare domain threw inside it and came back
falseevery single time, so the whole hand-off was dead in the shipped build while its own checks stayed green: they called the function the way it reads best instead of the way the product calls it. It is asked about the address now. What you get: the agent is told the sign-in is yours to do, waits up to five minutes withqa_wait_for { urlContains }, and carries on where it stopped; and if nobody starts, it says that plainly instead of trying again. - Some logins never move the address. Apple’s opens inside a frame: the address stays
icloud.comfrom beginning to end, and a snapshot there legitimately shows no fields, because this bridge does not enter frames. The advice now names the second route in the same breath (wait for what only appears once you are through) and says that an empty snapshot on a login page is a frame, not a fault. - The «reload the page» notice is part of the round button. After an update the old script on an open page is cut off from the browser, and the notice used to be a separate box with an × that deleted it, taking away the last sign that anything was wrong. It now slides out from under the button, the same height as it, folds back under it with the arrow, and leaves a small tab so the reminder never disappears on you. The button itself is untouched: no badge, no ring, no watermark.
- One reload clears every open tab. Adoption after an update runs once and misses whatever the browser had discarded or was still loading; those tabs kept a dead script and met you with the same notice one after another. A freshly started script that finds a notice now tells the worker, and every open tab is swept again.
- The update can be finished from the popup. It belongs to the new extension, so it works even when the page does not. The «updated to» note carries a Reload the page button, shown only on a tab that is actually waiting for it, because on a healthy tab it would throw your work away for nothing.
- An update is no longer held forever by a connected bridge. «Connected» is not «working»: a bridge that has been quiet for five minutes counts as idle, so Chrome stops postponing the swap indefinitely.
- The popup opens at its own top, instead of already scrolled past the site switch to the agent section.
- The markup editor leads with crop, selected by default, and every tool writes its shortcut on its own button (⌘ on a Mac, Ctrl elsewhere). Undo now covers the crop as well as the shapes, there is a step forward, and a drag is one undo step rather than sixty.
- A long-page screenshot no longer trips over sticky panels. The scan is bounded by a millisecond budget instead of a node count, because a sticky element’s place in source order says nothing about where it is on screen, and the docked panel no longer squeezes the page it is photographing.
1.51.0 – 1.54.0 ·what shipped in between
- Pro buys the bridge and nothing else (1.53.0). Everything you do with your own hands is free and switched on from the start: every block in the panel, the recorder, reports, Bug Capsules, personas, Follow mode, mocks, scenarios. The per-feature gates and the two-reports-a-month meter were deleted rather than left returning true, because a dormant meter is a promise that quietly comes back. The first package of that release still carried five of them; it was withdrawn and rebuilt, and a check now fails the build if a gate ever reappears.
- The site switch has three positions (1.54.0): Off · No button · On. Before, the only way to get the round Q off a page was to stop Qiksy on that site, and the agent bridge stopped with it. The middle position takes away the button and nothing else; the panel still opens on Alt+Shift+Q.
- The report is the bridge’s to write (1.52.0). A draft the agent adds lines to, edits in place and builds, and it refuses to build without a picture, so a report is evidence rather than a memo.
- Crop is a layer, not a choice of blocks (1.51.0). You drag a frame and land in the ordinary editor.
1.50.0 ·thirty seconds of silence became a sentence, and the manual caught up with the product
- A call the page never answered cost half a minute and then blamed your focus.
sendMessagerejects when nothing is listening, and that is handled everywhere. The expensive case is a listener that TAKES the message, promises a reply and never sends one: that promise never settles, the call burns the whole server window, and what comes back is “Is a normal web page focused in Chrome?”, a sentence about focus, handed to somebody whose focus was fine. Measured in a tab that had been open since before the last extension update:qa_storagedid this on every call whileqa_read,qa_stylesandqa_snapshotanswered normally in the SAME tab, so nothing pointed at the tab. A tab opened a minute later answered in 40ms with 29 keys. - Every round trip to the page now has a fuse. It does not repair that tab, it names it: “the page is still running a superseded copy of Qiksy, reload the tab”. Eight seconds for a read, thirty for an upload, fifty-five for axe, which walks the whole DOM. A diagnosis in a fraction of the time beats a silence that ends in the wrong advice.
- A watch answers to its name.
qa_watchtakes anameon start, shows it in the list, and read and stop resolved the id alone, so the refusal said “no watch called (none given)” about a call that had given one. Broken in both halves at once: the server droppednamefrom the arguments before they left the process. Stopping by name also deleted the wrong key and left the watch occupying one of the four slots. - The manual finally lists all sixty-six tools. The package README promised fifty-five and described forty-two; the agent guide had forty; this page named fifty-seven under a heading that said sixty-six. Missing were whole families: the camera and the shelf, reports, scenarios, the end-of-run signal. An agent does not use what it has never been told exists, so half the product was invisible to the half that was meant to drive it.
- Found by a self-check, not by a person. Both defects came out of walking every verb against a real tab and then re-running the ones that failed with the arguments their own schema asks for, which is how a wrong argument of mine stopped being reported as a bug of ours.
1.49.0 ·a report you write yourself, and a mark that stops shouting
- The report can be written by hand. Everything needed for one was already here (the blocks, the pictures, the print stylesheet) and only the agent could call it. A manual tester, the person this product is for, was left holding eight thoughts and a text file. Now there is a draft: pictures, problems and plain lines go into it while you work, and the document is assembled at the end.
- It is a shelf, not an editor. Nobody opens a blank document and sits down to write it; thoughts arrive one at a time over an hour. So the shot shelf gained a
+, the marking editor offers «add» the moment you finish drawing on a screenshot, and a quick line files a note, a section, steps, a problem or an observation with one keystroke. A problem or an observation can carry what happens, what was expected, how serious it is and which picture shows it. - Then «Build», and Cmd+P. It becomes the same self-contained HTML the agent builds (cards, ids you can quote, the run conditions) kept in the extension under Reports, and printing to PDF is the browser's own dialog. There is no PDF library in the package: Chrome does it better.
- One assembly, two hands. The panel and
qa_report_buildnow go through the same function, so a document written by hand and one written by the agent cannot drift. And the draft is shared: collect eight rough lines and three screenshots yourself, then ask the agent to tidy them: it writes into the very same draft. - The finish mark stopped blinking everywhere. A run that ended in one tab set the mark pulsing on every tab in the session: GitLab, Jira, mail, whatever else you had armed. Spotted from the outside by somebody watching it happen: «it feels like when a task finishes in one tab, all of them blink». It now pulses only the tabs the run actually touched.
1.48.0 ·four ways the bridge could go quiet, from one long real session
qa_gap puts a sentence straight into a file we read.- The server could hang on start, and the timeout blamed the wrong half. The config said
npx qiksy-mcp, and an npx that does not already hold the package stops to ask «Ok to proceed?» on stdin, which for an MCP server IS the protocol. The client's owninitializeframe answered the question, nothing replied, and the agent reportedconnection timed out after 30000mswith no trace anywhere, because the server never ran. It bites hardest right after a release, when the cached copy is no longer the latest. Every config we write or print now saysnpx -y. - A busy port named three suspects and no culprit. «another process, a bridge running a different token, or an OLDER qiksy-mcp» left the reader to work out which. The holder's pid and command line are one call to the operating system away, and they are now in the message.
- A token mismatch is its own sentence. Our own hub refuses a wrong token with a distinct close code, so «it IS a Qiksy bridge and it refused this window's token» is a fact, not a guess. And a window started with no
QIKSY_MCP_TOKENat all is told that first, because no amount of looking at the port explains a missing key. - The agent could not come back. The site under test was read off wherever the tab was standing, and localhost is always allowed, so one hop to a dev server rewrote it, and the app being tested was refused as «a different site» for the rest of the run. Navigation now also accepts any origin you switched the agent on for. Exact origins, so nothing widens:
gms-app-dev.example.orgstill does not admitexample.org. - A refusal says where the tab actually is. A sign-in walks one tab across several hosts, so it legitimately leaves the session in the middle, and «Tab 7 is not one of them» named neither the site it ended on nor the gesture that admits it. It does both now. Following a tab onto an identity provider automatically is the one thing we will not do: that switch is a permission, and an agent that could grant itself one would not be a permission.
qa_collecttakespropertiesas well astake. The tool's own sentence is «which of their properties to take», so that is the word that comes to mind, and an unknown argument was silently dropped, leaving the default columns and the impression that the feature does not work.qa_probeaccepts aref. It is the identifier every other verb takes. It is resolved against the snapshot it came from and re-found on the fresh one, so the renumbering that makes refs dangerous elsewhere cannot bite here.
1.47.0 ·the finish hook was being turned away at the door
- The exact finish signal never reached the browser.
npx qiksy-mcp doneis what a stop hook runs, and a hook runs in a bare shell, with none of theenvthe agent hands its MCP server, so no token, so the bridge closed the connection. Correctly: it is an authentication gate. The cost was the reply, «the bridge closed the connection», which reads as a browser fault and sends you to the popup, the one half that was working. The command now reads the same pairing out of your own agent config (.mcp.json,~/.claude.json; read, never written), and when there is nothing to read it says which half is missing. - An old token in your shell beat the right one in your config. A
QIKSY_MCP_TOKENleft over in a.zshenvfrom an earlier setup is exported into every shell on the machine and looks exactly like configuration. It is still tried first, because an explicit export must win when it is right, but a refusal now falls through to the pairing the agent itself is using. Two attempts, never more. npx qiksy-mcp connectcould re-pair you by accident. It looked for an existing token only in the files it writes itself, so on a machine wired withclaude mcp addit found none, minted a fresh one and left the browser holding a secret nobody had any more, so «Token rejected» on the next restart, from a command you ran to fix something else. It now reuses the pairing wherever it lives.- The update note uses the whole width. It was a flex row, so the two controls held a column of their own for the full height of the box and a ten-line paragraph wrapped inside 62% of it. They float now: the first two lines make room, everything below runs edge to edge.
1.46.0 ·the sound works without anybody wiring it up
- The end of a run is announced even when nobody asks for it. The signal used to exist only if the agent called
qa_done, and an instruction is a request rather than a mechanism, so an agent installed before this release kept its old habit and the person heard nothing. The bridge now says it itself once a session that did real work has been quiet for a while (90 seconds;QIKSY_MCP_DONE_AFTERchanges it,0turns it off). It is an estimate and it is built to be wrong cheaply: an agent that was only thinking cancels the signal by carrying on, because any call stops the pulse. - And there is an exact version for Claude Code.
npx qiksy-mcp initnow also adds a finish hook, which fires the moment the agent really stops. Other hooks in the file survive, running it twice adds nothing, and--no-hookskips it, because a command that will run on every stop is not something to install quietly. - Sixteen sounds instead of eight, and the coin is a coin. The whole generated set ships now. The one called “coin” was measured to be a 0.8-second sweep climbing to 7 kHz: a power-up flourish, not a coin; it was replaced with one that measures 0.36 s, pitched and rising, which is the signature of the sound people actually mean. The default moved to the anvil, the one sound in the set that measurably RINGS rather than clacks.
- The sound picker got smaller where it was shouting. The arrow beside it was the loudest thing in the row; the speaker button is now a square of exactly the picker’s height rather than a pixel off; and the row starts where every other row starts.
1.45.0 ·you can hear the end of a run, and a screenshot can keep a secret
- The end of a run is now something you can hear and see from across the room. An agent calls
qa_donewhen it has finished, the mark on the tab pulses slowly, and a sound plays once. The pulse stops the moment you look at that tab, and it never starts on a tab you are already looking at. Turning the sound off leaves the pulse: they are two separate answers to “are you still working”. - The sound only fires when something actually happened. A finish signal with no work behind it, or a second one half a minute after the first, is refused: a notification you learn to ignore is worse than none. Sixteen sounds to choose from in the popup, each one auditioned by clicking it, and the chosen one has a speaker button of its own. They are the set generated for Jig, so two products say “done” in the same voice, with one exception: the coin was measured to be a rising sweep rather than a coin, and was replaced.
- The camera was handing back the page as it WAS. When the browser window sat behind another one, Chrome returned the last frame it had drawn: no error, right size, right page, wrong content. Proven the hard way: the page was changed and two shots eighteen seconds apart came back byte-identical. Both capture paths now refuse instead, and say to bring the window forward. A screenshot that is quietly out of date is the most expensive thing a tool like this can produce.
- A report is no longer only a bug report. Blocks for sections and for points that work, a green mark for “this holds”, and no “Expected” line where nothing was expected, so a review, a handover note or a description of how something behaves is the same document, written by the same verbs. And it is kept in the extension: the Shots tab lists what has been built, and you can open, save or delete any of it without hunting through Downloads.
- Marking a shot: a blur pane and a leader line. Draw a pane over an address, a customer name or a token. It can be moved, stretched by its handles, and its strength set, and what is under it is taken apart rather than softened. The pane applies to the picture you save, copy or put in a report; the clean shot stays whole on the shelf, so it can still be moved or removed. And a caption now attaches itself: put it down near a block and a line joins the two, carry it away and the line lets go, bring it back on another side and the line comes from that side instead. The assistant does the same in one call:
qa_annotateplaces every label in free space, clear of the other marks, and joins it to what it is about. - What you have to do for the sound, and it is one thing. The extension updates itself and the sound is on unless you switched it off. The bridge is a program YOU run, though, and it only picks up a new version when it next starts, so restart your coding agent once. After that it works on its own: the agent is asked to say when a run has finished, and if it does not, the bridge says so itself once the session has been quiet for a while. Want the exact moment rather than a good guess?
npx qiksy-mcp initadds a finish hook to Claude Code, which fires when the agent really stops. - No new permissions for any of it, except the one that lets an extension play a sound at all.
1.44.0 ·a site you switched off says so, everywhere
- Switching Qiksy off on a site stops everything in the page, and the agent switches did not know. They still answered yes, and the extension counted the tab as driven, so a tab could sit in the agent session with no Qiksy in it at all: no panel, no mark on the favicon, and every agent call answering “not running on this tab” with nothing on screen to explain why. Reported on Gmail, where the site had been switched off weeks earlier and everything downstream looked broken for no visible reason.
- The popup now says which of the two is in the way, the site being off or the bridge not being connected. It locks the two agent rows while it is, and offers the one gesture that fixes it.
- And the agent’s own answer names the real reason. It used to say “reload the page”, which on a switched-off site cannot help at any number of reloads; it now says the site is off and where to turn it back on.
- No new permissions.
1.43.0 ·setting it up is one command, and a tab stopped acting twice
- One command wires the whole thing up.
npx qiksy-mcp init, run in the project you work in, picks a free port, mints the token, writes theqiksyserver into every agent config that is actually on the machine (Claude Code, Cursor, VS Code, Windsurf, Claude Desktop) and leaves the token on your clipboard. Finding and hand-editing that JSON file is the step people stopped at, and it is the one step the extension cannot do for them: a browser extension has no filesystem. This process does. - It does not invent anything. A config is written only where that client’s own folder already exists, every other MCP server in those files survives, a config it cannot parse stops the run instead of being overwritten, and a second run keeps the token that is already there, so a browser you have paired stays paired. On Windows the entry is written as
cmd /c npx …, the form that actually starts there. - The popup says what it needs, in order. The two agent switches do nothing without the bridge and used to say so nowhere: you could switch a tab on, watch nothing happen, and never learn that the setup lives in a card further down. They are now locked until the bridge is on, the block says why, and the one gesture that fixes it is right there. Accepting it scrolls to the next step and puts the keyboard on it.
- A driven tab says whether an agent is actually there. The corner marks on the favicon meant “the agent may act here” whether or not anything was connected. They are now quiet while nothing is on the line and full strength once an agent is: the same mark, not a new colour.
- Fixed: a tab open during an update ran two copies of the extension. Chrome injects the content script, and the extension separately adopts already-open tabs so they are not left with a dead context, and both copies answered every command. One agent click pressed the button twice, two milliseconds apart, with nothing in any reply to say so, and each copy kept its own panel on the page. On a real, logged-in application that is a duplicated submit. A copy that has lost the page now acts on nothing and lets its panel go.
- The marks on a favicon are visible on an orange one. Terracotta on Hacker News’ orange square was invisible at sixteen pixels: the mark was there and said nothing. Each corner now carries a dark outline, so it separates from whatever it lies on.
- A form card reads as a toolbar. Fill was a couple of pixels taller than the variants beside it, and both rows ended in a ragged edge with a third of the card empty. Same height now, and the buttons share the width.
- No new permissions.
1.42.0 ·the pointer can stay somewhere
- Hovering was the one thing the assistant could not do. A menu that opens on hover, a tooltip, the actions a row reveals only under the pointer: all of it was out of reach, because the only verb that moved a pointer was the click, and a click presses a millisecond after it hovers. It opened the menu and closed it in the same call.
qa_hoverputs the pointer somewhere and leaves it there, then reports what appeared, what the tooltip says, and whether the control now calls itself expanded. - It waits before it looks. Tooltips take their time (half a second is the usual delay) and a measurement taken at once reports “no tooltip” about one that was always going to appear. The hold is half a second by default and you can set it.
- And it says when it could not do the whole job. Dispatched events run the page’s own JavaScript, which is every menu and tooltip library, but they do not move the real cursor, so an effect written purely in CSS does not appear. The answer states that as a fact instead of leaving a healthy dropdown to be filed as broken.
off: truetakes the pointer away again, because “the tooltip disappears” is a separate check. - The popup now shows the mark your tab wears. The focus marks around a driven tab’s icon appear in the tab strip, where they belong, and nothing said which switch had put them there. The same glyph now sits beside Agent drives this tab, at the same sixteen pixels the tab draws it.
- No new permissions.
1.41.0 ·one picture, two pairs of hands
- A screenshot stopped being a moment and became a thing with a name. Every capture, yours or the assistant’s, goes on a shelf, and the marks drawn on it are kept as DATA beside the clean image rather than burned into the pixels. That one decision is what makes the rest possible: the shot can be re-opened and re-marked, a crop can be pulled back out, and the assistant can READ what you drew, caption and all, instead of guessing at pixels. Flattening happens once, on the way out.
- Crop, and marks you can move. Drag a region and the rest dims; eight handles resize it, dragging inside slides it, and holding near the edge scrolls the picture, because a ten-screen shot cannot have its region chosen inside one screenful. A mark that is already placed can be picked up and moved: it is grabbed by its ink, so a frame is taken by its border and not by its middle, and you can still draw a new mark inside an existing one.
- The arrow is a brush stroke rather than a line with a triangle on it. Its tail no longer tapers to nothing (that is what left the thin whiskers) and it is cut on the slant where a hand would lift. The palette left the default web colours behind: terracotta, warm ochre, forest green, the teal Jig is built around, and a near-black. Those defaults are the ones a browser draws errors in, which is why every mark used to shout.
- Accessibility has its own tab, and a report comes out of it. It is the one check in the panel whose verdict is machine-checkable (a named rule, not an opinion) so it is also the part of a report nobody argues with. One press builds a self-contained HTML page; print it for a PDF, with the structure intact.
- The report says whether the run was INTERFERED with. A mocked 500 looks exactly like a server’s 500, and a developer can lose an hour to a response that never came from their code. So the conditions of the run (mock rules in force, dialogs answered automatically) are read from the bridge’s own state and printed at the top, in red when they apply. A person writing this by hand says “I mocked it” because they remember doing it; a machine prints the result and forgets the conditions.
- Expected is never invented. No verb here knows your requirements, so the field is either filled by a person or honestly empty. A generated “expected” turns a report into a hypothesis wearing the clothes of a protocol.
- The tour stopped producing false coverage. The percentage and the progress bar are gone: a tour lists reasons to go and look, not the product’s requirements, so it has no denominator and a fraction invents one. The auto-tick, which turned“opened the form” into “happy path passed”, is now a weaker, separate mark that says “activity seen”. An item counts as checked only when a person writes what they saw, and items now come with EVIDENCE: a failed request, a text field with no length limit declared, an error the page threw.
- And the tour becomes yours by being spoiled. There is no “create a tour” button, because every builder dies of the blank canvas. Cross one generated item out and the list stops regenerating and starts being yours; adding is one line, with the page’s own controls offered as things to point at.
- The panel used to stop working while a page held a native dialog open.
showModal()blocks everything that is not a descendant of that dialog (clicks, focus, keyboard) and the panel sits beside it, so for as long as an application kept a modal open, Qiksy could not be used at all. Two candidate fixes were measured and rejected before the third: the panel now moves inside the open dialog and returns the moment it closes. Your own buttons keep working throughout. - Some favicons turned grey under the focus marks. The icon was fetched in a way that fails outright when a site serves it from a CDN without an allow header, and the empty result fell into the branch written for “this site has no icon”, a different fact entirely. There are now three routes to the icon, and the grey plate is the last of them.
- The red counter on the round button is gone. It counted errors the extension picks up by itself while you merely walk the page, a notification badge about something nobody asked for. The number still lives in the health ribbon and in Findings, where you go when you want it.
1.40.0 ·a screenshot stops being a dead end
- A picture now carries a map of the page. Taken from inside the document, a capture knows where every control is, what it is called and how to find it again, and a flat PNG threw all of that away. The map rides beside the file: name, role, selector and box in image pixels. It is what lets a mark name an ELEMENT instead of a coordinate, so the same instruction draws the right picture after a re-render or at another screen width, and it is why a finding can now draw itself, since a finding already knows where it is.
- Ctrl+F works inside a screenshot. A searchable copy writes every string the page was showing invisibly over its own pixels. It is not OCR and has no accuracy to argue about: the strings were read from the live document, so an eight-pixel label and a Chinese heading are exactly as correct as the page was. Text inside a canvas or a frame cannot be included, and the file says so.
- A recording arrives with the moments the page actually had. Clicks by the control’s own name, route changes, failed requests with the server’s answer, what a screen reader was offered, and the lines where it was offered nothing at all, which is the one kind of evidence a pixel recorder cannot approach. Click a moment and the video jumps there. Saved as one page with the video inside it.
- Every screenshot says which build it is of, and what was covered. The script files the page really loaded, the version it declares, the window and the colour scheme, so nobody has to ask “which build was this”. Beside it, what was hidden before the frame and what was never collected at all, which is what makes a picture of somebody else’s application sendable.
- The failure states nobody ever photographs. One command walks a screen through empty, 500 with the real error body, a held request, 403 and a dropped connection, and writes one self-contained HTML sheet. Every other capture tool can only photograph a failure that happens; this one causes it, because the mocks and the camera are the same product. The rules are always removed afterwards.
- Accessibility evidence that a screenshot cannot give. The keyboard route drawn on the page as numbered pins, so a tab order that leaps across the screen is obvious to somebody who has never heard of WCAG. A measured answer to “is focus visible”: what changed in the styles and what fraction of the pixels moved. And which error message is actually wired to which field, since being NEAR a field is not being attached to it.
- “The page jumped” became the name of an element. The browser reports every layout jolt with the nodes that moved and where they moved from.
- What changed, by control rather than by pixel. “The Submit button moved 8px down, its padding went 8 → 12, and it went disabled” instead of “23 417 pixels differ”. Text is deliberately not compared, because a name or a total differs between two healthy runs, and reporting that is the noise that gets visual diffing switched off.
- The canvas limit was taken from the documented minimum and was four times too low. A full-page shot is stitched into one canvas, and Chrome answers an oversized one with a BLANK picture rather than an error, so the code scaled the result down to fit 16 384 pixels a side. Measured in Chrome 152: a canvas paints at 65 535 a side and at 180 megapixels. A thirty-screen page fitted comfortably and was being scaled to 0.39 anyway, which is why short shots looked sharp and long ones looked like mush: the reduction was proportional to the length of the page.
- The limit is now measured rather than believed. After the canvas is created, one pixel is written into its far corner and read back; blank means too big for this machine right now, and the shot steps down and tries again. A limit that is measured cannot go stale the way that one did.
- And a shot written to a file keeps its full resolution. The reply used to be downscaled to 1200 pixels wide even when the caller had asked for it to be saved to disk. That shrink exists to protect the conversation from megabytes of base64, and a file has no such budget. Inline pictures are still capped, on purpose.
- Measured on a 28-screen page: 731×16 384 before (about half a pixel per CSS pixel) against 2560×57 408 now, one to one.
- No new permissions.
1.38.0 ·a table instead of a page, and watching for a while
qa_collect: ask for a table. Name which nodes and which of their properties, and get exactly that, once: “every row with its price”, “every link and where it points”, “every input with what it currently holds”. The last one matters most:valueis what a control HOLDS, and a snapshot cannot tell you that.qa_watch: watch for a few seconds. Every other verb answers about NOW, so a toast that appears and leaves, a request fired by a timer, a late row or a live-region announcement were invisible to a snapshot taken before or after them. Four kinds: the DOM, the network, the console, and what a screen reader would have said. Every watch expires by itself: there is no “until stopped”, because a watch left running is silent and the readings taken after it are the ones that quietly go wrong.- Replies can be asked for the fields you want.
selectandomiton the big reads: most of “the bridge cannot do this” turns out to be “the verb did not hand back something it had already measured”, and the opposite case costs you a whole page of context for three fields. - And when it genuinely cannot, there is somewhere to say so.
qa_gapwrites one sentence to a file you read. The flexibility then happens in the next version, with a verb that has a name and a test, instead of at runtime inside your browser, which is the one thing a tool that watches other people’s applications must never do. - No new permissions.
1.37.0 ·the mark on a driven tab frames the icon instead of lying on it
- The site’s icon steps inside the frame. The corner marks used to sit ON the artwork, which on a square favicon means on the logo itself: Cloudflare’s and npm’s, checked live. The favicon now moves two points inward and is centred, so the marks stand around it and cover nothing. That is what a focus frame is supposed to do, and it is the reason the marks could go back to full strength: the transparency existed only to let a covered logo show through.
- The marks are drawn four times larger before the browser scales them down. A tab shows a favicon at 16 pixels and nothing can add detail there, but the curves were being resolved inside a 32-pixel canvas and arrived with stepped edges. Computed over 128 and handed over for the browser to filter, the same shape arrives smooth.
- And the sharpest icon the site offers is the one we draw. Among the icons the browser itself would choose: the media-query rule that keeps a dark-mode logo from being repainted still decides that first: the largest wins, and an SVG wins outright. A site shipping both a 16px and a 192px icon was handing us the 16.
- No new permissions.
1.36.0 ·a list with its own search box, and a click that is judged by what it actually did
- A dropdown whose popover carries its own search box now gets driven the way a person drives it. Two habits made those controls unfillable. Some only draw a WINDOW onto their rows, so the row you asked for is genuinely absent from a healthy open list until something is typed, so the value now goes into that search box first, and if the filter empties the list the query is cleared and the original list is used. Others ignore a click entirely and commit the highlighted row on Enter; that is now tried once, after the click has failed, while the popover is still open.
- A click is judged by what it did, not only by what the page redrew. A click that changed the route and a click that fetched a file both leave the accessibility tree identical, and both used to be reported as a dead control, which, believed, files a bug against a healthy button. The reply now carries the address when it changed and the number of requests the click put on the wire, and says plainly when it could not measure at all. A button the page has switched off is refused with that reason instead.
- A write to web storage is read back before it is called done. It was measured and then ignored, so a page whose storage refused the write answered exactly like one that kept it.
- The mark on a driven tab is slightly transparent, so the site’s own favicon shows through it instead of being partly replaced.
- No new permissions.
1.35.0 ·a dropdown that did not take the value no longer reports success
- A choice is now read back from the control. When a custom dropdown was driven (by the agent through the bridge, or by the fill engine) the answer reported success and repeated the value that had been asked for. It said the same thing whether the widget had accepted the choice or ignored it, so a field that stayed empty came back as filled. The value is now read off the control after the list closes (which is also where a widget that treats Escape as cancel gives itself away), and the reply carries what the control shows, separately from what was asked.
- The same check reached three more places. A date picker that knew the field had not changed used to answer success with a note asking a human to look; a native slider was never read back, although a controlled one reverts and every range clamps to its own step; a rich-text editor measured whether the text stuck and then ignored the measurement. All four now report plainly when a value did not take.
- Nothing about this changes what the extension does on a healthy page; it changes what it says on an unhealthy one. No new permissions.
1.34.0 ·the tab mark became terracotta, with rounded corners
- The mark on a driven tab is calmer. It shipped magenta, chosen for pure visibility, and that turned out to be the wrong test: a contrast measurement cannot tell you whether something is bearable in your tab strip all day. It is now terracotta, the same colour the recording button already wears, so the extension marks both of its “something of mine is happening here” states in one colour rather than two, and the corners of the marks are softly rounded rather than square.
- No new permissions.
1.33.0 ·the tab says when the agent may act in it, and the audit runs the full rule set
- A tab the agent may act in now marks its own icon. Four magenta corner marks around the site’s favicon, the one place a browser lets a tab speak about itself, and the only one that keeps speaking while the tab is in the BACKGROUND, which is exactly when an agent is driving it and you are looking somewhere else. The logo is not shrunk, covered or cut; the marks sit on the outermost corners. The page itself is NOT marked: a border round your application would be our furniture in your room.
- The colour is not a verdict. Green, red and amber already mean passed, broken and warning in the findings list, so this mark takes none of them: «the agent may act here» deserves attention, not reassurance.
- Audit now runs axe-core. Pressing Audit used to give seven hand-written DOM checks while an agent asking through the bridge got hundreds of WCAG rules on the same page, while the person doing the work by hand got less than the robot beside them. It now runs the same rule set our site checker uses, and keeps the
incompleteresults as their own group: those are the rules axe could not DECIDE, which in an audit are the places a human has to go and look. - Your agent can work a canvas.
qa_click_pointandqa_drag_pointact at an x,y point instead of at an element: for Figma, Miro, maps, charts and diagram editors, where everything is drawn inside one<canvas>and there is nothing to name. Everywhere else keep usingqa_click: it names what it pressed and tells you when it missed, which a coordinate cannot. Honest limit, stated in the tool itself: these events carryisTrusted: false, so anything that checks it will ignore them. - The screenshot editor opens straight after a capture, the panel steps out of the way for the shot and comes back to it afterwards, arrows are drawn as brush strokes with a thickness you can set (Shift + wheel, or a number), and captions carry their own size.
- The popup stopped truncating. The line under each switch said what it does and then ended in an ellipsis; it now wraps to two lines of fixed height, so nothing jumps when you flip a switch, with the full sentence on hover.
- No new permissions.
1.32.0 ·switching the agent on for a site reaches the tabs already open on it
- The site was remembered; the tabs were not. Switching “Agent drives this tab” on has recorded the SITE since 1.19, so a tab that lands on it joins the session by itself. That was hooked to navigation, though, and a tab already sitting on the site when the switch was flipped was never re-examined: it stayed outside, showing an off switch. Which is exactly the case that matters, because a person spreads the app across tabs first and switches the agent on afterwards.
- Now the gesture reaches every tab on that site at once, in both directions, and switching off clears them the same way.
- Two more blind spots of the same kind. Tab numbers die with the browser while the remembered site does not, so after a restart the site was “on” and the session was empty until something navigated; and bringing a tab to the front (the moment you actually look at the switch) was never a checkpoint. Both are now.
- The key is still the site, not the domain. One deliberate gesture on
gms-app-dev.example.orgmust not quietly grantwww.example.orgas well, so a sibling host, the other scheme and a different port each stay separate. - No new permissions.
1.31.0 ·the full-page shot follows what actually scrolls
- On an application shell the picture used to be one screenful, and it looked correct. A portal with a fixed header, a fixed sidebar and the content in its own scrolling block does not scroll the document at all: asked how tall the page is, the document answers “one screen”, and answers truthfully, because that is its real height. The capture believed it, took one screenful and reported success. Found on a live admin portal.
- Now the shell is let out of its box for the capture. The height limits are lifted from the scrolling block and from every ancestor holding it, the whole page lays out in the document, and the ordinary path photographs it with the header and the sidebar in place: the page as it would print. Everything changed is inline style, remembered exactly and put back, down to the difference between “there was no style attribute” and “there was one”.
- If it cannot be let out, the block itself is photographed, full height, though some shells are held by something this cannot reach. The panel then says so, because a picture without the sidebar is correct and reads as broken when nobody mentions it.
- Which block counts as “the page” is decided by content, not by guesswork. The document is one candidate among the scrollers and wins when it has the most to show; a dropdown list and a chat sidebar scroll too, and photographing one of those instead of the page would be a confident wrong answer.
- Your agent gets the same fix:
qa_screenshotwithfull: true. No new permissions.
1.30.0 ·a picture of the whole page, marks on top of it, and screen recording
- The whole page in one picture. A new Screenshot tab in the panel scrolls the page, photographs it a screenful at a time and glues the parts into one PNG, so a ten-screen form comes back as one image rather than the first screenful. A sticky header is kept once, at the top, instead of being printed down the side. The browser only hands an extension the VISIBLE area; the full height needs the
debuggerpermission, which would put “read and change everything you do” in the install prompt for one convenience, so the page is photographed the way a person would do it. - Password and card fields are re-covered on every screenful. The masks are positioned at the coordinates of the moment they are drawn, so a set drawn once would drift with the scroll and a real password field two screens down would be photographed in the clear.
- Arrows, boxes and captions, drawn on the picture. Five colours, undo, and the marks are burned into the file when you save, not kept as a layer only Qiksy can see. Save sits inside the editor, so there is no going back to the panel to find it.
- Screen recording, to a file you can attach anywhere. MP4 with H.264: it plays in Slack, Jira, Teams, QuickTime and Windows’ own player with nothing installed, which WebM does not. Two minutes maximum, pause and resume, and the clock and the file’s real size sit on the button. Nothing is stored: Delete releases it, and so does leaving the page.
- Sound, both kinds. The screen’s own sound is a checkbox inside the browser’s picker. Your voice is the microphone, and because the panel lives in the page, the permission belongs to the site you are testing and is remembered per site. Refused, the take still happens without the narration and says so.
- While recording, the Qiksy button becomes the stop button: the panel is shut for the take, so the one control you need cannot live inside it. And in “record this tab only” the browser is asked to film the page and nothing painted over it, so Qiksy itself is not in the video at all. The panel says which of the two you got rather than promising it.
- Agents can take the full-page picture too:
qa_screenshotgainedfull: true, inqiksy-mcp1.30.0. Recording deliberately cannot be driven by an agent: both cameras open the browser’s own picker, and a person answers it. - No new permissions. Screen capture and the microphone are asked for by the browser, at the moment of use, with its own dialog.
1.29.0 ·the name says what it is
- Renamed to Qiksy Bridge. The listing had been named for what the product grew out of rather than for what people now install it for: a bridge between a coding agent and the browser you already have open, with your logins in it. Nothing about the extension changed.
- No new permissions.
1.28.0 ·it stopped restarting itself for no reason
- “Qiksy was updated, reload the page” about an update that never happened. Qiksy installs a waiting update by itself instead of leaving it to Chrome’s own five-hour schedule, and the guard on that asked one question (is anything in flight?) and never the other one: is there anything to install? So on every browser start, and twice a day after that, a quiet browser got the extension restarted with nothing downloaded and nothing to put on. A restart kills the content script in every open tab and, unlike a real update, tells us nothing, so nothing takes those tabs over: five seconds later each one asks to be reloaded, and reloading fixes it until the next start.
- Why it was one browser and not the other. A profile with an agent connected counts as BUSY, so the pointless restart was skipped there, and the browser that looked healthy was being spared by an accident of the same bug. A second profile, opened and closed through the day, met it every time.
- A restart now needs a downloaded version waiting. That is what Chrome tells us when it holds an update back because we are running, and it is the only thing worth interrupting every open tab for. Not knowing keeps its direction on each side: an unreadable session still counts as busy (a recording must never be cut short) while an unreadable mark counts as nothing pending, because a late update costs hours and a needless restart costs every open tab its panel.
- No new permissions.
1.27.0 ·the bridge introduces itself to the person, not only to the agent
- Connect the bridge and it now tells YOU what it can do. Everything it said about itself was written for the agent: verb names, gates, refusal ladders. A person who had just pasted a token into a config file got nothing and was left guessing what they had bought, while every capability in that list had existed for months and had simply never been named to them. Ten short lines, plain words, and deliberately not one verb name: verbs are the agent’s business, a person needs the SHAPE of what can now be asked for.
- In the language you chose in the panel. That is the one thing the server cannot work out by itself, because it never sees your conversation. But you had already answered the question by picking a language, so the extension now sends it along with every summary; it is not a guess on the agent’s side either. With no language set, the agent still knows which one you write in, and the instruction says so as the fallback.
- The agent’s own capability list caught up with the product. It carried neither screenshots, nor waiting, nor reload: all three shipped the same day and the list stayed yesterday’s.
- No new permissions.
1.26.0 ·so the corrected documentation actually shipped
- Not a line of extension code changed; the number went up so the docs could move. The agent guide and the package README travel INSIDE the npm package, and the copy published an hour earlier still said “the bridge has no screenshots”, which had stopped being true that morning. A claim that flatters us is worse than a missing capability, so it had to be RELEASED, not merely corrected in the repository.
- No new permissions.
1.25.0 ·six corporate component kits, and the engine change they exposed
- Kendo, Syncfusion, DevExtreme, SAP UI5, Fluent and jQuery UI. The registry’s 22 kits were a startup stack, while the person who pays for this usually sits in a bank’s or a logistics company’s internal admin. Every marker was measured on the library’s own demo rather than written from memory, because a row selector written from memory lies silently: the list reads EMPTY, the engine falls back to “take the first row”, and a field asked for “Net 30” gets “Prepayment”.
- jQuery UI is the one kit that needed the engine itself changed. It announces nothing: the menu is a
ul.ui-autocompletewith an empty role, its rows have an empty role too, and the text sits a level lower again. The other 24 kits present as a listbox with options; this one presents as nothing, so its rows were invisible. The engine’s row selector now knows it, tied to.ui-autocompleteon purpose, because a bare.ui-menu-itemis every jQuery UI menu on the page, the site’s own navigation included. - Two things each kit taught that could not have been guessed. Kendo re-uses its popup container instead of building a new one, so the container alone still matches ten minutes after the list closed, so the marker demands the LIST. SAP UI5 puts the very same
ul.sapMSelectListinline inside a CLOSED select, so the marker demands the popover. Fluent’s rows are buttons, not list items, so anything looking for anlifinds nothing. DevExtreme virtualises long lists, so there “value not found” can mean “it is further down”. - AG Grid and Salesforce SLDS are deliberately absent. Their doc-site examples would not open under seven attempted routes across three pages, and a marker nobody has actually seen is exactly the “from memory” the rest of this guards against.
- No new permissions.
1.24.0 ·pixels, patience, and a file where the page has nowhere to put one
- The bridge takes screenshots. “Measurements but no image” was written in the bridge’s description, in the agent instructions and in every session that repeated it, and it was true only of the BRIDGE. The product has photographed the screen since 0.1: step previews in History, the 📷 on a finding. What was missing was a verb that returns a picture AS a picture. It re-uses that same shot, so it inherits both of its properties: the Qiksy panel hides itself, and password and card fields are painted over BEFORE the frame is taken, so a screenshot pasted into a ticket carries no secret. One real limit remains: the browser photographs the ACTIVE tab, so a background tab cannot be shot, and the extension refuses rather than quietly handing you whichever tab happened to be in front.
- Waiting for an event instead of guessing. Every wait inside the extension is short and bounded (350 ms of quiet, 2500 ms hard stop) and only on a click or a press. An order confirmation that arrives six seconds later was invisible to every verb, so the agent polled the page, and each poll RENUMBERS every reference it was holding. It now waits on the change itself, not on a timer, because Chrome clamps timers in a background tab and a background tab is the whole point of this product. Two conditions are missing on purpose: “until it goes quiet” is what every action already does, and “until the network goes quiet” would measure only the page’s own requests in the top frame, a number that means less than it says. On a timeout it reports what it SAW: how many matched, how many are visible, the nearest names, anything announced.
- A file goes in where there is no file field at all. Some forms have nowhere to put one: the button opens the system window directly, and that window belongs to the operating system: outside the document, unreachable by any script, this extension included. The “Audio language” dialog in YouTube Studio is one of those, and an upload aimed at it silently went to the subtitle uploader instead, which is worse than a refusal. Now the page gets its answer BEFORE it opens the window. The normal route stays first always: a field is the honest place for a file.
- And that interception is armed for exactly one call. Patching the page’s own prototypes is interference in somebody else’s application: a site with its own wrapper around a file field would break, and a tool that hunts for defects must not add any. The originals come back the moment the offer is withdrawn, including when the click threw. Proven on a third-party product: after withdrawal the prototype is compared against an untouched one and matches.
- The recorder no longer treats anything with
aria-expandedas the owner of a layer. That attribute sits on any expansion (an accordion, a collapsible sidebar, a tree row) and on the whole left navigation of YouTube Studio, because a hamburger folds it. So the owner of an open dialog came back as a 240×793<nav>, which has no name, so the recorder fell through to its id, and then read three blocks of menu as the field’s values. A live recording came back as one step holding a menu. - A fill batch says what the form did next. A batch is a wizard step, the unit the agent actually works in, and “what happened after it” is exactly the question worth answering: a dependent list loaded, a section appeared, an error surfaced. One window per step, not per field, and it replaces the snapshot the agent would otherwise take next.
- The conditions the page was read under. The most expensive class of defect, “it only reproduces for him”, is almost never about the browser. It is about forced-colours eating a background, reduced motion carrying state, a locale whose date format the parser rejects, a timezone that moved a deadline to yesterday. All of it was free to read and simply never asked for. Page zoom comes from the worker, because inside the page it can only be inferred, and every inference mixes page zoom with display scaling.
- Library detection in one round trip instead of 51.
- No new permissions.
1.23.0 ·letters land in the field you clicked
- Project and Tags accept typing. The scenario naming card does not let the browser type into its own fields: it collects every printable key from the window and appends it by hand, because a field with two writers loses exactly one character at the moment the page grabs the caret back. There was one writer, and it wrote into ONE field: the name. So on a page that fights for the caret, clicking “Project” and typing sent every letter into the NAME: the field being looked at did not change, and the name grew behind your back. Reported on YouTube Studio: “focus does not work, you cannot add anything to project or to tags at all.”
- Both halves were needed, and either alone would have been useless. The defence against a stolen caret stood only on the name, and it is now on all three fields; and the key collector writes into the field the cursor is in, rather than into the name always.
- No new permissions.
1.22.0 ·Pro wears the studio’s own mark
- One crown instead of four different badges. The Pro mark had split into four: a violet gradient plate in the popup, a gold one in the panel header, a violet chip on every paid settings row and bare violet letters in the findings menu. Four versions of one thing read as four products, and not one of them looked paid. This is not a new drawing but the SAME crown and the same gold as everywhere else in Qiksy Studio, because a similar crown would be a different mark, so the path was copied rather than redrawn.
- Gold, not the accent colour, and that part matters. The accent says which SURFACE you are on, so a Pro badge painted in it changes colour between products and stops meaning one thing.
- The trial chip keeps its green. A countdown is a STATE, not a mark, and gold on it would promise a crown that has not been bought.
- No new permissions.
1.21.0 ·an update no longer asks you to reload the page
- The false “Qiksy was updated, reload the page” notice is gone. A minute after an update the worker adopts every open tab by itself, so there is nothing left to reload. The orphaned copy did not know that: its own poll removed the panel the NEW copy had just drawn (both hosts carry the same id) its own timer put the frozen host back, and on top of it all stood a notice that was no longer true. The dead copy now stops its own timers first, removes ITS OWN node by reference rather than by id, and only then, after a pause, asks whether anyone alive has adopted the tab. A heartbeat on the page answers, and only a script with a working extension connection can write it.
- The round button grows slowly on hover. 420 ms on a curve with no bounce; it was 240 ms and still read as a jerk.
- No new permissions.
1.20.0 ·the mark on the toolbar icon is gone for good
- The little square on the extension icon was ours, and nobody ever recognised it. It was asked about three times. Gold first: “what is that yellow square”. Recoloured so the glyph carried the colour instead of the fill: “what is that black square”. The problem was not the palette: Chrome draws a badge as a SOLID rectangle sized to its text, so a “small dot” does not exist on that surface in any colour, and at 16 px on a dark icon it is a square.
- Nothing was lost. A new version is still announced by the banner in the panel and the note in the popup, places with room to say WHAT changed, both of which go away once read. The badge was a third copy of the same message and the only one that could not explain itself.
- No new permissions.
1.19.0 ·recording is red around the whole window, and the scenario buttons hold one shape
- The recording frame is a warm red, and it goes all the way round. It used to be its own orange, matching nothing. Tying it to the error token was worse: that is the token Delete turns under the cursor, and a line around somebody’s whole application in the colour of deletion tells a lie about a healthy page. The red is now the tone of a camera’s tally lamp, because recording is an “on” state, not a warning. Its right edge used to be hidden whenever the panel was docked, because the frame was drawn at full window width while the panel stood over one edge; the dock genuinely narrows the page, and the frame now narrows with it.
- The scenario buttons stopped rearranging themselves per language. Four labelled buttons need 336 px in English and 370 in Russian, and the card gives 319, so the row wrapped in a DIFFERENT PLACE in each of the seven dictionaries, and Polish landed at exactly 319 and did not wrap at all. A wrap that depends on the translation looks accidental in every language. The shape is now fixed: Run across the full width (the thing the card exists for) and three below it. The longest dictionary fits with 60 px to spare.
- Delete keeps its icon and no word. An icon has no translation, it is the one thing that fits everywhere, a bin is the least ambiguous sign in an interface, and the press is undoable. Its name lives in the accessible label and the tooltip.
- No new permissions.
1.18.0 ·agent control is remembered per SITE
- Switch it on once and the next tab of the same application connects itself. The “drive” consent was already asked once and kept; what was rebound every time was the TAB, and a tester spends the day on one application. The key is the ORIGIN, and that is the one thing separating this from “on everywhere”: collapsing it to the registered domain would mean a single deliberate gesture on a dev host silently opened the whole company network.
- The badge on the icon carried the colour on its glyph. A short-lived attempt to make the “there is news” mark readable: Chrome fills a badge as a solid block, so gold on the background became a gold block. It was removed altogether in 1.20.0.
- No new permissions.
1.17.0 ·a page region is no longer a Next button
- Two recorded steps came back with the whole main content area cast as “Next”. The run could not find it by name, the fallback by selector handed back the entire region, and the “is this an upload?” question asked whether there was a file input ANYWHERE inside it, and found somebody else’s, elsewhere on the page. The person was told to attach a file to a field they had never recorded.
- The cause was one attribute read backwards.
tabindex="-1"means the page has taken the element OUT of the keyboard order; most often it sits on the target of a “skip to content” link, which is to say on any accessible site. The recorder only asked whether the attribute was present, so a click on empty space, the way an open calendar is dismissed, became a step boundary. Four fixes:tabindexcounts as a button only at 0 or above; a name equal to a block’s id is not a name; the selector fallback must return a control; and the upload question is asked about the control itself, not about everything beneath it. - No new permissions.
1.16.0 ·the lightning bolt is back where it belongs
- The icon. It was rotated in a passing edit on 29 August and 1.15.0 carried that to the store: at 16 px the bolt stopped being the tail of the Q, leaving a ring with an empty notch and a yellow smudge beside it. The exact four files from 1.13.0 are back, byte for byte, so the store page matches again. It slipped through because the two packages were compared by their FILE LIST rather than by their bytes.
- The scenario card stopped overflowing. Its “steps · pages · host” line was fixed-size, which was right while it held two short counters and wrong once a long host was appended. Letting it shrink was worse, because both halves shrank and produced two ellipses and not one readable fact, so it is two lines now, each with its own room. And “1 steps” is gone: a separate key for the singular in seven languages is cheaper and more honest than a declension engine for the one counter that is ever 1.
- No new permissions.
1.15.0 ·you can see that recording is on from anywhere on the screen
- A frame around the window while recording. Until now the only sign was a pill INSIDE the panel, exactly where a person is not looking: they are in the application, filling a form, with the panel closed or off to the side. A recording nobody can see gets left on for an hour, and then the scenario is named after everything that happened to occur. A frame at the edge of the window cannot be missed and cannot be mistaken for part of the application. It never takes a click, never moves the page, and lives inside our own host, so the screenshot code that hides our interface keeps it out of pictures, and full screen takes it away along with everything else of ours.
- The recording colour is its own token and deliberately not the accent. The accent changes with the theme and with your choice, and a warning that can be repainted into furniture stops warning.
- No new permissions.
1.14.0 ·scenarios that survive a real application
- A standing panel is no longer mistaken for an open menu. A popup used to be recognised by how it is drawn (positioned, above the page, big enough for choices) and a work-item panel in Jira answers yes to all of it while never having opened. One click inside it produced three separate lies in the same recording: the field took the name of a sprint chip nearby, the value typed into it was thrown away as "already recorded by whoever owns the layer", and the panel’s own rows were written down as the rows of a menu. A layer now counts only if it was NOT on screen when the press began.
- A run begins where it was recorded. The starting page was stored and never used, so Run filled whatever happened to be on screen: invisible on the recorded page, and on any other one it failed on step one looking for controls that were never there. It goes to the recorded page first, matching by page rather than by exact address so a live session is not swapped for the dead one written down, and only within the host shown under the scenario’s name.
- A field inside a frame is named, not silently missed. Qiksy runs in the top document only, a deliberate permission decision. Anything inside an
<iframe>(most payment boxes, some embedded editors) is therefore unreachable, and until now that was silent: nothing recorded, and a run that stopped blamed the page. Recording says it the moment the frame takes focus; a stopped run says which wall it hit. - The update notice tells you about THIS release. It said "Updated to v1.13.0" and then listed 1.12.0’s changes. The teaser is now tied to a version, the build fails if they disagree, and the panel falls back to a neutral line rather than repeating another release’s news. The extension also installs a waiting update itself, but never while a recording, a scenario or an agent is in flight.
- No new permissions.
1.13.0 ·scenarios, and four things that lied about their own work
- Scenarios. Record a form flow once and replay it with one press: across the wizard’s steps, across pages, and across sites. It stops at every page boundary and waits for you, stops at the first field the page no longer has and NAMES it, and records passwords and card numbers as fields rather than values. See Scenarios.
- A menu with no library behind it is drivable now. Rows used to be found by class name, so a picker built from anonymous divs (most hand-rolled ones) read as an empty list and the widget was reported as “did not respond”. A row is now recognised by shape: a leaf of the layer that just opened, and there is more than one of them.
- A read-only box that opens a list is a control, not a caption. Everything read-only was dropped by the scanner, which is right for a displayed account number and wrong for the commonest way the web builds a select. The field was skipped SILENTLY and the form still counted complete.
- “Filled 2 · skipped 2” now names the two. A count you cannot act on, in front of a form you have to submit, is how a person takes a half-filled form for a finished one.
- Validate stopped clearing fields it never checked. It looked for a red line “near” the field, and “near” could be the wrapper around the whole form, so one field’s genuine error vouched for its neighbours and a form with two unguarded fields came back silent. A false all-clear is the worst direction for a check.
- The bridge’s fast pick takes the row from THIS control’s list. With a suggest box left open elsewhere, an exact match there beat the right match here, and the value went to a field nobody named.
- No new permissions.
1.12.0 ·the panel finishes the sentence
- The language switch now reaches the whole panel. 1.11.0 translated the titles and left the explanations: every hover tip, the paragraph under each tool, the empty states, the toasts and the placeholders stayed English, so a Spanish tester got a Spanish panel that explained itself in English. 171 strings moved into the dictionary, and a test now reads the SOURCE rather than the dictionary, because the one thing a completeness check cannot see is a string that was never a key.
- Your app’s language, not ours. Four word lists decide what sends a form, what closes a modal, which dropdown rows are placeholders and which field wants a date, and they knew English and Russian. On a German or Spanish form the tool could not find the page’s own Save button, silently, because from outside that looks like “this page has no form”. All four now cover the seven shipping languages.
- A calmer panel. Twenty-five muted accent colours instead of eight saturated ones, and the colour you pick now tints the whole sheet rather than four controls. The light theme is warm paper instead of blue-white, and its shadows are a hint rather than a hole; the dark theme is a neutral grey. The default is still the grey that asks nothing of you.
- Our own contrast, measured. The most muted text on the light theme sat at 3.4:1, under the 4.5 this tool flags on other people’s pages. Fixed, in the two places it appeared.
- Findings, session notes and the client report stay English by design. No new permissions.
1.11.0 ·the panel speaks your language
- Seven languages, in the panel itself. English, Deutsch, Español, Polski, Português, Русский, Українська. Qiksy picks yours from the browser the first time it runs; the popup has a switch when your machine is in one language and your team works in another.
- Findings stay in English on purpose. They are copied into a ticket that a developer in another country reads, so the tool is translated and its output is not, the same split DevTools makes.
- Field recognition learned four more languages. A label that says
Kod pocztowy,Firmenname,TeléfonoorSobrenomeis now understood as a postcode, a company, a phone and a surname. And a bug that had been there from the start: plain RussianИмяwas never recognised, because a JavaScript word boundary only knows the Latin alphabet. - Two false failures fixed. A consent checkbox whose label contains the word “terms” was being sent down the dropdown route and then reported as unfilled, while the box was, in fact, ticked. And a hand-rolled combobox that shows its value as plain text was called wrong and “repaired”, which wrote text into a element that cannot hold it. Both now judge by what is on the screen.
- No new permissions.
1.6.0 ·the accessibility audit stack
- axe-core, inside. The industry-standard accessibility rule engine (the one under Lighthouse) now ships bundled in the extension and runs locally via
qa_axe: hundreds of WCAG A/AA rules, every violation with impact, WCAG tags and the offending selectors. Nothing leaves the page. - The keyboard route, walked for real.
qa_focus_walkpresses Tab step by step and reports where focus actually lands: the controls a keyboard user can never reach, suspected focus traps, and focus jumps that fight the layout. Everyqa_pressreply now names the element that truly holds focus. - Silent toasts are a finding now. After every click the bridge watches the page's live regions and reports
announced: a toast that appeared for the eyes and said nothing to a screen reader (WCAG 4.1.3) is caught by machine, not by luck. - Popup polish: a custom scrollbar and softer rows.
- No new permissions. Everything above is local analysis over the existing loopback bridge.
1.3.0 – 1.5.0 ·the fill engine stopped guessing
- A select takes the option you NAMED (exact, then prefix, then contains) and refuses with the real list instead of grabbing the first row; a multi-select counts its chips instead of trusting class names.
- A form that grows while it is being filled (gated sub-forms) is refilled as it grows; the fast
rawroute writes parseable values (dates, times, ranges) in tenths of a second instead of driving the popup. - Ant Design range pickers, time pickers, cascaders and trees driven reliably;
qa_pick_rangetypes both ends and verifies each against its own input. - The manual ⚡Fill narrates on its card instead of drawing rings over every field.
1.2.0 ·connect your browser with one button
- Pairing from the Qiksy web app. Qiksy Route (and the studio) can now ASK the extension to connect: press Connect this browser in its settings and the extension opens its own confirmation page naming the site, the local port and exactly what is granted. Only your click there enables anything; a website can never switch it on by itself.
- No token to copy on that path. The manual popup route stays for everyone who prefers it, and for clients we do not ship.
- The confirmation is honest. It checks your licence before promising anything, its buttons disappear once answered (so a refusal cannot be undone by a stray click), and only our own origins may ask, at most one dialog every few seconds.
- Windows and Linux fixes in the local engine (
[email protected]): PATH is no longer mangled on Windows,stopcan no longer kill an unrelated program that happens to use a similar port, and machines withoutlsof(containers, slim servers) are handled. - The bridge package (
[email protected]) now carriesAGENT-GUIDE.md: drop it into a project and any assistant knows the tools and the limits.
1.1.x · many agent windows, one browser
- Open a second or third editor window and they all reach the same browser through the same toggle: the first process holds the port, the rest relay through it. Nothing to configure, no second token.
- Whole-page accessibility snapshot (
qa_snapshot) with stable refs, so an agent can target a control that no CSS selector describes uniquely. - Agent control (Pro): click, type, press, navigate, open an isolated login, behind a licence and a separate one-time consent, re-checked on every call.
1.0.x · the first public build
- Form scanning and one-click fill (valid / edge / invalid), live findings, page audit, spotlight, copy-as-bug.
- Multi-login isolation: several sessions on one domain, in one browser.
- The MCP bridge: your own coding agent reads findings, forms and network from the page you are looking at.