Files
SoulStream/handoff/rounds/ins-r40-phone-expect.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

114 lines
8.7 KiB
Plaintext

/* ── round forty: the four steps that could not fail ───────────────
`library-ambience`, `queue`, `lyrics` and `community` have been
named phone steps with no key in this map since the rounds that
wrote them. The driver reads `expect[s.name] ? expect[s.name](info)
: true`, so each one was a screenshot that always passed — four of
eighty-eight. All four have a mock expectation; none had a phone
one. Each is written to hold against Kyle's own library rather than
a fixture. */
"library-ambience": (i) => i.title.startsWith("Your library") && i.amb.includes("on:img"),
queue: (i) => i.sheet.includes("up") && i.qFace,
/* the walked track may genuinely have no words on the device, so this
asks for the face and any one of its three honest states */
lyrics: (i) => i.sheet.includes("up") && (i.lyrLines >= 1 || !!i.lyrNote || i.lyrTools),
/* tolerant of the sign-in card, which is what a signed-out phone shows
here — but NOT of a toast: a sign-in that failed raises one on this
screen, and that is a finding rather than a step to loosen */
community: (i) => i.title.startsWith("Community") && !i.toast,
/* ── the walk's own playlist, on real SQLite ─────────────────────
The row count is stashed by the sheet step rather than assumed: the
playlist is made out of whichever record the walk picked, so its
length is his library's business — and a probe that read nothing
would otherwise pass every clause below it. */
"playlist-row-sheet": (i) => { globalThis.__plN = i.plrows; return i.half.includes("up") && /Remove from playlist/.test(i.halfText); },
"playlist-removed": (i) => i.plrows === globalThis.__plN - 1 && /Removed/.test(i.notice) && /Undo/.test(i.announce),
/* the undo stash round-tripped through the real backend for the first
time: the row is back, in the place it was taken from. `__pl0` is
the order `playlist-drag-arm` stashed before the two drags, which
`playlist-dragged-back` has already put back. */
"playlist-undone": (i) => i.plrows === globalThis.__plN && !i.notice && (globalThis.__plN < 2 || i.plOrder === globalThis.__pl0),
/* CONDITIONAL: `lift()` refuses a list of fewer than two rows, so a
one-track record — which makes a one-row playlist — has no press to
release. The two drag steps above tolerate exactly the same thing. */
"playlist-drag-held": (i) => globalThis.__plN < 2 || (i.half.includes("up") && /Remove from playlist/.test(i.halfText)),
/* CONDITIONAL in its last clause: the drag-refusal note needs more
than one row to drag. The note itself hangs off the UNFILTERED
list, so a filter that matched nothing still shows it. */
"playlist-filter": (i) => i.plrows === 0 && /Nothing here matches/.test(i.stubText) && (globalThis.__plN < 2 || /Clear the search/.test(i.plNote)),
"playlist-filter-cleared": (i) => i.plrows === globalThis.__plN && !i.plNote,
/* ── the review's Keep switch ────────────────────────────────────
CONDITIONAL: the row renders only when something did not resolve,
and a paste that resolved completely leaves `keepOn` false anyway.
The second step is what guarantees `import-created` below still
sees the three rows it has always seen. */
"import-keep-off": (i) => !i.keepOn && /Create playlist \(\d+ tracks?\)/.test(i.impBtn),
"import-keep-on": (i) => /Create playlist \(3 tracks\)/.test(i.impBtn) && i.imp === 3,
/* ── the library's own shape, on the device for the first time ──── */
/* `a11yBad` is left out for the reason `track-sheet` leaves it out: a
card's name is [title, artist, badge] off his own tags, and `junk()`
calls a name of nothing but digits bad — "1999, 311" is a real
record. The mock asserts it against a fixture, where it belongs.
The structural three stay: nameless, ghost and mis-stated. */
library: (i) => i.title.startsWith("Your library") && i.fmtBadges >= 1 && /hours|min/.test(i.countline)
&& !i.a11yAnon && !i.a11yGhost && !i.a11yState,
/* the sort lives on the Library entry (`c.sort`), not in the config,
so it needs no putting back — the chip after it does that anyway.
What a real library's top format IS cannot be asserted; that the
grid re-sorted and still badges every card can be. */
"library-by-format": (i) => !!i.firstBadge && i.fmtBadges >= 1 && !i.toast && !i.a11yState,
artists: (i) => i.title.startsWith("Your library") && i.rowArt >= 1 && !i.toast,
/* CONDITIONAL: follows live in the library folder and the phone's own
folder starts without any, so a phone with no follows can only be
asked for the stub */
gaps: (i) => i.title.startsWith("Your library") && !i.toast && (i.gaplines >= 1 || !!i.stubText),
/* a LOCAL track's sheet: the three things it offers, the file block
underneath it off a real path, and the layer named and holding
focus. `a11yBad` is left out deliberately — it reads the names of
real rows, and a record of his is allowed to be called anything. */
"track-sheet": (i) => i.half.includes("up") && /Play next/.test(i.halfText) && /Add to queue/.test(i.halfText)
&& /Add to playlist/.test(i.halfText) && /Format/.test(i.halfText) && /Folder/.test(i.halfText)
&& /^dialog\/true\/.+\/focus$/.test(i.dlg) && !i.a11yAnon && !i.a11yGhost,
/* the recents are localStorage on the device, written by the walk's
own earlier search when that screen was left */
"search-recent": (i) => i.title.startsWith("Search") && i.sq === "" && i.recents >= 1,
"search-recent-pick": (i) => i.title.startsWith("Search") && !!i.sq,
"queue-face": (i) => i.sheet.includes("up") && i.qFace,
/* CONDITIONAL: the walked track can be the last queueable row, and
then there is nothing to press. Asserted exactly when there is. */
"queue-row-sheet": (i) => !i.half.includes("up") || (/Remove from queue/.test(i.halfText) && /Play now/.test(i.halfText)),
/* Home's feed on a phone whose library folder holds no follows: the
store answered, and not one of its slots came back with an error */
"home-feed": (i) => /^(Good |Still)/.test(i.title) && !i.toast && i.homeSlots >= 0 && i.homeErr === 0,
"home-more": (i) => i.half.includes("up") && /Sleep timer/.test(i.halfText),
"sleep-sheet": (i) => i.half.includes("up") && /Pause after/.test(i.halfText) && /1½ hours/.test(i.halfText),
/* every chip in that sheet goes through `act()`, which closes it — so
this says nothing at all about the timer. The step after it does. */
"sleep-set": (i) => !i.half.includes("up") && /^(Good |Still)/.test(i.title),
/* the proof: the More sheet's Sleep-timer subtitle IS `sleepLabel()`,
and the only thing that can make it read "N min left" is a
`player.sleepAt` in the future */
"sleep-armed": (i) => i.half.includes("up") && /min left/.test(i.halfText),
"sleep-off": (i) => !i.half.includes("up") && /^(Good |Still)/.test(i.title),
"sleep-cleared": (i) => i.half.includes("up") && /Sleep timer/.test(i.halfText) && !/min left/.test(i.halfText),
/* the retention chip: read, moved, put back. The harness's stash and
the page's own (`window.__keep0`) hold the same value for the same
reason the drag steps keep two — a step's script is fixed before
the walk starts and cannot carry this one. */
"you-keep-arm": (i) => { globalThis.__keepWas = i.keep; return !!i.keep; },
/* CONDITIONAL: the step moves nothing when "Forever" is the chip that
is on, because the way back writes the flag and not the number. The
label is what says which case this is. */
"you-keep": (i) => !!i.keep && (/^Forever/.test(globalThis.__keepWas) ? i.keep === globalThis.__keepWas : i.keep !== globalThis.__keepWas),
"you-keep-back": (i) => i.keep === globalThis.__keepWas,
/* the Wi-Fi-only guard, which exists only because this is a phone */
"wifi-arm": (i) => { globalThis.__wifiWas = i.wifiOnly; return !!i.wifiOnly; },
"wifi-on": (i) => i.wifiOnly === "on",
"wifi-refused": (i) => /Wi-Fi/.test(i.toast),
/* the shadow removed and the sentence taken off the bar, with the
switch still where the step above put it */
"wifi-cleared": (i) => !i.toast && i.wifiOnly === "on",
"wifi-restored": (i) => i.wifiOnly === globalThis.__wifiWas,
/* a port the core refuses: nothing written, and the number the walk
already left standing still standing */
"you-port-refused": (i) => !i.half.includes("up") && /between 1024 and 65535/.test(i.toast) && /^50300 /.test(i.port),