<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_cli_dispatch.py, branch 0.17</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.17</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.17'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-27T20:21:26Z</updated>
<entry>
<title>test: make both suites pass on Windows</title>
<updated>2026-09-27T20:21:26Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-27T20:21:26Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=a45e77df1b0024707914a446aa89d33baa223787'/>
<id>urn:sha1:a45e77df1b0024707914a446aa89d33baa223787</id>
<content type='text'>
Most of these failed on Windows for reasons that had nothing to do with the
code under test, which is how real Windows defects hid among them:

- Read and write files as UTF-8, and talk to Node in UTF-8. read_text(),
  write_text() and subprocess text=True use the locale codepage, cp1252 on
  Windows: "é", "—" and "→" arrived as "?" or crashed, some sixty tests.
  Calls to PowerShell and schtasks are left alone -- they answer in the
  console codepage.
- Import ESM harness modules by file URL (as_uri): a raw "C:\..." path is not
  a module specifier.
- test_cli_golden: mask the tmp path in its JSON-escaped form, spell it the
  POSIX way, record on Linux, mask the protocol version (the recording had
  failed everywhere since the MNP 4.0 bump) and argparse's version-dependent
  quoting; point USERPROFILE at the tmp home, or `member invite` and
  `operator pair` wrote their codes into the developer's profile.
- test_disk_io_off_loop: expect what a free loop can reach on the platform's
  timer, 15.6 ms on Windows, not an assumed 5 ms.
- test_root_paths_are_operator_only: expect the OS's spelling of the path.
- test_audio_meta_cache: find ffprobe with shutil.which.

Node suite on Windows: 1489 passed, none failed. Hub suite: 3 failures left,
all older than this change (two SQLite concurrency tests, one transfer resume).

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<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): keep the CLI dispatch test off the developer's node</title>
<updated>2026-09-24T15:58:08Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-24T15:44:08Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=c2dabdb870ecd8fd8ea6392e44258de02e9ad467'/>
<id>urn:sha1:c2dabdb870ecd8fd8ea6392e44258de02e9ad467</id>
<content type='text'>
The stub left HOME and config.DEFAULT_CONFIG_PATH real: every run wrote
TEST-CODE over ~/.local/share/meshbay/invite-code and pair-code, and
`status` read the real keystore and asked the running node.

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>feat(node): member invite --link and member cancel in the CLI</title>
<updated>2026-09-23T16:16:02Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-23T16:16:02Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1d169141f3a5542efda2f7fdb0bb88308c06191b'/>
<id>urn:sha1:1d169141f3a5542efda2f7fdb0bb88308c06191b</id>
<content type='text'>
The CLI makes both halves itself — the node's code, then the hub's
ticket bound to the address — and prints the link; a refused ticket
takes the code back, and cancel takes back both. The CLI never asks the
hub to mail.

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>Merge origin/main: the operator's upload ceiling beside the disk-thread work</title>
<updated>2026-09-19T12:01:38Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T12:01:38Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=d2495a2c4b89fbbfc18cefec83ae96cabdd745e2'/>
<id>urn:sha1:d2495a2c4b89fbbfc18cefec83ae96cabdd745e2</id>
<content type='text'>
One conflict, in §15.3's open list, and it was two changes agreeing rather than
disagreeing: this side removed the rows for the third-party search bound and the
node-announcement bound because both are now built (AV27, AV28), while the other
side kept them and added a new one. Resolved by keeping what is genuinely still
open — per-device revocation having no CLI — and leaving the two closed.

`webrtc_server.py` merged without conflict but the two sides met inside one
function: `_upload_chunk` gained the operator's `max_upload_gb` ceiling from
there and the per-group lock and `off_disk` calls from here. Read back rather
than trusted: the operator's ceiling now sits inside the critical section that
keeps chunk ordering, and the unlink beside it goes to the disk thread with
everything else.

2893 passed. The twelve `test_sticky_header.py[firefox]` setup errors are the
open Firefox on this machine, as before.
</content>
</entry>
<entry>
<title>docs: cite MESHBAY_DESIGN.md and a section instead of the merged notes</title>
<updated>2026-09-19T00:12:47Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T00:12:47Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=20a824118c09af15d6c338db4c9480ffe5cbcdb6'/>
<id>urn:sha1:20a824118c09af15d6c338db4c9480ffe5cbcdb6</id>
<content type='text'>
The per-feature design notes were merged into docs/MESHBAY_DESIGN.md and
deleted from the tree on 2026-09-11, but ~230 comments across the three
packages still named them — usually written `docs/musicbay.md §3.2`, as
though the file were still in docs/. A reader had to know §16 existed to
resolve any of them. They now name the section directly.

