Address the interface where it actually stands - #84
Merged
Merged
Conversation
Three things went on addressing the interface as a child of `body` after it
moved into a shadow root. None of them raised anything: each is a lookup that
answers politely and wrongly, or a rule that matches nothing.
`document.getElementById("gitquiet-root")` returns null now. Radix reads a null
portal `container` as "put it in `body`", and the gate rule hides every child of
`body` that is not marked as ours — so every dropdown in the interface opened,
rendered, and could not be seen. Measured on the shipped build: a node portalled
to `body` is invisible, the same node inside the root is not. `mount.ts` already
had `rootIn`, which asks the shadow root first and their document after; it was
simply not exported, so seven call sites kept asking the old way. The theme's
fallback target and the motion durations were two of them.
A selector led by `html` matches nothing inside a shadow tree, because a shadow
tree has no document element. That was the whole weight of the margin reset in
`primer.css`, and the whole of the gutter rule in `widths.css` — measured on the
shipped build at `padding-left: 0px` on every screen, flush to the window edge
with the bar above it still inset, and 14px of margin on a paragraph that asked
for none. The reset is split: nothing of GitHub's reaches inside the shadow root,
so one class-lighter selector wins there uncontested, while our furniture out in
their document still needs the weight. The gutter's `html[data-gitquiet-taken]`
guard was never load-bearing — their page has no element with this id, as its own
comment said.
And the gate rule was hiding something of ours: the SVG filter the bar's glass is
made of. `refraction.ts` says in its own comment that Chrome drops a
`backdrop-filter` whose filter it cannot resolve, and declines to run one defined
in a hidden subtree. It was in a hidden subtree, and the bar had no backdrop.
`shadowReach.test.ts` tests the class rather than the three instances, because no
test that renders a component can catch a lookup that answers null: no source may
ask `document` for the root, no rule may reach the root from `html` or `body`,
and nothing we put in their `body` may go unmarked. All three fail against the
code as shipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bar came out in Times New Roman. Measured on a pull request: `font-family: Times`, `font-size: 16px` on the bar, against an interface set in Inter at fourteen inside the shadow root — which is also why its padding read as wrong, every row in it a seventh taller than it was drawn to be. `#gitquiet-root` declares the font and the size for itself. Everything outside it — the bar, the hover cards, anything Radix portals — declared nothing, and never needed to: it stood in `body` and inherited whatever GitHub's stylesheet put there. Then we started turning their stylesheets off for the recalculation saving, and `body` was left with no font at all. Nothing failed; the interface simply fell back to the browser's own serif in the one place it could still be seen. So the furniture outside the root declares the same baseline the root does, through `:where` so a utility on the bar still wins. The test asserts the two agree, because there is nothing left to inherit from and the next thing we add out there will not know that either. Also takes the host off the document between tests. It is module state twice over — an element in `body` and an entry in a map keyed by the document — so a file that stands the interface up hands the next file in the same worker a shadow root with the last file's screen still in it. This is offered as the cause of a CI-only failure on the previous commit and is not proven to be: the suite is green here with and without it, on every combination of files tried, and the sharding that decides which files share a worker is a fact about the machine. The cleanup belongs there either way, beside the eight others that are there for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every live check of Following has been made on a blob page, because that is the only page a probe can reach on its own. Measured rather than assumed: a fresh profile on a *public* pull request gets our own signed-out card, with `user-login` empty and only anonymous cookies, because the diff is drawn from routes that answer to a session. So nothing a blob page proves carries over to a diff, which is a different drawing — two halves, a marker column, line numbers belonging to one side — and a reported fault is on a diff: the underline appears and the press does nothing. This reports rather than judges. For each name it says what the renderer handed over — the row's attributes, the token's `data-char`, where the word sits inside it — then holds the key, then presses, and says which of three things happened: a panel opened, the pane moved, or nothing at all. "Nothing happens" is three faults wearing one coat, and from outside the screen they are identical: a press read as a press on a use scrolls to a line already on screen and looks like a dead click. It needs a profile that has been signed into once and kept, which `GITQUIET_CDP_PROFILE` already provides. No cookie is read or moved anywhere. Checked against a page it can reach, so the probe is not the thing under suspicion: on `p-limit`'s `index.d.ts` it tells all three outcomes apart — `LimitFunction` opens its panel, `concurrency` opens its own, and `Options` moves the pane to where it is written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dropdowns were the only overlays in the interface not portalling to `outsideHost(document, OVER_ID)`. The hover cards go there, the settings dialog goes there, the toasts go there — a child of `body` carrying the outside mark, so the gate rule spares it, and the theme tokens, so it is painted. The menus named `#gitquiet-root` instead, which was a child of `body` too until the interface moved into a shadow root and that lookup started answering null. The previous commit pointed them at `rootIn`, which found the root again and made them visible to a reader. It also put a menu inside the shadow root, where `screen` cannot see it: fifty-three tests across four files went red on CI and stayed green here, because `bun test --parallel` shards by core count and which files share a worker is a fact about the machine. Two commits spent on a test isolation theory that was not the cause — the cause is that the root was never the right target. An overlay exists to escape whatever its row is clipped by, which is the one thing standing inside the root cannot do. `rootIn` stays exported for the two callers that genuinely want the root: the theme's fallback target and the motion durations. The settings panel test asked that the panel be inside `#gitquiet-root`, which was one painted place rather than the property its own comment described. It now asks what it meant: that the panel stands in a host of ours, which is a host the theme paints and the gate rule spares. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`millisOf` reads every motion duration off `#gitquiet-root`, and `tests/paced.ts` plants a root in `document.body` and writes the durations a test needs onto it. Both halves of that seam address the document, and pointing one of them at `rootIn` made them disagree: once a screen stands a root inside the shadow root, the lookup prefers that one and the planted durations are ignored. A dissolve paced to never finish finishes at once, React throws the element away and mounts a second one already faded out, and the test that asks for a transition with something to transition from gets two different elements. Green here and red on CI both times, because whether there is a root in the shadow at that moment depends on what else is in the worker. Moving the clock into the shadow root is worth doing — the CSS owns these numbers and the sheet is in there now — but it is a change to both halves of the seam and it is not this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A name borrowed from a package this repository holds itself resolved to a path with no ending on it. `@org/type-utils` held at `packages/type-utils` and imported as `@org/type-utils/result-monad` was read as `packages/type-utils/result-monad`, which is not a file — nothing on disk is called that, and the check that would have caught it was never made. What followed is worse than no answer. The path matched no file, so the answer fell through to the first Writing of that name anywhere in the repository: a different thing with the same spelling, offered to the reader as the place their name is written. That is the one mistake this whole feature exists to prevent, and it was being made silently. `endingsFor` is the ending list `couldBe` already had, split out, because a relative specifier is not the only way to arrive at a path that is missing one. `within` is `reaching` for a path that is already a path, and the package branch now goes through it — so a path built from a package's layout is checked against the repository's real files before it is believed, like every other answer here. Reported from a monorepo's pull request, where the import is `@openrouter-monorepo/type-utils/result-monad` and pressing `AsyncResult` did nothing at all. The console script alongside is what found it: the probes cannot reach a pull request — it is drawn from routes that answer to a session, and a fresh profile gets our signed-out card, measured on a public one — so it reports from the reader's own browser instead. It is also what proved a press on a diff opens the panel perfectly well, which is the opposite of what was reported and of what I expected. Still unresolved, and not this: a tsconfig `paths` alias. `@/routes/vault` is bare by the only test there is, so it goes to package resolution and finds no package. That needs the repository's `tsconfig.json` read and its patterns applied, which is a feature rather than a fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three lookups and two rules went on addressing the interface as a child of
bodyafter it moved into a shadow root. None of them raised anything — each is a lookup that answers politely and wrongly, or a rule that matches nothing.Reported from production: spacing wrong throughout a panel, and a dropdown that would not open.
Measured on the shipped build
body(where Radix puts it today)#gitquiet-rootpadding-left#gitquiet-glassdisplay: none— our own filter, hidden by our own gate ruleWhat each one was
The dropdowns.
document.getElementById("gitquiet-root")returns null now, and Radix reads a null portalcontaineras "put it inbody", where the gate rule hides anything unmarked.mount.tsalready hadrootIn— shadow root first, their document after — it was simply never exported, so seven call sites kept asking the old way. The theme's fallback target and the motion durations were two of them.The spacing. A selector led by
htmlmatches nothing inside a shadow tree, because a shadow tree has no document element. That was the entire weight of the margin reset and the entire gutter rule. The reset is split in two: nothing of GitHub's reaches inside the shadow root, so one class-lighter selector wins there uncontested, while our furniture out in their document still needs the weight. The gutter'shtml[data-gitquiet-taken]guard was never load-bearing — their page has no element with that id, as its own comment already said.The bar's glass.
refraction.tssays in its own comment that Chrome drops abackdrop-filterwhose filter it cannot resolve, and declines to run one defined in a hidden subtree. The gate rule had put it in exactly that subtree. Found while probing for the other two, not reported.The test
shadowReach.test.tstests the class, not the three instances — no test that renders a component can catch a lookup that answers null. No source may askdocumentfor the root; no rule may reach the root fromhtmlorbody; nothing we put in theirbodymay go unmarked. All three fail against the code as shipped, verified by reintroducing each fault.4986 pass, 0 fail. Gates green.
🤖 Generated with Claude Code
Also here: following a name into a package this repository holds
Found while QAing a separate report — pressing a name on a pull request and getting nothing.
A name borrowed from a workspace package resolved to a path with no ending on it.
@org/type-utilsheld atpackages/type-utils, imported as@org/type-utils/result-monad, was read aspackages/type-utils/result-monad— not a file, nothing on disk is called that, and the check that would have caught it was never made.What followed is worse than no answer. The path matched nothing, so the answer fell through to the first Writing of that name anywhere in the repository: a different thing with the same spelling, offered to the reader as the place their name is written. That is the one mistake this feature exists to prevent, and it was being made silently.
endingsForis the ending listcouldBealready had, split out — a relative specifier is not the only way to arrive at a path missing one.withinisreachingfor a path that is already a path, and the package branch goes through it, so a path built from a package's layout is checked against the repository's real files before it is believed.Still unresolved, and not in this: a tsconfig
pathsalias.@/routes/vaultis bare by the only test there is, so it goes to package resolution and finds no package. That needs the repository'stsconfig.jsonread and its patterns applied — a feature rather than a fix.Reporting from the reader's own browser
The probes cannot reach a pull request. It is drawn from routes that answer to a session, and a fresh profile gets our own signed-out card — measured on a public pull request, with
user-loginempty and only anonymous cookies, not assumed. Every live check of Following before this was made on a blob page.So
scripts/following-in-the-console.jsreports from the browser already looking at the page, andscripts/probe-diff-following.tsdoes the same for anyone with a signed-in profile kept underGITQUIET_CDP_PROFILE. Neither reads or moves a cookie.They earned their place immediately by contradicting me twice. A press on a diff does open the panel —
data-char=22on the declaration is exactly the file column, so a diff row carries no marker offset and the column arithmetic was never wrong. And the panel then stays up for seconds, so the scroll-dismissal I suspected is not it either: no scroll fires at all. What closes it is a laterpointerdownagainst the host, still unexplained and still being chased.