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

118 lines
6.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Round twenty-seven: the phone turned sideways. The UI was drawn
// portrait-first and had never been looked at in landscape; the player sheet
// was the casualty (a cover sized by WIDTH inside a sheet that does not
// scroll, so at 915x411 the transport sat 687px below the fold). Two media
// queries answer it, the shell reads the side insets the cutout and the
// gesture pill take in landscape, and the walk turns the phone over itself:
// `{ view: [w, h] }` is a step kind now.
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);
/* The shell.css block is an APPEND with no anchor, so `once` cannot see a
second run coming: it would simply be written twice. Refuse one here,
before anything is touched. That string exists nowhere but in the block
this round adds. */
if (fs.readFileSync(root + 'src/mobile/shell.css', 'utf8').includes('a phone turned sideways')) {
throw new Error('round 27 is already applied in ' + root + ' — reset the tree rather than re-running');
}
// ── two more insets: sideways, the cutout and the pill are at the sides ──
load('src/mobile/mobile.css');
rep(` --inset-bottom: env(safe-area-inset-bottom, 0px);`,
` --inset-bottom: env(safe-area-inset-bottom, 0px);
/* landscape moves the cutout and the gesture pill to the SIDES, and
viewport-fit=cover puts our content under both */
--inset-left: env(safe-area-inset-left, 0px);
--inset-right: env(safe-area-inset-right, 0px);`);
save();
// ── the look. Appended, and the order is load-bearing: the side-inset
// longhands must come after .gbar's padding shorthand, .mini's left/right
// and .toast's left/right to win at equal specificity.
load('src/mobile/shell.css');
s = s.replace(/\s*$/, NL) + NL + ins('ins-r27-css.txt') + NL;
save();
// ── the sheet says which shape it is in: `small` is already the derived the
// artwork uses, and the landscape grid needs it on .np as a plain class
// rather than a :has()
load('src/mobile/Sheet.svelte');
rep(` <div class="np">`, ` <div class="np" class:small>`);
save();
// ── the walk ──
load('tests/mobile/smoke.mjs');
// the geometry the orientation steps read back
rep(`const PROBE = \`(function () {
var e = document.querySelector('.screen');
return {`,
`const PROBE = \`(function () {
var e = document.querySelector('.screen');
/* geometry, for the orientation steps: box() is the rect or null, and
inside() asks whether every pixel of it is within the viewport —
which, sideways, is the whole question */
function box(sel) { var x = document.querySelector(sel); return x ? x.getBoundingClientRect() : null; }
function inside(b) { return !!b && b.height > 0 && b.top >= -1 && b.bottom <= innerHeight + 1 && b.left >= -1 && b.right <= innerWidth + 1; }
return {`);
rep(` contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),`,
` contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),` + NL + ins('ins-r27-probe.txt'));
// the new step kind, beside the adb one
rep(` /* a phone step can be an adb command — an intent, a key */
if (s.adb) {
try { adb(...s.adb); } catch (e) { problems.push("ADB " + (s.name || s.adb.join(" ")) + ": " + e.message); }
}`,
` /* a phone step can be an adb command — an intent, a key */
if (s.adb) {
try { adb(...s.adb); } catch (e) { problems.push("ADB " + (s.name || s.adb.join(" ")) + ": " + e.message); }
}
/* a mock step can turn the phone over: the viewport, through the
same override the walk boots with. A real device's orientation
is not the harness's to set, so a view step on the phone walk
is a failure rather than a no-op. send() resolves with the
whole reply and does not reject on a protocol error, so a
refused override has to be read out of it. */
if (s.view) {
if (PHONE) problems.push("VIEW " + (s.name || s.view.join("x")) + ": the phone walk must not set the viewport");
else {
try {
const r = await cdp.send("Emulation.setDeviceMetricsOverride", { width: s.view[0], height: s.view[1], deviceScaleFactor: 2, mobile: true });
if (r.error) throw new Error(r.error.message || JSON.stringify(r.error));
} catch (e) { problems.push("VIEW " + (s.name || s.view.join("x")) + ": " + e.message); }
}
}`);
rep(` { do: \`__click('.halfsheet .chip', '30 min')\`, name: "sleep-set", wait: 700 },`,
` { do: \`__click('.halfsheet .chip', '30 min')\`, name: "sleep-set", wait: 700 },` + NL + ins('ins-r27-steps.txt'));
rep(` "sleep-set": (i) => !i.half.includes("up"),`,
` "sleep-set": (i) => !i.half.includes("up"),` + NL + ins('ins-r27-expect.txt'));
save();
// ── the doc ──
load('ANDROID.md');
rep("`linear()`. Reduce motion (You → Appearance, or the OS setting) turns it off." + NL + NL + `## Search and the resolver`,
"`linear()`. Reduce motion (You → Appearance, or the OS setting) turns it off." + NL + NL + ins('ins-r27-doc.txt') + NL + NL + `## Search and the resolver`);
rep(`the sleep timer, asserts what each step should show,`,
`the sleep timer → the phone turned sideways (the player's three faces, a
record with a long sheet over it, an artist page, and the side insets faked,
all at 915×411) and back, asserts what each step should show,`);
rep(`page after a confirmed delete was found).`,
`page after a confirmed delete was found). A mock step can also set the
viewport — \`{ view: [915, 411] }\` sends \`Emulation.setDeviceMetricsOverride\`
through the same client, which is how the walk turns the phone over and back;
the phone walk refuses one, because a real device's orientation is not the
harness's to set.`);
save();
console.log('round 27 wired');