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.
98 lines
6.2 KiB
JavaScript
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');
|