<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/harness/group_tab_probe.py, branch 0.13</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.13</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.13'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-10T15:23:40Z</updated>
<entry>
<title>refactor(mnp)!: one answer to "may this member write", and it is the root</title>
<updated>2026-09-10T15:23:40Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-10T15:23:40Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1dcedc77083b908b7b3b431bad679a4813884355'/>
<id>urn:sha1:1dcedc77083b908b7b3b431bad679a4813884355</id>
<content type='text'>
The group-wide `member_upload` switch is gone: the message, the signed
operation, the field on the handshake ack, the `upload` alias on every root in
the index payload, and the client's fallback path to it.

Whether a member may write has been a property of each root for a while, and
that is the model that survives: a single flag over the group cannot express
"this library is published read-only and that folder is a drop box", which is
the ordinary arrangement. What was left of the switch was a handler that logged
a deprecation and acted on nothing, and a client that read `ack.member_upload`
whenever the roots carried no `writable` — a second source for one question,
with whichever the code consulted first deciding it.

`roots.describe()` drops `upload` for the same reason: it was `writable` under
an older name, and two names for one boolean is one too many.

The paperclip now says "nowhere to write" rather than picking a root, in a group
that has none writable. That is the honest answer; the fallback picked whatever
came first and failed at send time.

Node suite 1215 passed.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
</content>
</entry>
<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): fall back to the group's first app when the landing tab is absent</title>
<updated>2026-09-01T22:38:27Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-01T22:38:27Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=f7917bdde37fe089485bb2b65c5504315dcc9c56'/>
<id>urn:sha1:f7917bdde37fe089485bb2b65c5504315dcc9c56</id>
<content type='text'>
A group could open on a tab that rendered nothing: no panel, no tab shown
active, and nothing on screen to explain it.

The landing tab is chosen at mount from a preference -- default_tab for the
group, else the account-wide one, else 'chat'. Which applications the group
runs comes from the node, in the handshake ack, several awaits later. A
preference is a preference, not a promise that the app exists here, so the
two disagree in two ordinary cases: the group has Chat disabled while
'chat' is everyone's default, or the reader prefers an app this group does
not run. `apps.map(a =&gt; tab === a.key &amp;&amp; ...)` then matches nothing.

The first app the group does offer answers both. Two more cases come free:
a preference naming an app that no longer exists, and an operator disabling
the app someone is currently looking at -- enabledApps changes live over
apps_enabled, and being moved to a working tab beats staring at an empty
panel. Settings is exempt: it is not an application, and the create-group
wizard lands on it deliberately.

`const apps` moves above the effect that reads it; a const further down
would be in its temporal dead zone, which is the hook-ordering trap already
recorded in CLAUDE.md.

tests/harness/group_tab_probe.py renders the real GroupPage against a stub
node answering a chosen enabled_apps and reads the tab bar back, over five
cases. With the fix reverted the three fallback cases report no active tab
at all and four of the six tests fail; the two that pass either way are the
ones that must not change -- a group running everything, and a preference
the group does honour (Videos stays selected, so the fallback has not
become "always the first app").

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