<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/test_asset_versioning.py, branch 0.15</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.15</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.15'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-19T12:24:13Z</updated>
<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(hub): the asset fingerprint covers every file under static/</title>
<updated>2026-09-11T11:57:44Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-11T11:57:44Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=7adbf5163f0193d80bb5578dc875eba811f864bb'/>
<id>urn:sha1:7adbf5163f0193d80bb5578dc875eba811f864bb</id>
<content type='text'>
Everything under static/ is served at /a/&lt;hash&gt;/ with a year's
`immutable`, but the hash was computed from a hand-kept list of 43
top-level modules. The ten catalogues and vendor/ were not on it, nor
was anything the guarding test could see: it globbed *.js at the top
level only. A change confined to the catalogues therefore kept the hash,
and a phone went on showing a heading that had been rewritten and
deployed - pull-to-refresh fetched the no-store shell, which was
current, and never refetched en.js at a URL that had not moved.

The fingerprint now hashes every file under static/, path and content,
so a change, a rename or a new file moves the version with nothing to
register. _ASSETS is gone, and CLAUDE.md, MESHBAY_DESIGN.md 9.4 step 6,
assets/brand/README.md and docs/playlists.md no longer ask for it.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
</content>
</entry>
<entry>
<title>fix(hub): stop the maintenance loop racing the tests, and pin _ASSETS</title>
<updated>2026-09-02T15:31:50Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-02T15:31:50Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=7b25f1c09ba1b8692988d9616f3c33c97af9f3ca'/>
<id>urn:sha1:7b25f1c09ba1b8692988d9616f3c33c97af9f3ca</id>
<content type='text'>
Two defects found while closing out the Search merge, neither of them in
that feature.

The maintenance loop. create_app's lifespan starts cleanup_loop as an
asyncio task, so every test — each entering that lifespan — ran a purge
pass concurrently with its own requests. On SQLite :memory: that is not
merely noisy: the engine uses a StaticPool, one connection for the whole
process, so the request's session and the cleanup task's session
interleave transactions on the same connection. A registration could
commit and then be invisible to the login three lines later, surfacing
as 401 Invalid credentials for an account created moments before, in
roughly one run of test_node_ws_auth.py in four.

The purge itself is not at fault and this is not a production condition.
A passive SQL listener caught the DELETE removing 0 rows, and the INSERT
carrying status='active' — so neither the pending-account mechanism nor
the purge filter is involved, and PostgreSQL gives every session its own
connection. What the fixture removes is the second user of the shared
one. 60 runs of the previously flaky file, 0 failures; reproductions
before the fix landed on attempts 4, 6, 13 and 29 of separate loops, so
a clean run of 60 has about a 1% chance of being luck.

_ASSETS. source-merge.js shipped missing from webapp._ASSETS, the
cache-busting hash's input list — exactly the silent failure docs/apps.md
§4 step 5 warns about: the file changes, the asset URL does not, and a
browser holding the old page keeps the old copy. Harmless this time only
because search-page.js changed in the same commit and is listed, which is
the worst way for it to go unnoticed. Found by re-reading that checklist
for the doc pass, not by any test — so there is a test now, holding
_ASSETS to every .js in static/ (sw.js excepted, unversioned on purpose).
It was the only one missing.

Phase 9 of docs/refactoring-search.md also lands here: mediacenter.md
§10.6, musicbay.md §9b, photos.md §10b, apps.md §2b and step 5.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01AbwJDbNTkiRUh7HTWEoyss
</content>
</entry>
<entry>
<title>fix(hub): serve the SPA under a fingerprint of what it is</title>
<updated>2026-08-16T18:57:52Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-16T18:57:52Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=5dea19d9950518887be7eb14696f600ee023dbbd'/>
<id>urn:sha1:5dea19d9950518887be7eb14696f600ee023dbbd</id>
<content type='text'>
`Cache-Control: no-cache` requires a browser to revalidate, but it only binds
one that asks. A browser that cached app.js before that header existed applies
heuristic freshness instead — a fraction of the file's age, which for a file
dated weeks ago is days — and never asks. It then runs an old player against a
new node.

That cost most of a session. A phone kept a player without the read-ahead bound
and filled the browser's buffer ceiling at 106 MB, the exact symptom the bound
had been written to remove, for an hour after the bounded player went live. A
fix that is written, tested, deployed and served, and still not what runs, is
indistinguishable from a fix that does not work.

The whole module graph now lives under `/a/&lt;content-hash&gt;/`. A path prefix
rather than a query string, because relative imports inherit it: `app.js`
reaching for `./i18n.js` gets the build it was written against, and never a
mixture of two — which does not render a stale page, it fails to link. The URL
changes with the content, so those may be cached hard.

`sw.js` stays at the root. Its scope is its own path, and under the prefix it
would no longer control the pages whose downloads it exists to intercept.
</content>
</entry>
</feed>
