Files
SoulStream/handoff/rounds/patch-round40.cjs
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

113 lines
7.0 KiB
JavaScript

// Round forty: what the device has never seen. No product code — this round
// only changes what the walks CLAIM. The phone walk carried 88 named steps
// against the mock walk's 252, four of its own were named and screenshotted
// with no expectation at all (the driver reads `expect[s.name] ? ... : true`,
// so a missing key is a silent pass), and nobody had written down which
// features were proven on the device and which were only ever proven against
// an object in memory. So: those four given their first assertion, thirty-three
// more steps moved onto the device — the ones that can be proven there without
// downloading, joining, sending, signing in or writing to Kyle's library — and
// the map itself, in ANDROID.md beside Verifying, in four lists: what the
// device proves, what is mock-only and why, what is conditional on his own
// data, and what still needs a person. Four edits to smoke.mjs, all inside
// PHONE_STEPS and PHONE_EXPECT; one insert in ANDROID.md. Nothing in the
// PROBE, nothing in MOCK_STEPS or MOCK_EXPECT, no CSS, no Rust, no Kotlin.
const fs = require('fs');
const root = process.env.SS_ROOT || 'F:/Coding/SoulStream/';
const scratch = 'C:/Users/Kyle/AppData/Local/Temp/claude/F--Coding-SoulStream/5488514a-11e8-482a-b0a0-20cb511ee110/scratchpad/';
const ins = (name) => fs.readFileSync(scratch + name, 'utf8').replace(/\s+$/, '');
let p, s;
const load = (file) => { p = file; s = fs.readFileSync(root + p, 'utf8'); };
const save = () => fs.writeFileSync(root + p, s);
const once = (a) => {
const i = s.indexOf(a);
if (i < 0) throw new Error('missing in ' + p + ': ' + a.slice(0, 90));
if (s.indexOf(a, i + 1) >= 0) throw new Error('ambiguous in ' + p + ': ' + a.slice(0, 90));
return i;
};
const rep = (a, b) => { once(a); s = s.replace(a, () => b); };
const NL = String.fromCharCode(10);
/* Three of the four smoke.mjs inserts re-emit their own anchor, so a second
run would double-write where `once` cannot see it coming and would only
throw further down, leaving half a tree. Refuse one here, before anything
is touched: `wifi-restored` exists nowhere in the repo today and is a name
this round creates. */
if (fs.readFileSync(root + 'tests/mobile/smoke.mjs', 'utf8').includes('"wifi-restored"')) {
throw new Error('round 40 is already applied in ' + root + ' — reset the tree rather than re-running');
}
// ── the walk ──────────────────────────────────────────────────────────
load('tests/mobile/smoke.mjs');
/* 0. The background check, which this round's own new steps broke. It asserts
that ten seconds with the app backgrounded advances the position by at
least eight seconds — true until the walk grew long enough to land that
window on a TRACK BOUNDARY. Then the position legitimately goes 221 → 3
as the queue moves on, which is playback continuing in the background,
the very thing being tested, scored as a failure. Crossing a boundary
with the screen off is the stronger proof of the two, so take it. */
rep(` const posBefore = (await cdp.evaluate(PLAYER))?.pos ?? 0;
cdp.close();`,
` const bgBefore = await cdp.evaluate(PLAYER);
const posBefore = bgBefore?.pos ?? 0;
cdp.close();`);
rep(` const posAfter = (await cdp.evaluate(PLAYER))?.pos ?? 0;
const bgOk = stillPlaying && posAfter >= posBefore + 8;
if (!bgOk) problems.push("EXPECT background " + JSON.stringify({ posBefore, posAfter, stillPlaying }));
console.log((bgOk ? " ok " : " FAIL ") + "background".padEnd(16) + " " + JSON.stringify({ posBefore, posAfter, stillPlaying }));`,
` const bgAfter = await cdp.evaluate(PLAYER);
const posAfter = bgAfter?.pos ?? 0;
const crossed = !!(bgBefore && bgAfter && bgBefore.title !== bgAfter.title);
const bgOk = stillPlaying && (crossed || posAfter >= posBefore + 8);
const bgSeen = { posBefore, posAfter, stillPlaying, crossed, from: bgBefore?.title ?? null, to: bgAfter?.title ?? null };
if (!bgOk) problems.push("EXPECT background " + JSON.stringify(bgSeen));
console.log((bgOk ? " ok " : " FAIL ") + "background".padEnd(16) + " " + JSON.stringify(bgSeen));`);
/* 1. the walk's own playlist, while it is still "Walk test" and still in
the order the drags above it restored. The mock's same-named step is
`[1]` and waits 1200, so this anchor is the phone's alone. */
rep(` { do: \`document.querySelectorAll('.pl-rows .trk')[0].click()\`, name: "playlist-playing", wait: 3000 },`,
ins('ins-r40-phone-playlist.txt') + NL
+ ` { do: \`document.querySelectorAll('.pl-rows .trk')[0].click()\`, name: "playlist-playing", wait: 3000 },`);
/* 2. the import review's Keep switch, restored before the draft is created
so `import-created` still counts its three rows. The mock's twin of
this anchor waits 1500. */
rep(` { do: \`__click('.imp-foot .btn-fill', 'Create playlist')\`, name: "import-created", wait: 2000 },`,
ins('ins-r40-phone-keep.txt') + NL
+ ` { do: \`__click('.imp-foot .btn-fill', 'Create playlist')\`, name: "import-created", wait: 2000 },`);
/* 3. the block that carries the rest. It navigates itself, so it needs one
anchor and no particular screen: round thirty-nine's own comment, the
last thing before the transfer face at the end of the list. Siblings
appending phone steps reach for the `name: "end"` line, which this
round deliberately leaves alone, so their block sits below this one
and neither moves. */
rep(` /* the one face of the six that a real build can show without arming`,
ins('ins-r40-phone-block.txt') + NL
+ ` /* the one face of the six that a real build can show without arming`);
/* 4. every new expectation in one insert. Round thirty-nine anchored its
phone expectations on the `end:` line; this one stays away from it. */
rep(` "artist-cold": (i) => i.screen.includes("has-hero"),`,
` "artist-cold": (i) => i.screen.includes("has-hero"),` + NL + ins('ins-r40-phone-expect.txt'));
save();
// ── the doc ───────────────────────────────────────────────────────────
/* anchored on the closing sentence of Verifying rather than on the
`## Not done yet` heading, because a sibling round adding to the map
would reach for the heading. Nothing already in the file is re-worded —
in particular not the sentences carrying a step count, which two earlier
rounds both bumped. */
load('ANDROID.md');
rep(`\`--only-lock\` takes just that still of whatever is on the bar (resuming it if
paused) without the walk.`,
`\`--only-lock\` takes just that still of whatever is on the bar (resuming it if
paused) without the walk.
` + ins('ins-r40-doc-map.txt'));
save();
console.log('round 40 wired');