<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/harness/chat_scroll_probe.py, branch main</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=main</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-08T11:21:02Z</updated>
<entry>
<title>test(hub): stop the Chrome profile cleanup racing its own children</title>
<updated>2026-09-08T11:21:02Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-08T11:21:02Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=79520f5f2f9a38ef719fca4dc0f95b001b477e38'/>
<id>urn:sha1:79520f5f2f9a38ef719fca4dc0f95b001b477e38</id>
<content type='text'>
`terminate()` signals the parent only. Chrome's zygote, renderer and gpu
children outlive it by a moment and go on writing into the profile, so rmtree
walked a directory that gained a file between its readdir and its rmdir and
raised "Directory not empty". The probe exited non-zero, and every test in the
file errored at setup — intermittently, roughly one run in three, for a reason
nowhere near the chat code they were testing.

TemporaryDirectory(ignore_cleanup_errors=True) in all four probes that own a
profile: a few bytes left in a throwaway directory are harmless, failing the run
is not. `proc.wait()` after `kill()` was also missing — a killed process still
has to be reaped.

layout_probe.py never cleaned up at all (mkdtemp, no removal) and never waited
for Chrome; it leaked a profile into /tmp on every run.

Ten consecutive runs of test_chat_send.py are clean.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01HCGdheDLxGReuKHga3BtST
</content>
</entry>
<entry>
<title>fix(hub): let the reader scroll up in the chat again</title>
<updated>2026-09-01T21:34:49Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-01T21:34:49Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=32855a95e11032302f8d24036f6f2dd44b829368'/>
<id>urn:sha1:32855a95e11032302f8d24036f6f2dd44b829368</id>
<content type='text'>
The chat could not be read back: any wheel gesture was undone in the frame
it happened in, and the "jump to latest" button never appeared.

None of the pins in ChatPanel are at fault -- every one of them is guarded
by "only if the reader is at the bottom". The reader never got to stop
being at the bottom.

fit() set the panel's height, read documentElement.scrollHeight back and
subtracted the overflow, so the document alternately did and did not
overflow the window. The page scrollbar appeared and vanished with it and
visualViewport fired resize at every pass -- the event fit() is bound to.
It therefore re-entered itself for the life of the panel: measured at 240
firings in two seconds on a page nobody was touching, against 2 for a bare
document. Each pass ran fitAndPin, which re-pinned the list to the bottom
before the scroll event that would have recorded the gesture was delivered
a frame later, so atBottomRef never went false.

- fit() learns the space below the panel once and remembers it on the
  element instead of re-deriving it by writing and measuring back. At the
  steady state it writes nothing, so it produces no resize. A real window
  resize or an orientation change forgets the learnt value and measures
  again (the page under the panel may have reflowed); visualViewport
  deliberately does not, since a phone fires it constantly.

- The scroll-to-bottom is now scoped to *arrival*, which is all it was ever
  for: opening the group, or coming back to the Chat tab, including the
  thumbnails and link-preview cards that keep growing the list for a second
  afterwards. It ends when the reader takes hold of the scroll, and the
  ResizeObserver disconnects there.

- That release is recorded from the gesture (wheel/touchmove/pointerdown/
  keydown), not from the scroll event, which arrives too late to protect
  anything.

Unchanged: landing on the newest message, following new messages while
already at the bottom, the "load older" anchor and the unread marker.

tests/harness/chat_scroll_probe.py mounts the real ChatPanel in a browser
and reads a conversation back; test_chat_scroll_up.py asserts against it.
With the fix reverted, five of its six tests fail and the sixth -- landing
on the newest message -- still passes, which is the property that must not
have been traded away. A structural test cannot see any of this, which is
why it is measured.

test_layout_responsive.py pinned the listener's name and follows the
rename.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01W8oRqEHhnKUr1NfmTVdcyL
</content>
</entry>
</feed>
