// 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');