Skip to content

One headless browser: ps-qa drives chuzz, and qa-inspect-host goes - #19

Merged
pathscale merged 9 commits into
masterfrom
feat/target-controls-with-no-area
Sep 9, 2026
Merged

One headless browser: ps-qa drives chuzz, and qa-inspect-host goes#19
pathscale merged 9 commits into
masterfrom
feat/target-controls-with-no-area

Conversation

@pathscale

@pathscale pathscale commented Sep 8, 2026

Copy link
Copy Markdown
Owner

There were two headless browsers. ps-qa drove qa-inspect-host, chuzz is what ships, and the web platform existed in only one of them.

URLSearchParams, matchMedia, storage, the observers and performance.getEntriesByType come from chuzz's document loader. A host without them blanks every routed page before its first render — @solidjs/router destructures the result of performance.getEntriesByType during setup — so this harness could not reach a single site in the fleet, and every gap closed for the browser had to be closed a second time here, by hand.

The host is a mode of the browser now: chuzz-headless, in pathscale/chuzz#37. It loads through the same loader and the same engine a tab uses and serves this protocol over the same socket. ps-qa still links no renderer, and the core job's dependency-tree gate still proves it: that constraint was about the socket, never about which repository the host lives in.

--host was always a path, so nothing in ps-qa needed changing to point at it.

Also here

ps-qa click could not be used against a headless host. It read frame metrics either side of the action, as context, and failed outright when the host said it had none:

inspector returned unsupported: the headless page serves diagnostics Capture, Snapshot and WindowComposition only

A host that owns a document and no compositor is telling the truth there, and the one thing that could drive a built site could not be clicked. The numbers are optional where they are only context now, and the command says the host has none rather than printing zeroes. Where they are the point — idle, frames, drift, blink — nothing changes and the refusal still fails the command, because a zeroed reading from a host that never painted would pass every one of those while proving nothing.

Four earlier commits on this branch that had never been pushed, all from driving the @pathscale/ui sweep:

  • feat(ps-qa)! type before pressing, and target a control the renderer laid out but did not paint.
  • fix(ps-qa) the descriptor is the line that looks like one. The page shares the host's stdout, so one console.log at startup was read as a path and the component was reported as never having started — wrong about the host and about the component. Found on ThemeColorPicker.
  • Two qa-inspect-host commits that the deletion supersedes. The substance of one, replacing a field's text by byte arithmetic rather than by a selection that needs a font catalogue, is carried into the new host in feat(headless): ps-qa drives chuzz, so there is one browser chuzz#37.

What this costs

The host CI job goes with the crate. It was the one place ps-qa drove a live page end to end, and it cannot be rebuilt here: this workspace may not link an engine. That coverage moves to chuzz, where both halves exist; pathscale/chuzz#37 carries the fixture and the socket test.

The published qa-inspect-host 0.1.12 stays on the registry, so the @pathscale/ui component sweep keeps working until it is pointed at the new host.

Verified

chuzz-headless serving support.cafe's built dist, driven by this ps-qa over the socket: find reports 4 buttons and 9 links with real geometry, audit reports 3 audited and 0 faults, clicking "Switch to light mode" renames the control to "Switch to dark mode", and clicking "Log in" leaves the home page and loads /login.

🤖 Generated with Claude Code

@pathscale pathscale changed the title One headless browser: delete qa-inspect-host, point ps-qa at chuzz One headless browser: ps-qa drives chuzz, and qa-inspect-host goes Sep 8, 2026
meh and others added 4 commits September 8, 2026 14:41
`ps-qa click` reads frame metrics either side of the action, as context.
A headless host owns a document and no compositor, so it answers
"unsupported", and the whole command failed with

  inspector returned unsupported: the headless page serves diagnostics
  Capture, Snapshot and WindowComposition only

before the click was ever dispatched. The one thing that could drive a
built site could not be clicked.

The numbers are now optional where they are only context, and the command
says the host has none rather than printing zeroes. Where they are the
point -- `idle`, `frames`, `drift`, `blink` -- nothing changes and the
refusal still fails the command: a zeroed reading from a host that never
painted would pass every one of those while proving nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It was a second one. `ps-qa` drove it, chuzz is what ships, and the web
platform existed in only chuzz: `URLSearchParams`, `matchMedia`, storage,
the observers and `performance.getEntriesByType` are supplied by chuzz's
document loader, and a host without them blanks every routed page before
its first render. So the harness measured a browser nobody uses, and every
gap closed for the browser had to be closed a second time here, by hand.

