<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/harness/playlist_store_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-19T12:39:38Z</updated>
<entry>
<title>style: the 98 ruff could not fix, so the linter is a signal again</title>
<updated>2026-09-19T12:39:38Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-19T12:39:38Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=9e7b75bb0f6f6649fb00f2dc97059e90b7d52875'/>
<id>urn:sha1:9e7b75bb0f6f6649fb00f2dc97059e90b7d52875</id>
<content type='text'>
The pass before this applied ruff's own fixes. These are the ones needing a
decision, and the point of doing them is that `ruff check .` now passes: a
linter reporting 98 known-acceptable findings reports nothing, because the next
real one arrives invisible.

**Lines over 100 (70).** Mostly wrapped where they stood. Two exceptions: the
aligned trailing comments in `protocol.py`'s message table were shortened rather
than wrapped, because wrapping one row of a table breaks the table; and in
`models.py` the column comments moved above their columns for the same reason.

**Imports below the first statement (14).** `csam.py` kept its FastAPI imports
under a section header halfway down the file; two node tests had a constant and
a `pytestmark` wedged between two import blocks. Moved, not suppressed.

**Bindings nothing reads (4).** Three in tests, where the call stays and only the
name goes — `_user(client, "listener")` is there to create the user, not to
return one. The fourth was in `revocation.py` and was not a lint finding at all:
`_connect_and_listen` opened an httpx stream to the WebSocket URL, did `pass`,
and then opened the real connection through the `websockets` library. One
pointless request per connect, left over from before that library was used
directly. Removed, and `httpx` with it.

**`l` as a name (4)**, **semicolons (6)** in the POC spikes, and the rest.

2893 passed, the same count as the two commits before it.

`meshbay_node/revocation.py` is worth a decision separately: 154 lines that
nothing imports, superseded by `hub_client.maintain_ws`'s `on_revocation`. This
commit only stopped it failing the linter.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>playlists: promise the count that holds when nothing compresses</title>
<updated>2026-09-16T15:51:19Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-16T15:51:19Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=929bfcccf8928a30d86d48a8b0137db0dd4a1ee0'/>
<id>urn:sha1:929bfcccf8928a30d86d48a8b0137db0dd4a1ee0</id>
<content type='text'>
The ceiling is 1200 tracks on a library that repeats and 660 on one that
never does; the message said 1000, which would send a reader back into the
wall at 660. It says 500, measured at both ends.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>playlists: refuse a body no frame can carry, and say which one</title>
<updated>2026-09-16T15:47:00Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-16T15:47:00Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=c35f3a73eb68cdd7c4179cded3d1ead63ec5a033'/>
<id>urn:sha1:c35f3a73eb68cdd7c4179cded3d1ead63ec5a033</id>
<content type='text'>
A DataChannel send() throws above the max-message-size the node advertises
(aiortc: 65536), so the 1 MB body cap was unreachable and each body push
swallowed the difference in a bare catch. Cap at 62 KB after sealing, name
the playlist in the sync result, keep syncing the rest.

Measured: 1000 tracks seal to 53 276 bytes, so the ceiling is ~1200.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>playlists: make the writes actually leave the browser</title>
<updated>2026-09-16T14:07:18Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-16T14:07:18Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=2916aa376009283305a7acec4aafa3c96544499e'/>
<id>urn:sha1:2916aa376009283305a7acec4aafa3c96544499e</id>
<content type='text'>
Reported from a phone: signing in with the same account showed no
playlists. syncWith was called from exactly one place in the interface,
so creating a playlist, deleting one, removing a track and saving the
queue all wrote to IndexedDB and stopped there. The store pushes itself
now, coalesced, so a new mutation cannot forget to.

Silence was the real defect. The node audited only successes, so a
refusal left no trace and user_blob_list none at all; the background
push swallowed its reason; the interface said nothing. All three report
now, and "Sync now" says what happened either way.

An unreadable blob on a node was treated as a fetch failure and returned
before the push — permanent, once the node held anything. It is an
absence: the client is the authority, and it gets overwritten.

A sign-in reconciles whatever this browser already holds, a pending push
is flushed when the page goes away, and a push that did not land is
retried once.

no_key is spelled out: a client that signs in with its remembered device
key only ever has a bundle key persisted before the playlist subkey
existed, and an AES handle is non-extractable.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>playlists: the store, and putting it on nodes</title>
<updated>2026-09-16T09:52:03Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-16T09:52:03Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=2e973795383b71f63ae9e3bef0e5dfc7930b4c90'/>
<id>urn:sha1:2e973795383b71f63ae9e3bef0e5dfc7930b4c90</id>
<content type='text'>
playlists.js is IndexedDB, WebCrypto and a transport, and node has no
IndexedDB — so it is driven in Chrome against a node stubbed to record
what it was handed, which is also how what leaves the browser is
checked to be sealed.

Sync asks the node what it holds (user_blob_list) rather than comparing
against the merged watermark, which says nothing about that node: the
first version pushed every body on every sync. A tombstoned playlist's
body is deleted as each node is reached, or the quota fills with graves.

The database version and its stores stay in hub-client.js — two modules
opening one database at versions of their own is a VersionError thrown
at whichever runs second.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
