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.
@@ -35,6 +35,18 @@ import path from 'node:path';
35
35
  /** The shape of a journeys file on disk. Version it, because a format nobody versioned is a format nobody can change. */
36
36
  export const JOURNEY_FILE_VERSION = 2;
37
37
 
38
+ /**
39
+ * Where recordings live, relative to the project.
40
+ *
41
+ * One name, because three readers used to spell it out for themselves — the gatherer, the
42
+ * check path and the recording command — and three copies of a folder name is three chances
43
+ * for a recording to be written where nothing looks for it, which reads afterwards exactly
44
+ * like a recording that was never made.
45
+ *
46
+ * It is committed, not ignored: a recording is the promise, not the evidence.
47
+ */
48
+ export const RECORDINGS_DIR = path.join('.staysfixed', 'journeys');
49
+
38
50
  // ---------------------------------------------------------------------------
39
51
  // Keeping secrets out
40
52
  // ---------------------------------------------------------------------------
@@ -493,13 +493,13 @@ export function toolDefinitions() {
493
493
  journeys: {
494
494
  type: 'string',
495
495
  description:
496
- "Where the steps come from. 'code' is the default and needs nothing: each adapter reads your source and offers what it finds - routes, commands, screens, message channels. 'suite' walks the project's own test suite as well: each test file runs twice inside the scratch copy, every check is reported by name, and it stops after 90 seconds naming each file it did not reach. It catches breaks nothing else can - a rounding change the product's own output never shows. It is opt-in because running a stranger's whole suite twice on every check is not something to do by default. You can also pass a path to a journeys file naming steps by hand. 'recorded' (replay a recorded session) is written and not yet wired into a run: ask for it and it says so rather than checking something else.",
496
+ "Where the steps come from. 'code' is the default and needs nothing: each adapter reads your source and offers what it finds - routes, commands, screens, message channels. 'suite' walks the project's own test suite as well: each test file runs twice inside the scratch copy, every check is reported by name, and it stops after 90 seconds naming each file it did not reach. It catches breaks nothing else can - a rounding change the product's own output never shows. It is opt-in because running a stranger's whole suite twice on every check is not something to do by default. 'recorded' replays the sessions somebody recorded with `staysfixed record` and kept in .staysfixed/journeys - the only source that knows which four screens a person actually opens every morning, which reading the source cannot work out. You can also pass a path to a journeys file naming steps by hand.",
497
497
  },
498
498
  surface: {
499
499
  type: 'string',
500
- enum: ['auto', 'cli', 'library', 'server', 'web', 'electron', 'android', 'ios'],
500
+ enum: ['auto', 'cli', 'library', 'server', 'web', 'electron', 'android', 'ios', 'extension'],
501
501
  description:
502
- "What kind of product to aim at. Default 'auto', which uses the settings. 'web' opens the page in a browser of the tool's own — never yours — and reads what the screen says each control is and does. 'electron' opens the desktop app with its own scratch data folder and drives it over its own debugging port. 'android' installs the APK on a virtual device; 'ios' boots the built app on a simulator. Aim it at something this copy or this machine cannot drive and it refuses by name rather than checking something else and reporting that.",
502
+ "What kind of product to aim at. Default 'auto', which uses the settings. 'web' opens the page in a browser of the tool's own — never yours — and reads what the screen says each control is and does. 'electron' opens the desktop app with its own scratch data folder and drives it over its own debugging port. 'android' installs the APK on a virtual device; 'ios' boots the built app on a simulator. 'extension' loads a browser extension into a throwaway browser: its manifest is read as a contract, its popup and options pages are walked like any other page, and what its content scripts do to somebody else's page is measured by opening that page with the extension and without it and comparing the difference — point 'extension.dir' in the settings at the folder you would load unpacked. Aim it at something this copy or this machine cannot drive and it refuses by name rather than checking something else and reporting that.",
503
503
  },
504
504
  at: {
505
505
  type: 'string',
@@ -934,19 +934,12 @@ async function toolCheck(ctx, input) {
934
934
  const limit = positive(input.limit) ?? DEFAULT_LIMIT;
935
935
  const offset = positive(input.offset) ?? 0;
936
936
 
937
- // A value the engine does not understand must be refused BY NAME, never passed down.
938
- //
939
- // `suite` is now wired and reaches the harvest. `recorded` is still written and called by
940
- // nothing, so passing it down would reach the engine as the name of a FILE and come back as
941
- // "there is no journeys file at .../recorded" — an error that sends an agent looking for a
942
- // file it never asked for. Refusing it by name and saying why is the honest answer, and a
943
- // clean result about the wrong steps would be worse than no result.
937
+ // Every word this tool offers now reaches something that walks. `suite` harvests the
938
+ // project's own tests; `recorded` replays the sessions in `.staysfixed/journeys`, which was
939
+ // written and wired to nothing until 2026-08-31 and was refused here by name because of it.
940
+ // A project with no recordings comes back BLOCKED from the engine, naming the folder and the
941
+ // command that makes one - never as a clean result about steps nobody walked.
944
942
  const wantedJourneys = text(input.journeys);
945
- if (wantedJourneys === 'recorded') {
946
- return problem(
947
- 'Replaying a recorded session is written and not wired into a run yet, so nothing was checked. Leave journeys out to use the steps each adapter reads from your source, pass "suite" to walk your own test suite, or pass the path to a journeys file.'
948
- );
949
- }
950
943
 
951
944
  const surface = text(input.surface);
952
945
  const at = text(input.at);
package/src/v2/types.js CHANGED
@@ -39,7 +39,7 @@
39
39
 
40
40
  /**
41
41
  * Where an observation came from, when it matters which platform produced it.
42
- * @typedef {'cli'|'library'|'server'|'web'|'electron'|'android'|'ios'|'windows'} Surface
42
+ * @typedef {'cli'|'library'|'server'|'web'|'electron'|'android'|'ios'|'windows'|'linux'|'macos'|'extension'} Surface
43
43
  */
44
44
 
45
45
  // ---------------------------------------------------------------------------
@@ -56,9 +56,12 @@ export const SURFACE_WORDS = Object.freeze({
56
56
  server: 'Server',
57
57
  web: 'Website',
58
58
  electron: 'Desktop app',
59
+ extension: 'Browser extension',
59
60
  android: 'Android phone',
60
61
  ios: 'iPhone',
61
62
  windows: 'Windows app',
63
+ linux: 'Linux desktop app',
64
+ macos: 'Mac app',
62
65
  });
63
66
 
64
67
  /**
@@ -71,9 +74,12 @@ export const SURFACE_NOTES = Object.freeze({
71
74
  server: 'started on its own port, watched through what it answered',
72
75
  web: 'opened in a browser, watched through what the page says its controls do',
73
76
  electron: 'launched with its own data folder, watched through the app and its channels',
77
+ extension: 'loaded into a throwaway browser, watched through its manifest, its own pages, and what it does to somebody else\'s page',
74
78
  android: 'installed on an emulator, watched through what is on the screen',
75
79
  ios: 'installed on a simulator, watched through what is on the screen',
76
80
  windows: 'driven on a real Windows desktop, watched through what is on the screen',
81
+ linux: 'opened on a real Linux desktop, watched through what the accessibility bus says its controls do',
82
+ macos: 'opened in the background on a Mac, watched through what is on the screen',
77
83
  });
78
84
 
79
85
  /**