Files
SoulStream/handoff/rounds/ins-r46-smoke-tail.txt
T
KeelrLeek 9f0601027c android: the handoff — the pending rounds, the pipeline, and what is unverified
Everything written this session that has not been through the device: rounds 43
to 48 (each patch adversarially verified, 43's transient-notice expectation
fixed), a partial round 49, the pipeline scripts, the Android Auto design and
the icon comparison mock. Also the two harness changes that were never A/B'd —
adaptive waits, which may only shorten a declared wait and never lengthen it,
and a ship that runs the mock walk and the deploy at the same time. The A/B and
its 255-step baseline are in handoff/tools; main stays at what the phone has
actually walked.
2026-09-14 07:57:41 -05:00

84 lines
5.8 KiB
Plaintext

/* ── the queue window, across the bridge ───────────────────
Round forty-six put a bounded window of the queue into every
card update — the current item and ten either side, each with
its id, title, artist, album, duration and art key, plus where
the current one sits among them — and left MediaService
ignoring every bit of it, because media3's
SimpleBasePlayer.getState() will want a playlist and an index,
and that data should be a round old before anything depends on
it. Which means every check above this one is blind to it: a
window that arrived empty, an index off by one, or a field the
bridge dropped because a key was spelled the wrong way would
each look exactly like success.
So: what the phone SENT — __ss.card, the mirror
media.svelte.js keeps beside xfernote's — against what the
plugin LOGGED on the far side, field for field, through a next
and a previous. Field for field is the point: a camelCase key
spelled snake_case arrives as nothing at all on both hops of
this bridge, with no error anywhere, so `art` (the only
camelCase field an entry has) is compared as well as the size
and the index.
ARMED TO A KNOWN ROW, index one, rather than to wherever the
walk left off. Three things fall out of that and every one of
them is load-bearing: there is a row on either side, so next()
steps instead of stopping at the end and prev() steps instead
of seeking to zero at the top; the track starts at zero, so
prev() is still a step two and a half seconds later rather
than the seek it becomes past five, and no track ends
underneath the three readings; and while the queue index is
under ten the window's own index IS the queue index, because
the slice starts at zero and nothing is clamped off the front
— which is what makes `got.i` comparable to `base` below
without recomputing the arithmetic here. Nothing is added,
removed or reordered: this is the queue the walk has been
playing all along, stepped one row forward and one row back
inside itself.
Not skipped when it cannot be armed: a queue shorter than
three rows, or repeat-one — under which next() seeks to zero
instead of stepping — FAILS, with `armed` in the message
saying which. A check that passes by not running is the thing
this walk is most careful never to be. */
const CARD = `(function () { var s = window.__ss; if (!s || !s.card || !s.player) return null; var c = s.card, p = s.player; return { u: c.updates, n: c.window, i: c.index, art: c.art, at: c.at, qi: p.qi, title: p.track ? p.track.title : '' }; })()`;
const armed = await cdp.evaluate(`(function () { var p = window.__ss && __ss.player; return p ? { n: p.queue.length, qi: p.qi, rep: p.repeat } : null; })()`);
const base = 1;
const room = (armed?.n ?? 0) >= 3 && armed.rep !== 2;
const wins = [];
if (room) {
await cdp.evaluate(`__ss.playQueueAt(${base})`);
await sleep(2500);
wins.push(["here", await cdp.evaluate(CARD), mediaWindow()]);
await cdp.evaluate(`__ss.next()`);
await sleep(2500);
wins.push(["next", await cdp.evaluate(CARD), mediaWindow()]);
await cdp.evaluate(`__ss.prev()`);
await sleep(2500);
wins.push(["prev", await cdp.evaluate(CARD), mediaWindow()]);
}
/* each reading: the window SENT is a real one — at least two
entries, the index inside it, and the entry AT the index is the
track on the bar, which is what an off-by-one fails even when
the counts agree — and what the plugin LOGGED is that same
window, field for field. */
const sane = ([, sent, got]) => !!sent && sent.n >= 2 && sent.i >= 0 && sent.i < sent.n
&& !!sent.at && sent.at === sent.title
&& got.n === sent.n && got.i === sent.i && got.art === sent.art
&& got.at === String(sent.at).trim();
const winOk = room && wins.length === 3 && wins.every(sane)
/* and it MOVED. The queue index steps forward and back; the
LOGGED window index steps with it, which is the assertion
an off-by-one or a frozen index dies on. And each reading
is a FRESH one at both ends — the phone pushed again
(`u`) and the plugin logged again (`lines`) — because a
prev() that lands back on the row it started from reads
identically to a stale line otherwise. */
&& wins[0][1].qi === base && wins[1][1].qi === base + 1 && wins[2][1].qi === base
&& wins[0][2].i === base && wins[1][2].i === base + 1 && wins[2][2].i === base
&& wins[1][1].u > wins[0][1].u && wins[2][1].u > wins[1][1].u
&& wins[1][2].lines > wins[0][2].lines && wins[2][2].lines > wins[1][2].lines;
if (!winOk) problems.push("EXPECT queue-window " + JSON.stringify({ armed, base, wins }));
console.log((winOk ? " ok " : " FAIL ") + "queue-window".padEnd(16) + " "
+ JSON.stringify({ armed, seen: wins.map(([k, sent, got]) => [k, sent && sent.qi, got.n, got.i, got.art, got.at]) }));