<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_root_paths_are_operator_only.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-24T16:19:13Z</updated>
<entry>
<title>refactor(node): move the CLI out of daemon.py into cli/</title>
<updated>2026-09-24T16:19:13Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T16:19:13Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=6e9afe3e54ab6fb0162393150b7892655b4561d4'/>
<id>urn:sha1:6e9afe3e54ab6fb0162393150b7892655b4561d4</id>
<content type='text'>
Each `if args.command == ...` branch of main() becomes a function in
cli/ (one module per family of verbs); the parser, the process setup
and a VERBS table go to cli/parser.py and cli/dispatch.py. daemon.main
runs the verb or starts the daemon. Tests patch the CLI through
conftest.patch_cli; the CLI golden is unchanged.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>test(node): read the daemon's source wherever it is split</title>
<updated>2026-09-24T15:58:08Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T15:42:10Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=ef942b93d1e9bfc28bf607dfaf501cc7d9685e7b'/>
<id>urn:sha1:ef942b93d1e9bfc28bf607dfaf501cc7d9685e7b</id>
<content type='text'>
Tests that read daemon.py now take their text from node_source
(daemon.py, NodeDaemon's bases, the cli package once it exists). The
CLI tests that sliced main() run the CLI instead.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>style: ruff's own fixes, mechanically applied</title>
<updated>2026-09-19T12:24:13Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T12:24:13Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=86188385cbdae1ee90c1dca7a7b9db2edef1ecd4'/>
<id>urn:sha1:86188385cbdae1ee90c1dca7a7b9db2edef1ecd4</id>
<content type='text'>
`ruff check .` had gone unrun long enough to report 568 errors, which is the
same as having no linter: the next real finding would have been invisible in the
noise. This is the 521 it fixes by itself, in 173 files, and nothing else — the
98 it cannot fix are the next commit.

What actually changed: import sorting (225), imports nobody used (87, none of
them a re-export — no `__init__.py` is touched, which was the one way this could
have broken an import elsewhere), `datetime.timezone.utc` to `datetime.UTC` (69)
and `asyncio.TimeoutError` to `TimeoutError` (18), both plain aliases on the 3.12
this project requires, `Optional[X]` to `X | None` (24), and f-strings with
nothing to interpolate (19).

Checked rather than assumed: every module in the three packages still imports,
and the suite is 2893 passed — the same count, test for test, as the merge
before it.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: a root change reaches every client without a page reload</title>
<updated>2026-09-06T21:01:36Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-06T21:01:36Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=13ccc4a16c604f46ed10b6c5dbfbc8fdd7d008c0'/>
<id>urn:sha1:13ccc4a16c604f46ed10b6c5dbfbc8fdd7d008c0</id>
<content type='text'>
The directory table travelled on `index_sync` alone — a *full* index, which the
node only ever sends on request. Every ongoing change went out as an
`index_delta`, which carried files and nothing else. So the message that says
"something changed" was the one message that could not say a root had.

The acks hid it: `root_add_ack` and friends broadcast the new table to whoever
is connected, so the common cases looked right. What that could not cover was
the operator's own client, where the ack landed and was then overwritten — the
table calls `onRefreshIndex` after an add, that fetch returns the set from
*before* the node's reload (fire-and-forget, because a rescan is minutes on a
real library), and `applyIndex` writes it over what the ack had just delivered.
The new directory appeared for one paint and vanished.

Two halves. `index_delta` now carries the roots table, sealed with the rest and
identical to `index_sync`'s — additive, so a 1.0 client sees a field it does not
read. And the table no longer refreshes the index after a root change: the ack
gives it the new set immediately, and the delta the node pushes when the scan
finishes gives it again, along with the files.

The test that pins it uses an *eject* as its case, because an eject changes no
file at all — the entries freeze — so its delta is empty of additions,
deletions and updates. Without the table it says literally nothing, which is
how a library disappearing from under a group went unannounced to everyone but
whoever pressed the button.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_011pvMdvLBG92jyhvD5pD6us
</content>
</entry>
<entry>
<title>fix(node): `root list` printed "?" for every path</title>
<updated>2026-09-06T20:20:46Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-06T20:20:46Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=d6e1cc19a09df35988f6a90c226c94f2fdf6b209'/>
<id>urn:sha1:d6e1cc19a09df35988f6a90c226c94f2fdf6b209</id>
<content type='text'>
`RootSet.describe()` feeds two audiences that want opposite things. The index
payload goes to every member and has always deliberately carried no paths — a
member is told what exists and whether it is readable, not that the library
sits under someone's home directory. The loopback API answers the operator
themselves, over a channel that already requires their machine and the run
token, and the path is exactly what they asked for.

The `root list` CLI I added in Phase 1 read `path` from the member form, so it
printed a placeholder for every directory. Nothing caught it: the CLI reads a
dict, the API returns a dict, and neither end states which keys it owes.

`describe(with_paths=True)` is the operator's view and `list_groups` is the
only caller. The shared-directories table has the same hole over MNP — the
roots there come from the index payload — so its Path column now appears only
when a path is actually present, rather than rendering a column of blanks.

The test asserts both halves, because they pull opposite ways: one that only
checked the operator sees paths would be satisfied by leaking them to every
member. It reads the indexer's source for the member side, and compares the
CLI's key reads against what the payload offers for the other — checked to
fail in each direction independently.

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