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

98 lines
6.2 KiB
JavaScript

// Round thirty-nine: the faces no walk has seen. Round thirty-one built the
// phone's honest failure states and its own verifier named the gap it left —
// six of them shipped with no expectation at all, because neither `loadHome`
// nor `loadLyrics` was reachable from the `__ss` handle the walk drives the
// app through, and both answer a second ask with nothing (one is gated on
// `started`, the other on the track's own `key`), so no amount of tapping
// re-runs them. Four names on the handle — the two stores and their two
// loaders — eight probe fields, and a block of thirty-six mock steps that
// arms each failure with the mock's own weather, drives the face, asserts the
// words a person reads AND that the Rust string is not among them, and then
// disarms. It lands BEFORE the first-run block: that one reloads twice, and
// the mock's follows live in a module-level `let` a reload empties, so after
// it Home's catalogDown and the Gaps shelf are both unreachable. No new UI,
// 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);
/* Several inserts below re-emit their own anchor, so a second run would
double-write where `once` cannot see it coming and only throw further down,
leaving half a tree. Refuse one here, before anything is touched:
`loadLyrics` appears nowhere in Mobile.svelte today and is the first thing
this round writes there. */
if (fs.readFileSync(root + 'src/mobile/Mobile.svelte', 'utf8').includes('loadLyrics')) {
throw new Error('round 39 is already applied in ' + root + ' — reset the tree rather than re-running');
}
/* PROBE is ONE template literal, so a raw backtick anywhere inside it — a
comment included — ends it, and what follows becomes a tagged template:
the file still parses, `node --check` says nothing at all, and smoke.mjs
throws "Cannot read properties of undefined" the moment it is imported.
`${` is the same trap the other way. Both checked here, before a byte
is written. */
for (const bad of ['`', '${']) {
if (ins('ins-r39-probe.txt').includes(bad)) {
throw new Error('ins-r39-probe.txt carries a ' + bad + ' — the probe block lives inside a template literal');
}
}
// ── the seam: two stores and their two loaders, and nothing else ──────
load('src/mobile/Mobile.svelte');
rep(` import { bindHome } from "./home.svelte.js";`,
` import { home, bindHome, loadHome } from "./home.svelte.js";
import { lyr, loadLyrics } from "./lyrics.svelte.js";`);
/* deliberately mid-literal: round thirty-one took the tail of this line and
round thirty-four took the middle of it, so a sibling round appending to
the handle will reach for the tail, which this one does not touch */
rep(`art, catalog, slsk, boot,`, `art, catalog, home, loadHome, lyr, loadLyrics, slsk, boot,`);
save();
// ── the walk ──────────────────────────────────────────────────────────
load('tests/mobile/smoke.mjs');
/* the last line of round thirty-one's own weather probe, NOT the
`contactsLoaded:` line that both thirty-one and thirty-four anchored on */
rep(` lyrTools: !!document.querySelector('.lyr-tools'),`,
` lyrTools: !!document.querySelector('.lyr-tools'),` + NL + ins('ins-r39-probe.txt'));
/* BEFORE the first run, not at the array end: see the block's own comment */
rep(` /* ── the first run, on a device that has just been installed ──────`,
ins('ins-r39-steps.txt') + NL + ` /* ── the first run, on a device that has just been installed ──────`);
rep(` "weather-clear": (i) => !i.toast && !i.why && !i.folderGone && !i.roomsNote && i.slskOn === "online" && /^(Good |Still)/.test(i.title),`,
` "weather-clear": (i) => !i.toast && !i.why && !i.folderGone && !i.roomsNote && i.slskOn === "online" && /^(Good |Still)/.test(i.title),`
+ NL + ins('ins-r39-expect.txt'));
rep(` { do: \`document.querySelectorAll('.nav button')[0].click()\`, name: "end", wait: 800 },`,
ins('ins-r39-phone-steps.txt') + NL + ` { do: \`document.querySelectorAll('.nav button')[0].click()\`, name: "end", wait: 800 },`);
rep(` end: (i) => /^(Good |Still)/.test(i.title),`,
ins('ins-r39-phone-expect.txt') + NL + ` end: (i) => /^(Good |Still)/.test(i.title),`);
save();
// ── the doc ───────────────────────────────────────────────────────────
load('ANDROID.md');
rep(`because the folder is gone — then all of it cleared and the app left as it was
found.`, ins('ins-r39-doc-walk.txt'));
/* the count in the sentence before this one — "Nine mock steps drive them" —
belongs to whichever sibling rounds add weather steps, and two of them do;
this anchor starts AFTER it and says nothing about it, so the number stays
theirs and the phone walk's own clause stays round thirty-one's wording */
rep(`one safe step of its own (\`trouble-words\`: eleven real backend strings through
\`__ss.say\` on the device, every output asserted free of Rust). A mock step can`,
ins('ins-r39-doc-weather.txt'));
/* "Not done yet" keeps every line it has: nothing in it corresponds to this
round. Round thirty-one's gap was recorded by its own verifier, not by the
map, so the closure is written where the failure states are described. */
rep(`toast says it in English.`, ins('ins-r39-doc-seen.txt'));
save();
console.log('round 39 wired');