diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 34 |
1 files changed, 33 insertions, 1 deletions
@@ -302,6 +302,35 @@ keypair bundle, or anything that looks like a user's public key. reproduced the defect in one run. Model the environment, never the code under test +- **A hook cannot depend on one declared below it.** `const a = useCallback(fn, + [b])` evaluates `[b]` where it is written, so a `b` further down the component + is still in its temporal dead zone: `ReferenceError` on every render, before + anything the component does can run. The component simply does not appear — + clicking a video did nothing at all, with no error on screen and nothing in + the node's log because nothing was ever requested. It reached production. + `node --check` passes; the code is well-formed. Worse, the MSE harness + extracted the player functions in a list order of its own and therefore + *reordered* them, quietly repairing the one class of defect it was best placed + to catch — it now sorts by position in the file. `test_hook_ordering.py` + checks the whole SPA + +- **Flow-control accounting comes before every early return.** A segment that + arrived is no longer in flight, whatever is then done with it. Discarding one + before decrementing the in-flight window leaked a slot per discard, and + `reinitAt` is asynchronous, so a seek's whole window could arrive while it + was still awaiting its `updateend`s — the player then believed a full window + was in flight, granted no further credit, and the node waited for ever while + logging a stream it had fed perfectly. A race, so it worked twice and hung on + the third try; "it works now" is not evidence against a race, and + `tests/harness/window_leak.mjs` forces the worst case instead + +- **A new stream starts from a known state, and that list grows.** `appendingRef` + and `endedRef` were the first two; `awaitingInitRef` and `seekTargetRef` + repeated the same bug a session later, and worse — only `reinitAt` lowers + `awaitingInit`, and the next film never reaches it, so every one of its + segments would have been discarded. Reset at the *start* of the stream, never + in the teardown of the one before, which is skippable + - **`Cache-Control: no-cache` only binds a browser that asks.** One that cached the SPA before that header existed applies heuristic freshness — a fraction of the file's age, days for a file dated weeks ago — and does not ask at all. A @@ -462,7 +491,10 @@ SFR residential Fedora 44 → meshbay.org OVH VPS: | Asset versioning (hub) | `meshbay_hub.api.webapp` | `_asset_version()` — content hash; whole module graph served under `/a/<hash>/` so a cache cannot mix two builds | | Stream capacity (node) | `meshbay_node.config` | `[node] max_concurrent_streams` (default 8) — a slot is held for the length of a film, so it counts simultaneous viewers | | Stream diagnosis (node) | `webrtc_server.py` | `client_diag` at DEBUG — the player's own view (`ready`, `quota`, `ranges`, `err`) in the node's log. The only window into a phone | -| Stream probe (no browser) | — | `QE/deploy/stream_probe.py` — pulls a real film over real MNP. Answers "is it the node or the browser" in one run (not versioned) | +| Stream probe (no browser) | — | `QE/deploy/stream_probe.py` — pulls a real film over real MNP, `--start` to seek. Answers "is it the node or the browser" in one run (not versioned) | +| Seeking (browser) | `static/app.js` | `requestSeek` → node restarts ffmpeg with `-ss`; `reinitAt` clears the buffer and sets `timestampOffset`. `-copyts` does *not* preserve position — measured — so the offset comes from the client | +| Seeking (node) | `webrtc_server.py` | `start` on `stream_req`; `-ss` **before** `-i` (index seek, not decode-and-discard), clamped away from the end, echoed in `stream_init` | +| Resume position | `static/app.js` | `readResumePosition` / `writeResumePosition` — localStorage, per file, per browser. No protocol, and nothing new learns what you watch | ## meshbay.org server (état cible) |