staysfixed 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/v2/init.js CHANGED
@@ -1522,14 +1522,20 @@ export function configText(project) {
1522
1522
  const androidHere = android?.adapter === 'android';
1523
1523
  w(' // ───────────────────────────────────────────────────────────────────────');
1524
1524
  w(' // Android apps. Installed on an emulator of its own, walked, then removed.');
1525
- w(' // Compared against the stored record — whether two emulator snapshots come');
1526
- w(' // back byte for byte is unproven, and a run says which mode it used.');
1525
+ w(' // Compared against the stored record, or paired if you name the old build.');
1526
+ w(' // A snapshot restore was measured on 2026-08-31 and repeats: 301 of 309');
1527
+ w(" // addresses agreed across five pairs, and the eight that moved were the app's");
1528
+ w(' // own identity code, which the wobble subtraction already removes.');
1527
1529
  w(' // ───────────────────────────────────────────────────────────────────────');
1528
1530
  w(androidHere ? ' android: {' : ' // android: {');
1529
1531
  const andOn = androidHere ? ' ' : ' // ';
1530
1532
  const apk = android?.suggest?.apk;
1531
1533
  w(`${andOn}// The built package. Left out, it looks in the usual build folders.`);
1532
1534
  w(apk ? `${andOn}apk: ${JSON.stringify(String(apk))},` : `${andOn}// apk: 'app/build/outputs/apk/debug/app-debug.apk',`);
1535
+ w(`${andOn}// A kept copy of the OLD build's package, so a paired run has two builds to compare.`);
1536
+ w(`${andOn}// A checkout of the old commit contains no APK — an APK is a build output — so`);
1537
+ w(`${andOn}// without this a paired run falls back to the stored record and says so.`);
1538
+ w(`${andOn}// reference: 'builds/0.14.0.apk',`);
1533
1539
  w(`${andOn}// Which emulator to use. Left out, it takes the first one that is not a Play Store image.`);
1534
1540
  w(`${andOn}// avd: 'Pixel_7_API_35',`);
1535
1541
  w(`${andOn}// Or a device already plugged in or already running.`);
@@ -1577,6 +1583,10 @@ export function configText(project) {
1577
1583
  w(`${iosOn}// The built app bundle for the simulator. Left out, it looks where builds land.`);
1578
1584
  w(iosApp ? `${iosOn}app: ${JSON.stringify(String(iosApp))},` : `${iosOn}// app: 'build/Debug-iphonesimulator/YourApp.app',`);
1579
1585
  w(`${iosOn}// Which simulator to use, and which system to run it on. Left out, it takes a sensible`);
1586
+ w(`${iosOn}// A kept copy of the OLD build's bundle, so a paired run has two builds to compare.`);
1587
+ w(`${iosOn}// A checkout of the old commit contains no .app — a bundle is a build output — so`);
1588
+ w(`${iosOn}// without this a paired run falls back to the stored record and says so.`);
1589
+ w(`${iosOn}// reference: 'builds/0.14.0.app',`);
1580
1590
  w(`${iosOn}// one and says which.`);
1581
1591
  w(`${iosOn}// deviceType: 'iPhone 17', runtime: 'iOS 26.4',`);
1582
1592
  w(`${iosOn}// Walks through the app. Left out, it opens the app and reads the first screen.`);
@@ -1893,7 +1903,15 @@ export async function run(ctx) {
1893
1903
  const others = (result.plan?.readiness ?? [])
1894
1904
  .map((/** @type {any} */ r) => String(r.product ?? ''))
1895
1905
  .filter((/** @type {string} */ n, /** @type {number} */ i, /** @type {string[]} */ all) => n !== '' && all.indexOf(n) === i);
1896
- if (result.written.length > 0) ok(`Written: ${result.written.map((f) => shortPath(f)).join(', ')}`);
1906
+ if (result.written.length > 0) {
1907
+ ok(`Written: ${result.written.map((f) => shortPath(f)).join(', ')}`);
1908
+ // Worth one line, because of what happens if it is not done. These files are part of the
1909
+ // build now: until they are committed the working tree is not what git has, so the first
1910
+ // reference gets cut from a tree that has no commit of its own — and a later check cannot
1911
+ // put that build back on the machine to walk it live. It falls back to the stored record,
1912
+ // says so, and is weaker for it. One `git add` avoids the whole thing.
1913
+ say('Commit them before you ship. Settings that are not committed leave the first reference tied to a build git does not have, and a later check can then only compare against the record rather than running the old build live.');
1914
+ }
1897
1915
  if (others.length > 1) {
1898
1916
  warn(
1899
1917
  `Those settings describe ONE product. ${others.length} were found here (${others.join(', ')}), and the others are not covered by this file. ` +
@@ -33,7 +33,7 @@ import path from 'node:path';
33
33
  import { measureWobble } from '../observation.js';
34
34
  import { journeysFromCode } from './from-routes.js';
35
35
  import { DEFAULT_HARVEST_BUDGET_MS, harvestJourneys, testsNear } from './from-suite.js';
36
- import { loadJourneyFolder, whatWillNotReplay } from './record.js';
36
+ import { RECORDINGS_DIR, loadJourneyFolder, whatWillNotReplay } from './record.js';
37
37
 
38
38
  /** @typedef {import('../types.js').Journey} Journey */
39
39
  /** @typedef {import('../types.js').JourneySource} JourneySource */
@@ -47,7 +47,7 @@ import { loadJourneyFolder, whatWillNotReplay } from './record.js';
47
47
 
48
48
  export { journeysFromCode, journeysFromDoors, irreversibility } from './from-routes.js';
49
49
  export { detectRunner, harvestJourneys, listTestFiles, testsNear, DEFAULT_HARVEST_BUDGET_MS } from './from-suite.js';
50
- export { startRecording, recordSession, saveJourneys, loadJourneys, loadJourneyFolder, redact } from './record.js';
50
+ export { startRecording, recordSession, saveJourneys, loadJourneys, loadJourneyFolder, redact, RECORDINGS_DIR } from './record.js';
51
51
 
52
52
  /**
53
53
  * A journey with everything this folder knows about where it came from.
@@ -508,7 +508,7 @@ export async function gather(opts) {
508
508
 
509
509
  // ---- recorded sessions ---------------------------------------------------
510
510
  if (opts.recorded !== false) {
511
- const dir = opts.recorded?.dir ?? path.join(root, '.staysfixed', 'journeys');
511
+ const dir = opts.recorded?.dir ?? path.join(root, RECORDINGS_DIR);
512
512
  const loaded = await loadJourneyFolder(dir);
513
513
  for (const journey of loaded.journeys) {
514
514
  const willNotReplay = whatWillNotReplay(journey);