It is `chuzz-headless` now, in pathscale/chuzz, loading through the same
loader and the same engine a tab uses and serving this protocol over the
same socket. `ps-qa` still links no renderer, and the `core` job's
dependency-tree gate still proves it: that constraint was about the socket,
never about which repository the host lives in.

`--host` was always a path, so nothing in `ps-qa` needed changing to point
at it.

What goes with the crate is the `host` CI job, which was the one place
`ps-qa` drove a live page end to end. That coverage moves to chuzz, where
both halves exist, rather than being dropped: pathscale/chuzz#37 carries the
fixture and the socket test.

The published `qa-inspect-host` 0.1.12 stays on the registry, so the
`@pathscale/ui` component sweep keeps working until it is pointed at the new
host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The documented layout puts `ps-qa.ron` beside the checks, and the check glob
read it as one: every run failed with "Expected opening `[`" at line 5,
pointing at the profile's syntax, which is correct, rather than at the file
being included by mistake. The layout the documentation describes could not be
used.

Found writing the first site's checks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Publishing is per-crate and triggered by the version, and a site's CI installs
the driver from the registry. Without this the documented check layout still
fails there: `ps-qa.ron` beside the checks is read as a check group and every
run stops with "Expected opening `[`".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pathscale
pathscale force-pushed the feat/target-controls-with-no-area branch from 9a0b13f to b1bcc97 Compare September 8, 2026 07:41
meh and others added 5 commits September 8, 2026 16:50
Every site's `qa.yml` sets `QA_TIMEOUT_SCALE` in the job environment and
nothing read it, so all eleven of them would have run a shared runner
against the strict local latency contract. The knob was `--timeout-scale`
and only that.

Found on nofilter.io, where it is not a runner-speed nicety: every
navigation reloads and re-executes a 1.8MB bundle, so arrival takes
about a second of engine time and the 900ms default gave up, went home,
and reported the surface as unreachable. The flag still wins when both
are given.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An outcome poll narrows to the pane the action happened in, so a settling
check does not serialise a large tree every turn. It probed the full document
once when the scoped verdict said the result was absent, and never again.

That made it permanently blind to anything outside the pane that arrived
after that single probe. A sign-in is exactly this shape: the credential is
accepted, the account page replaces the pane, and the shell's header swaps a
Login for a Logout. The probe was taken before the swap, so the poll stayed
scoped for the rest of its deadline.

Measured on honey.id against auth-dev: thirty seconds of polling never saw
`button:Logout`, and one fresh snapshot taken a second after the harness
disconnected found it at once. The check now passes in three.

The one-shot becomes an interval. Between probes the stability samples stay
scoped, which is what the scope was for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`inventory` classified a control by its DOM id before it looked at whether the
application had declared it an exception. A link that leaves for a third party,
a native file panel, a session-ending action: each was reported as
`failed-missing-id`, so the only way to clear the audit was to give an id to a
control no check will ever address, or to withdraw the exception.

The exceptions now come first. This is what makes the mechanism usable for the
fleet, where most links on a marketing page go to GitHub, crates.io or docs.rs
and following them would make a run depend on those services rather than on the
site under test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One line in the outcome loop was wrapped the other way; `cargo fmt --check`
is the only failing job on this branch.
…hanged

`fix(qa): count a declared exception as excluded, not as unaddressable` moved
the manual and isolated exclusions ahead of the id checks in
`inventory_class`, so a control the application has excluded is reported as
excluded rather than as missing an id. The test still asserted the previous
order and was not updated, so it failed with

    left: Manual  right: MissingId

The function is right: an exclusion says no check will drive this control, and
reporting it as unaddressable names a defect that can only be fixed by
withdrawing the exception.

Split in two rather than just corrected. The first test keeps its subject, id
defects on a control that is not excluded. The second asserts the precedence
directly, because it is a decision rather than an accident and the last test
that touched it was silently wrong about it.
@pathscale
pathscale merged commit 13dacab into master Sep 9, 2026
3 checks passed
@pathscale
pathscale deleted the feat/target-controls-with-no-area branch September 9, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant