> ## Content Index
> Fetch the complete content index at: https://vill.ar/llms.txt
> Use this file to discover other available public pages before exploring further.

# The death knell for narrow-scoped apps
- URL: https://vill.ar/the-death-knell-for-narrow-scoped-apps/
- Published: 2026-10-30T11:42:13.000Z
- Updated: 2026-10-30T16:42:42.000Z
- Description: Anyone sufficiently technically inclined can now build whatever they want. So when I noticed our QA team using a very capable platform for one narrow job, we built them a tool for exactly that job. Here's why, what it's made of, and how AI does the paperwork.
- Author: Mike Villar

I've stopped paying for plugins. I've stopped paying for themes. I've mostly stopped paying for the kind of specialist apps that do one thing, charges per seat for it, and sends me a "we've updated our pricing" email every March.

I run a [digital agency](https://growth-rocket.com/?ref=vill.ar), and I'd happily pay for something I can't make. The trouble is that the list of things I can't make has become very short, and the list of things I can make before lunch has become long and slightly embarrassing for a lot of software companies.

The latest example is Luke, a testing tool we built for our own QA team. It went from an empty folder to a tagged production release in under 4 hours. I'm not a developer. I didn't write a single line of it.

![Luke's Test screen showing National Geographic in three device frames with a QA toolkit panel on the right](https://vill.ar/content/images/2026/10/luke-test-screen.jpg)

Luke's Test screen, with National Geographic open on an iPhone 17 Pro, a Galaxy S24 and an iPad Pro 11, scrolling in sync. Each frame is a real browser engine running on our server. The amber note and the fps counters will come up again later.

## Welcome to the next Anthropocene

The Anthropocene is the idea that humans became the dominant force shaping the planet. What we're in now looks like the next one, except the force is AI and the thing being reshaped, for now, is how knowledge work gets done. Geologists will have opinions about that sentence. They can file a ticket.

If your work involves even a modicum of tech, you don't get to watch this from the bleachers. An agency like ours sells expert hours, and the price of a lot of expert hours is falling through the floor. So I've been aggressively reassessing how we work: every tool, every workflow, every line on the software bill. I wrote about some of that in [my dev stack as a non-developer](https://vill.ar/my-dev-stack-as-a-non-developer/), and in [the redesign of our own site](https://vill.ar/trusting-vibe-coding-with-a-major-redesign-project/).

One pattern is painfully obvious:

## Narrow-usecase apps are practically dead

Anyone sufficiently technically inclined can now build more or less whatever they want. I don't mean a prototype, or a nice demo for the all-hands. I mean the thing itself, with sign-in, roles, emails, CI and release notes.

A WordPress plugin, a premium theme, a specialist SaaS with one good idea: most of these can be reverse engineered in a couple of minutes, adapted, and made your own. Point an agent at what the product does, describe what you actually need, and throw away the 80% of the feature list that exists to justify the enterprise tier. What comes back fits your team, because nobody else's roadmap got a vote.

Here's my rough test for whether a piece of software is in trouble. Take away its logo and ask what's left.

- **A real moat:** a network, proprietary data, physical infrastructure, regulatory cover, someone to sue. You'll keep paying, and you should.
- **A form, a database and a nice empty state:** you are paying rent on a weekend project.

Most of the "tools" budget at most companies sits in that second bucket. Vendors can feel it too, which is why every one of them has spent the past two years stapling a chat box to the sidebar.

## The audit that started it

I noticed our QA team was paying for multiple [BrowserStack](https://www.browserstack.com/?ref=vill.ar) subscriptions. BrowserStack is a very capable platform: more than 35,000 real devices by their count, native app testing, a serious automation product. It sits squarely in the first bucket, and nothing in this post changes that.

The gap was on our side, because we were underutilizing it. The team mostly used it to check for issues in the client websites and web applications we deploy and manage, across different viewports. That's one small corner of what the platform can do, and we were paying for all of it.

That's no knock on anyone. The team used what their job called for, and a platform that broad is built for far more companies and far more kinds of testing than ours. So I decided to make the team their own narrow-scoped tool. We looked at what they actually did all day and built that, on open source browser engines.

## Meet Luke

Luke is Growth Rocket's internal Multi-Viewport Testing Hub, because I refuse to let even internal tools be boring. 

A quick note on the screenshots. Everything below comes from a throwaway demo instance pointed at public websites, not at anything we build or manage. None of these sites are clients, and none of them had any part in this. The findings you can see are automated heuristics, so please don't read them as a review. Stripe will survive my tap-target linter.

### Real engines, in real-looking devices

The core of Luke is the Test screen. Pick a page and up to three devices, and you get a fully interactive session in each one: scroll, tap, type, rotate, toggle dark mode, throttle the network, with the panes optionally synced. Behind every frame is a real browser on our server, either [WebKit](https://webkit.org/?ref=vill.ar) or [Chromium](https://www.chromium.org/?ref=vill.ar), driven by [Playwright](https://playwright.dev/?ref=vill.ar) and streamed to the tab. When you just need a quick look, Fast preview does a lighter version of the same job through a rewriting proxy and an iframe.

The viewports sit inside realistic device photos, because a bare rectangle tells you nothing about notches, or how much of the screen the browser's own UI eats. Those photos were generated with [fal](https://fal.ai/?ref=vill.ar), cut out with [BiRefNet](https://github.com/ZhengPeng7/BiRefNet?ref=vill.ar), and measured by a script so the page lands where the screen is. They're AI-generated rather than official renders, and accurate to within about 3% of the real screen proportions.

![Luke's launcher with a URL field and a grid of device cards, iPhone 17 Pro and Galaxy S24 selected](https://vill.ar/content/images/2026/10/luke-device-picker.jpg)

One card to choose the page and the devices. Search understands model names and sizes, so "390" works.

There's also a console and network sheet beside the devices, which catches everything the page logs on each one, including the mess from other people's ad tags and analytics.

![A side sheet listing console errors and failed requests per device next to the live device frames](https://vill.ar/content/images/2026/10/luke-console.jpg)

Console and network, per device. Most of what's in here belongs to third-party scripts, so Luke only counts first-party exceptions as issues.

### The device list maintains itself

Ours starts from [Playwright's device descriptors](https://playwright.dev/docs/emulation?ref=vill.ar), adds a curated set of desktop sizes, and flags new models for an admin to approve. Then it pulls updated market share, tells you how much of the market your devices cover by screen width and region, and suggests the smallest set that gets you to your target. 

![A table of phone screen widths with market share bars and the devices that cover each width](https://vill.ar/content/images/2026/10/luke-coverage.jpg)

Coverage, using worldwide figures for September 2026\. Every width has a list of the devices that cover it.

### Eight devices, one click

Screenshot runs are the workhorse. Choose pages and devices, and Luke captures all of them in the background, in frames, with animations settled and lazy-loaded content scrolled into view so the results are repeatable. Runs can also fire on a schedule, or from a deploy webhook so a release checks itself.

![Linear's homepage in four phone frames and two larger frames, with finding counts under each](https://vill.ar/content/images/2026/10/luke-screenshot-run.jpg)

A screenshot run on Linear's homepage across phones, a foldable, a tablet and desktops. Each device reports what the checks found.

### The boring stuff a machine should be catching

Every capture gets linted. Luke measures tap targets, tiny or clipped text, overlapping controls, fixed overlays hogging the screen, leftover placeholder copy, upscaled images, disabled zoom, layout shift and whatever is causing horizontal overflow. [axe-core](https://github.com/dequelabs/axe-core?ref=vill.ar) runs the [WCAG](https://www.w3.org/WAI/WCAG21/quickref/?ref=vill.ar) A and AA accessibility checks alongside it. Each finding carries the measured size and the CSS selector, so a QA person can overrule the robot with evidence, which is the correct power dynamic.

![Stripe's mobile homepage on the left and a list of 53 automated findings on the right](https://vill.ar/content/images/2026/10/luke-findings.jpg)

Findings on Stripe's homepage, pinned to the screenshot. The linter is strict by design: a 20-pixel-tall text link counts, and a human decides whether it matters.

### Check every width

This is my favourite. Layout bugs love to live between the sizes on your device list, so Check every width renders a page at every step from 320 pixels up to wide, then tells you exactly where it breaks.

Exhibit A is the original 1996 [Space Jam website](https://www.spacejam.com/1996/?ref=vill.ar), which is somehow still online and still has no idea what a phone is. It breaks at 12 of 29 widths and comes back clean from 800 pixels up. For a site that's old enough to have a mortgage, I consider that a passing grade.

![A width check of the 1996 Space Jam site: a red striped bar from 320 to 760 pixels, then thumbnails of the page at each width](https://vill.ar/content/images/2026/10/luke-width-check.jpg)

The verdict comes first, then a strip with one cell per width, then the renders. Striped means broken.

### What changed since the last time it was right

Approve a capture and it becomes the baseline. Later runs get diffed against it with [pixelmatch](https://github.com/mapbox/pixelmatch?ref=vill.ar), nearby changes are grouped into regions, and the size of the change sets the severity. A news homepage is the worst possible page to use this on, since it's different by design every time you look. So naturally that's what I used for the screenshot.

![A slider comparing two captures of the BBC News mobile homepage, with a finding that says the page looks 60 percent different](https://vill.ar/content/images/2026/10/luke-baseline-diff.jpg)

BBC News against its baseline, minutes apart. The baseline on the iPhone SE had an ad slot that was gone in the later run, which pulled the whole page up, so it reads 60% different. The other two devices in the run matched exactly.

### AI does the paperwork

Spotting a bug takes a trained eye. Writing it up afterwards mostly means describing a screenshot to a developer who could have just looked at the screenshot. That's the part of QA I wanted gone, so Luke does the capture and the first write-up in two places.

**When a run captures screens.** Every screenshot run ends with an AI review that nobody has to ask for. A vision model looks at the captures, knowing the device and what the automated checks already found, and reports what it can plainly see: cut-off text, colliding elements, stretched or missing images, content hiding behind a fixed bar. Each observation lands on the run as a finding, with a severity and the region of the screenshot it refers to, and anything the model is less than 60% sure of gets dropped. If a page has moved since its baseline, it explains what changed and whether that looks deliberate. Then it writes a short summary for the person testing by hand: the worst problems first, which devices they hit, and what to check next.

Its instructions are firm: "Do not report design preferences, brand choices, or anything you cannot see." I'd like that on a poster in every agency in the world.

**When a tester captures a screen.** Press Report on any device, and Luke freezes what you're looking at and takes a sharp capture. Mark up the problem with arrows, boxes, pen, text or redaction, then press Draft with AI. The model gets the marked-up screenshot, close-ups of each marked area, every mark described in words, the device and page, the automated findings and the console errors. Back comes a title, a description split into What happens, What was expected and Where to look, and a severity.

The marks steer it. It's told to report only what they point at, to leave blacked-out areas alone instead of guessing what's underneath, and never to invent steps it can't support. Nothing gets filed on the model's say-so, either: the draft fills in the form, and the tester reads it, fixes it and saves it. Typing a title also checks for open issues that read the same, so one bug doesn't get filed five times.

Sending every screenshot to a vision model would get expensive quickly, so there's a bouncer on the door. [Jev, the System One model I wrote about previously](https://vill.ar/the-ai-you-need-doesnt-always-need-to-be-generative-implementing-a-system-one-model-in-our-projects/), first decides whether a capture probably has a visible problem the automated checks would miss, and most of the spend only happens after that. Every project can have a monthly budget, and admins see what was spent by model, purpose, project and person.

![Luke's report screen with an annotated screenshot of the Space Jam site beside a filled-in issue form](https://vill.ar/content/images/2026/10/luke-report-issue.jpg)

The describe step, with the marks still in view and Draft with AI at the top of the form. This demo instance had no model connected, so this write-up was typed by hand. Severity set to minor out of respect for the elderly.

![Luke's report screen with a marked-up screenshot of the Space Jam site and a title, description and severity drafted by AI](https://vill.ar/content/images/2026/10/luke-ai-draft.png)

The same report with a model connected. Draft with AI took about 23 seconds to fill in the title, description and severity, and it picked out the tiny red footer text inside the box on its own.

Issues live inside Luke and export to Markdown, CSV, PDF or ZIP, or go out by email. And for the audience who pays the bills, any run, width check or issue list becomes a share link. A client view shows screenshots and plain descriptions. A developer view adds console errors, user agents and automated findings. Links can expire, can be revoked, and count their views.

![A light-themed shared report showing National Geographic on four phones](https://vill.ar/content/images/2026/10/luke-client-share.jpg)

The client view of a shared run: a clean, branded report with no app around it.

### The bits that only make sense for an agency

Once it's your software, you stop asking "does the product do this?" and start asking "why doesn't it?". A few things that are specific to how an agency works:

- **Projects.** Each client site gets its pages, staging credentials (encrypted at rest), screenshots, issues and activity in one place.
- **Insights.** Findings get counted across runs and sites, so the same broken pattern on several sites shows up as one problem to fix properly.
- **Everything a grown-up app needs.** Google and email-link sign-in, four roles, invites, notifications, an activity log and branded emails for every event. I mention it because this used to be the part that took months.

![A grid of project cards including BBC News, the 1996 Space Jam site, Wikipedia, Apple, Stripe, Notion, Linear, National Geographic and Craigslist](https://vill.ar/content/images/2026/10/luke-projects.jpg)

Projects is the home base. In production these are client sites. In this demo, it's whatever I felt like poking.

![An Insights page listing rules, selectors and the sites they appear on](https://vill.ar/content/images/2026/10/luke-insights.jpg)

Insights, counted rather than guessed: the same rule on the same CSS selector across sites. Craigslist and Space Jam are united in not declaring a language.

## The stack

All of it was built by agents running through [Paseo](https://paseo.sh/?ref=vill.ar), working from a plan I reviewed. It's a pnpm monorepo in TypeScript, about 18,000 lines so far, on its fifth release.

- **App:** Next.js 15, React 19 and Tailwind CSS 4, with Zod for validation and Lucide icons. Type is Sora and Geist, carried over from the Growth Rocket identity.
- **Rendering:** [Playwright](https://playwright.dev/?ref=vill.ar) running Chromium and WebKit behind a Fastify service, live frames over WebSockets (Chromium's come from the DevTools Protocol screencast), and sharp for images.
- **Jobs:** BullMQ on Redis for runs, schedules and maintenance.
- **Data:** PostgreSQL with Drizzle. Duplicate issue hints use pg\_trgm, because trigrams were enough and embeddings would have been showing off.
- **Checks:** axe-core and pixelmatch.
- **AI:** Claude, OpenAI, Kimi and GLM sit behind one role mapping, with [Jev](https://typesafe.ai/?ref=vill.ar) as the gate. The device photos and logo came from [fal](https://fal.ai/?ref=vill.ar) and [Ideogram](https://ideogram.ai/?ref=vill.ar).
- **Sign-in and email:** Auth.js and [Resend](https://resend.com/?ref=vill.ar).
- **Shipping:** Docker Compose with hard CPU and memory caps, GitHub Actions for CI and tag-driven deploys, images on the GitHub Container Registry, and Caddy in front, all on a Hostinger VPS we were already paying for.

## Where Luke loses

This wouldn't be much of a hot take if I pretended the trade was free, so here's what we gave up.

**It's emulation.** Playwright's WebKit on Linux is close to iOS Safari, but it isn't iOS Safari. Fonts differ, 100vh and toolbar behaviour differ, scrolling physics differ. If a bug only shows up on a specific physical Samsung on a specific OS build, Luke won't see it. There's no native app testing and no real hardware. That's where a platform like BrowserStack earns its keep, and if your work depends on real devices, you should be using one.

**It's not always smooth.** Remember that amber note in the first screenshot? WebKit renders in software on our box, so live scrolling on a heavy page runs at around 7 to 13 frames per second. Chromium does better and the screenshots don't care, but nobody is going to mistake it for a phone in their hand.

**And now it's ours.** When it breaks, there's no support line and no one else to blame. The usual argument against building is that maintenance eats you alive. That argument was written in a world where maintenance meant a developer's salary. For me it now means an agent, a release process and a ledger of known gaps. Ask me again in a year, but I like my odds.

We built Luke for what our QA team does most of the time, which is checking layout and behaviour across viewports. For that, emulation is good enough. The rare bug that needs a real handset is still a job for a real handset.

## The part where this gets uncomfortable

I've been smug for a few thousand words, so let me close by pointing the gun at my own foot.

Everything I just said about narrow software applies to narrow services. If a client can take away our logo and find the agency equivalent of a form, a database and a nice empty state, they will do to us what I just did to a subscription line. The agencies that come out of this era will be the ones with something an agent can't reverse engineer in a couple of minutes: judgment, taste, accountability, and a track record of being right when it counted.

That's why I keep doing this. Saving on a software bill is the nice side effect. The real return is practice: every tool we build ourselves teaches the company how to work in the world that's coming, while there's still time to be early.

The plugin vendors can keep their March emails.