Every mapping comes from §16, the concordance, which already records where
each old section landed: musicbay -&gt; §9.8, mediacenter -&gt; §9.7 for the
Videos app and §6.5 where the subject is derived data, photos -&gt; §9.9,
auth-confirm -&gt; §3.6, refactoring-search -&gt; §9.11, invite-pairing-v1 -&gt;
§3.4, per-node-identity-v1 -&gt; §3.2, captcha -&gt; §7.7, chat-sender-keys -&gt;
§4.5, apps/refactor-groups -&gt; §9.1–§9.4, desktop-client-v1 per section.
Bare citations of the same documents (`draft-v6 §2.11`, `§4.8`, `§3.4`)
are retargeted too: those collide with real section numbers in the design
document, so leaving them would have been worse than the named form.

Four cases the concordance does not cover, each decided rather than guessed:

  Sub-item references into documents that no longer exist — mediacenter's
  `§3.3 row 4`, `§3.4b/c`, `apps.md §3b` — name rows and sub-items §9.7 and
  §9.2 do not reproduce. The module-level citation stays; the sub-item
  pointer is dropped.

  The V-findings keep their labels but lose the dead `§10.1/` prefix.
  §13.8 lists V1–V13 as per-application open items, which is not what the
  labels mean in these comments, so pointing them at §13.8 would have been
  a false citation.

  `apps.md §5`'s virtualization requirement has no counterpart anywhere in
  the design document. The requirement is stated in the comment itself, so
  the citation is dropped rather than aimed at a section that does not say
  it.

  Comments that attributed a *sentence* to an old note — musicbay's "several
  thousand files" example, its "what got measured" note, its measured
  ~11%/~26% cover-art figures, the "original no root, whole shared tree"
  call — state the fact without attribution now. §9.8 does not contain those
  sentences and citing it for them would have been wrong.

CLAUDE.md's "a reference to a document that no longer exists" row now says
the concordance is for git history and out-of-tree material; the code cites
sections directly.

Verified: 2851 passed, 4 skipped. The 12 errors in the run are the Firefox
leg of test_sticky_header.py's browser harness, which is broken at the
browser level on this machine — headless Firefox (snap) dies with
`[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer`, renders
nothing, and the probe exits `{"error": "no measurement"}` after its full
90s wait. Chrome runs the same 12 assertions in 3.2s and passes. Nothing
here can affect it: every changed line in style.css is inside a comment.
Also checked: ast.parse on every changed .py, `node --check` on every
changed .js, the /* */ balance in style.css, and that no changed line
exceeds the width its file already used.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(node): per-file upload ceiling is an operator setting, default 8 GB</title>
<updated>2026-09-18T23:49:56Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-18T23:02:05Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=c2c49182e6d4f1f1ef3ed3ba5e8699912f9a27bd'/>
<id>urn:sha1:c2c49182e6d4f1f1ef3ed3ba5e8699912f9a27bd</id>
<content type='text'>
Was a 4 GB constant in webrtc_server.py, the same on a small board and on
a machine holding a library. Now max_upload_gb in node.toml, on the Node
page and via `meshbay-node transfers max-size`, read from the transport
context per chunk so a change reaches an upload already running.

MESHBAY_DESIGN.md §6.4; §15.3 records a defect found beside it.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(node): give the per-member transfer cap a door anyone can open</title>
<updated>2026-09-08T23:15:41Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-08T23:15:41Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=b3ea222ca8b6218e0187070fe94bc8fed7059a90'/>
<id>urn:sha1:b3ea222ca8b6218e0187070fe94bc8fed7059a90</id>
<content type='text'>
Reported as "the slots seem hardcoded to 2": `meshbay-node transfers set 8 8`
and still only two downloads at a time. Not hardcoded — that is the *per-member*
cap, which is a group's setting and is checked before the node's, so raising the
machine's total cannot move it. But the diagnosis was right in the way that
matters: nothing could change it.

`OP_TRANSFER_LIMITS` shipped with exactly one front door, the signed MNP
handler, and nothing anywhere opened it — no client call, no CLI verb, no
loopback route. So the cap sat at its default of 2 for ever, which from outside
is indistinguishable from a constant. CLAUDE.md states the rule this missed:
operator operations are one implementation with several front doors.

  - `PUT /api/groups/{id}/transfer-limits`, calling the same
    `ops.set_transfer_limits` the signed handler calls;
  - `meshbay-node transfers per-member &lt;downloads&gt; &lt;uploads&gt; [--group X]`;
  - `transfers show` now separates the node-wide pools from the per-group
    per-member caps, and marks each `[set]` or `[default]`. It printed "2 per
    member" with no indication of where the 2 came from, which is half of why
    this looked like a constant.

Zero is refused here as everywhere else: it is not "unlimited", and a member who
may not transfer at all is a member the operator revokes.

Verified on a live node: the cap changes, survives a daemon restart, and
`transfer_probe.py --want 6` measures 4 granted against a cap of 4 where it
measured 2 before.

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