<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_root_ops_reach_the_live_set.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-24T23:30:47Z</updated>
<entry>
<title>refactor(node): split ops.py into the ops package</title>
<updated>2026-09-24T23:30:47Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T23:30:47Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=762233772162a05be67432aa551a430b939250de'/>
<id>urn:sha1:762233772162a05be67432aa551a430b939250de</id>
<content type='text'>
Each section of ops.py becomes a module of meshbay_node/ops/ (core,
node_toml, members, chat, groups, roots, files, settings, apps), cut as
text; ops/__init__.py keeps the docstring and re-exports every name, so
`ops.&lt;name&gt;` is unchanged for every caller. Logger name unchanged.

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(node): the MNP root path never reloaded, and my first repair made it worse</title>
<updated>2026-09-06T19:43:00Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-06T19:43:00Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=232fd2a8d15f63b6bf6ad26286dd9836d6819668'/>
<id>urn:sha1:232fd2a8d15f63b6bf6ad26286dd9836d6819668</id>
<content type='text'>
The previous commit was the wrong fix. `add_root` did leave the running node
unchanged, but editing the live `RootSet` in place — which is what I did — is
wrong in the other direction.

`DirectoryIndexer.retarget` decides what to scan by diffing the names it
already holds against the ones it is handed, and `_retarget_indexer` hands it
`groups_ctx[gid]["roots"]`: the very object the op had edited. So the new root
sat on both sides of the comparison, nothing was scanned, and the directory
would have appeared in the table permanently empty. `_reload_config_inner`
diffs the same way and would have concluded nothing changed. `remove_root` had
the same shape and would have kept serving a removed directory's files.

The real defect is that two front doors did different things. `ui/app.py` has
always fired the daemon's `reload_fn` after these ops, which re-reads node.toml
and builds a *fresh* set; the MNP path retargeted a stale object instead. That
asymmetry is exactly what `ops.py` exists to prevent, and it is why the bug
survived until an operator added a directory from a browser — the loopback path
worked all along.

So: the ops leave the live set alone, `_retarget_indexer` asks the daemon to
reload, and `update_root` keeps editing in place because flags change no files
and the synchronous upload handler reads that object on the next request.

The tests now check the files rather than `describe()`, which proves nothing
about whether anything was scanned. One of them demonstrates the failure mode
instead of describing it, so the rule is checkable and will say so if
`retarget` ever changes. Two more cover the seam itself — that the MNP path
reloads, and that a context with no daemon still retargets.

Diagnosed by reading the running node's journal rather than the source: the
first add logged "Reloading config" and a rescan, the two later ones logged
neither. I should have looked there before the first attempt.

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): adding a root reached node.toml but not the running node</title>
<updated>2026-09-06T19:25:23Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-06T19:25:23Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=4c4e9ba7a17e058dc12cb10171743329201dd7e6'/>
<id>urn:sha1:4c4e9ba7a17e058dc12cb10171743329201dd7e6</id>
<content type='text'>
`add_root` appended to the config and to node.toml and stopped there.
`_retarget_indexer` — the MNP path — then re-points the indexer at
`groups_ctx[gid]["roots"]`, an object nobody had touched, so it was retargeted
at exactly what it already had. The directory was in the config file and
invisible everywhere else until a restart.

Worse than invisible: the ack does carry the new set, so the client showed the
directory for one paint and the next index_sync took it away again — which
reads as a UI bug and is not one. Adding it a second time was then refused as
colliding with itself, which is the only reason anyone found out.

`remove_root` and `update_root` already updated the live set; this one was
missed. The loopback API hid it, because `ui/app.py` fires `reload_fn()` after
the op and that re-reads node.toml from disk. The MNP path does not, and the
shared-directories table only started offering Add over MNP in this refactor —
a latent bug made reachable.

The ack now describes the set the node will actually serve rather than one
built on the side, so the two cannot disagree.

test_root_ops_reach_the_live_set.py holds all three ops to it, including the
counter-property that the same directory is still refused twice and that
node.toml and the live set stay in step — the two halves drifting is how an
operator's next restart silently undoes their last change. Four of its seven
fail against the code above.

Recovery on a node already in this state is `meshbay-node reload`: node.toml
has everything, nothing was lost.

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