verify-shell-scroll.mjs was written and negative-tested, then left with no
package.json entry and no importer, so nothing could ever run it again. It
also defaulted to localhost:8899 -- an ad-hoc port from an earlier session --
so it failed with ERR_CONNECTION_REFUSED for anyone else and read as a broken
gate rather than a missing server. Now check:shell-scroll, defaulting to
BASE_URL or 127.0.0.1:4173 like every other browser gate, and listed in
verify-browser-all's GATES array.
check:no-orphan-gates fails any gate-shaped file in internal/ that nothing can
invoke. The repo carded this once already; it recurs because writing a gate
and registering one are separate acts and only the first feels like work.
Not every unreferenced file is a lost gate, so the rule is not "everything
must be wired": theme-flash-probe is a dated diagnostic for two fixed bugs,
worth keeping as a reproduction recipe. It now carries an @one-shot tag saying
so, which makes the author state which kind of thing they wrote.
Two self-referential defects in the first version of that gate, both from
reading evidence out of documentation:
- it cited theme-flash-probe.mjs in its own header as an example, then grepped
for importers, found itself, and declared the orphan reachable. The gate
written to catch orphans made an orphan pass by mentioning it. A gate must
never be its own witness, so it now excludes its own file.
- it matched @one-shot anywhere in prose, including its own paragraph
explaining the tag, and reported "1 tagged" when nothing had been tagged.
The tag must now be a marker at line start.
Negative-tested: removing check:shell-scroll from package.json fails by name.
The scroll fix itself is NOT done. contain:layout and .vtl-host overflow-y are
confirmed in the built bundle, and a real wheel gesture still moves the grid
0px with 2586px mounted and unreachable. The gate is what caught that; the
diff looked correct.
The menu is position:fixed with viewport coordinates. That resolves against the
viewport only while nothing establishes a containing block for fixed
descendants. VerticalTabsLayout -- the cap-5 shell that hosts every plugin as a
tab -- sets contain:layout paint on .vtl-content, inside .vtl-main{left:256px}.
Measured at 1600x900, same component, two hosts:
standalone research-dashboard ...... dLeft=0 dTop=4 six of six
inside plugin-studio dashboard tab . dLeft=256 dTop=111 every one
256 is the rail width and 111 the header height. Reported as the menu landing
"in a random spot further down to the right"; it was never random.
An earlier probe measured six triggers at four viewport widths and passed, so
the card was closed while the bug was still there. Every number was accurate.
They were answers about a surface the user never looks at -- widening across
more widths could not have found it, because the variable was the host.
Fix measures the containing block instead of assuming it: a fixed probe at 0,0
inserted in the menu's own parent reports the origin the menu resolves against.
When nothing captures, that origin is 0,0 and the standalone numbers are
unchanged bit-for-bit.
Not fixed by dropping contain from the layout: it is load-bearing there,
stopping a rail collapse from reflowing a heavy mounted map.
check:fixed-anchoring measures both hosts and treats an unmeasured host as a
failure. Negative-tested: reverting the subtraction fails the studio host with
dLeft=256 while standalone still passes -- it fails exactly where the old probe
passed.
Also cards a second symptom of the same cause, not yet fixed: fixed-position
modal scrims written inset:0 stretch to the content pane instead of the
viewport, leaving the rail clickable behind an open modal.
I excluded smoke:share from the aggregate two ticks ago and the reason was wrong.
Every clause of it was true — the script takes required arguments, and its
gallery mode does need the vite dev server — but the conclusion did not follow. I
had tried exactly two invocations, bare and --plugin-gallery, and never --url,
which the script's own header documents first. It runs fine against the static
tree: 8 of 8 pass, each Share button reopening the correct custom element with a
live canvas.
That is the same shape I logged as an anti-pattern last tick, now found in my own
work: a not-applicable verdict is more durable than a wrong bug report, because
it closes the question. It cost the suite its highest-consequence surface for two
ticks. A share URL is the one artefact a partner pastes into an email, and it
fails silently.
It runs as smoke:share-all, carrying its own eight --url flags and --browser
chromium, since Firefox headless has no WebGL on this box and the default engine
stops. The eight leaves are the set derived by grepping for ShareButton, not a
guess. The simple-map case is the discriminating one: it shares to a different
leaf than the one opened, so the deep link is really being followed rather than
an SPA fallback rendering something plausible.
Two shell traps on the way. A VAR=x prefix does not apply to $VAR on the same
line, because the shell expands the argument before the assignment scopes to the
command, so the URLs came out with an empty base; deferring through sh -c fixes
it. And a glob in a block comment closed the comment early.
All seven browser gates were orphans: each had a script, and not one was invoked
by anything else. They were reachable only by someone who already knew the name
to type, which is exactly the person who does not need the list. This repo has
been bitten by this before — the Gate-1 acceptance test existed, worked, and was
invoked by nothing, so it could never report anything. An orphan gate is worse
than a missing one because it looks like coverage.
The data guards already solved this; verify-all.mjs says it plainly — a guard
nobody runs is documentation. That file has no reference to playwright or to the
built tree. It is the data half. This is the browser half.
Every gate listed here was run before it was listed, following the rule the
earlier audit set: listing a program you have not executed puts unvalidated
entries in the hand-off surface. All six pass, and the aggregate now runs them
end to end: capabilities, embeds, map paint, the Gate-1 chain, accessible names,
and phone navigation.
smoke:share is excluded on purpose and says so. It takes required arguments and
its gallery mode reads a registry only the vite dev server serves, so listing it
would make the suite fail for something that is not a defect.
Preconditions are asserted up front and exit 2, so a missing server reads as a
setup problem rather than six mystery failures.