doc-detective 4.32.0 → 4.33.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.
Files changed (38) hide show
  1. package/dist/common/src/schemas/schemas.json +1288 -126
  2. package/dist/common/src/types/generated/record_v3.d.ts +21 -2
  3. package/dist/common/src/types/generated/record_v3.d.ts.map +1 -1
  4. package/dist/common/src/types/generated/step_v3.d.ts +21 -2
  5. package/dist/common/src/types/generated/step_v3.d.ts.map +1 -1
  6. package/dist/common/src/types/generated/test_v3.d.ts +42 -4
  7. package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
  8. package/dist/core/tests/ffmpegRecorder.d.ts.map +1 -1
  9. package/dist/core/tests/ffmpegRecorder.js +17 -2
  10. package/dist/core/tests/ffmpegRecorder.js.map +1 -1
  11. package/dist/core/tests/recordingCheckpoints.d.ts +76 -0
  12. package/dist/core/tests/recordingCheckpoints.d.ts.map +1 -0
  13. package/dist/core/tests/recordingCheckpoints.js +335 -0
  14. package/dist/core/tests/recordingCheckpoints.js.map +1 -0
  15. package/dist/core/tests/saveScreenshot.d.ts +5 -1
  16. package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
  17. package/dist/core/tests/saveScreenshot.js +46 -12
  18. package/dist/core/tests/saveScreenshot.js.map +1 -1
  19. package/dist/core/tests/startRecording.d.ts.map +1 -1
  20. package/dist/core/tests/startRecording.js +26 -10
  21. package/dist/core/tests/startRecording.js.map +1 -1
  22. package/dist/core/tests/stopRecording.d.ts.map +1 -1
  23. package/dist/core/tests/stopRecording.js +283 -14
  24. package/dist/core/tests/stopRecording.js.map +1 -1
  25. package/dist/core/tests.d.ts.map +1 -1
  26. package/dist/core/tests.js +53 -37
  27. package/dist/core/tests.js.map +1 -1
  28. package/dist/hints/context.d.ts +1 -0
  29. package/dist/hints/context.d.ts.map +1 -1
  30. package/dist/hints/context.js +9 -0
  31. package/dist/hints/context.js.map +1 -1
  32. package/dist/hints/hints.d.ts.map +1 -1
  33. package/dist/hints/hints.js +15 -0
  34. package/dist/hints/hints.js.map +1 -1
  35. package/dist/hints/types.d.ts +8 -0
  36. package/dist/hints/types.d.ts.map +1 -1
  37. package/dist/index.cjs +1909 -325
  38. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -30470,9 +30470,10 @@ var init_schemas = __esm({
30470
30470
  anyOf: [
30471
30471
  {
30472
30472
  title: "Screenshot (simple)",
30473
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
30473
30474
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
30474
30475
  type: "string",
30475
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30476
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30476
30477
  transform: [
30477
30478
  "trim"
30478
30479
  ]
@@ -30706,9 +30707,10 @@ var init_schemas = __esm({
30706
30707
  },
30707
30708
  path: {
30708
30709
  title: "Screenshot (simple)",
30710
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
30709
30711
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
30710
30712
  type: "string",
30711
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30713
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30712
30714
  transform: [
30713
30715
  "trim"
30714
30716
  ]
@@ -30966,9 +30968,10 @@ var init_schemas = __esm({
30966
30968
  schemas: {
30967
30969
  path: {
30968
30970
  title: "Screenshot (simple)",
30971
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
30969
30972
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
30970
30973
  type: "string",
30971
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30974
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30972
30975
  transform: [
30973
30976
  "trim"
30974
30977
  ]
@@ -31202,9 +31205,10 @@ var init_schemas = __esm({
31202
31205
  },
31203
31206
  path: {
31204
31207
  title: "Screenshot (simple)",
31208
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
31205
31209
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
31206
31210
  type: "string",
31207
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31211
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31208
31212
  transform: [
31209
31213
  "trim"
31210
31214
  ]
@@ -31678,9 +31682,10 @@ var init_schemas = __esm({
31678
31682
  },
31679
31683
  path: {
31680
31684
  title: "Screenshot (simple)",
31685
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
31681
31686
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
31682
31687
  type: "string",
31683
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31688
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31684
31689
  transform: [
31685
31690
  "trim"
31686
31691
  ]
@@ -32062,6 +32067,7 @@ var init_schemas = __esm({
32062
32067
  "image.png",
32063
32068
  "static/images/image.png",
32064
32069
  "/User/manny/projects/doc-detective/static/images/image.png",
32070
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
32065
32071
  "https://example.com/static/images/image.png",
32066
32072
  {
32067
32073
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -34496,10 +34502,11 @@ var init_schemas = __esm({
34496
34502
  },
34497
34503
  overwrite: {
34498
34504
  type: "string",
34499
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
34505
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
34500
34506
  enum: [
34501
34507
  "true",
34502
- "false"
34508
+ "false",
34509
+ "aboveVariation"
34503
34510
  ]
34504
34511
  },
34505
34512
  name: {
@@ -34557,6 +34564,38 @@ var init_schemas = __esm({
34557
34564
  }
34558
34565
  }
34559
34566
  ]
34567
+ },
34568
+ checkpoints: {
34569
+ title: "Recording checkpoints",
34570
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
34571
+ anyOf: [
34572
+ {
34573
+ type: "boolean",
34574
+ title: "Recording checkpoints (boolean)",
34575
+ description: "If `true`, enables checkpoints with default settings."
34576
+ },
34577
+ {
34578
+ type: "object",
34579
+ title: "Recording checkpoints (detailed)",
34580
+ additionalProperties: false,
34581
+ properties: {
34582
+ maxVariation: {
34583
+ type: "number",
34584
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
34585
+ default: 0.05,
34586
+ minimum: 0,
34587
+ maximum: 1
34588
+ },
34589
+ directory: {
34590
+ type: "string",
34591
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
34592
+ transform: [
34593
+ "trim"
34594
+ ]
34595
+ }
34596
+ }
34597
+ }
34598
+ ]
34560
34599
  }
34561
34600
  },
34562
34601
  title: "Record (detailed)"
@@ -34818,10 +34857,11 @@ var init_schemas = __esm({
34818
34857
  },
34819
34858
  overwrite: {
34820
34859
  type: "string",
34821
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
34860
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
34822
34861
  enum: [
34823
34862
  "true",
34824
- "false"
34863
+ "false",
34864
+ "aboveVariation"
34825
34865
  ]
34826
34866
  },
34827
34867
  name: {
@@ -34879,6 +34919,38 @@ var init_schemas = __esm({
34879
34919
  }
34880
34920
  }
34881
34921
  ]
34922
+ },
34923
+ checkpoints: {
34924
+ title: "Recording checkpoints",
34925
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
34926
+ anyOf: [
34927
+ {
34928
+ type: "boolean",
34929
+ title: "Recording checkpoints (boolean)",
34930
+ description: "If `true`, enables checkpoints with default settings."
34931
+ },
34932
+ {
34933
+ type: "object",
34934
+ title: "Recording checkpoints (detailed)",
34935
+ additionalProperties: false,
34936
+ properties: {
34937
+ maxVariation: {
34938
+ type: "number",
34939
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
34940
+ default: 0.05,
34941
+ minimum: 0,
34942
+ maximum: 1
34943
+ },
34944
+ directory: {
34945
+ type: "string",
34946
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
34947
+ transform: [
34948
+ "trim"
34949
+ ]
34950
+ }
34951
+ }
34952
+ }
34953
+ ]
34882
34954
  }
34883
34955
  },
34884
34956
  title: "Record (detailed)"
@@ -34945,6 +35017,17 @@ var init_schemas = __esm({
34945
35017
  path: "results.mp4",
34946
35018
  engine: "ffmpeg"
34947
35019
  },
35020
+ {
35021
+ path: "results.mp4",
35022
+ checkpoints: true
35023
+ },
35024
+ {
35025
+ path: "results.mp4",
35026
+ checkpoints: {
35027
+ maxVariation: 0.02,
35028
+ directory: "baselines/results"
35029
+ }
35030
+ },
34948
35031
  {
34949
35032
  path: "results.mp4",
34950
35033
  engine: {
@@ -76109,9 +76192,10 @@ var init_schemas = __esm({
76109
76192
  anyOf: [
76110
76193
  {
76111
76194
  title: "Screenshot (simple)",
76195
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
76112
76196
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
76113
76197
  type: "string",
76114
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76198
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76115
76199
  transform: [
76116
76200
  "trim"
76117
76201
  ]
@@ -76345,9 +76429,10 @@ var init_schemas = __esm({
76345
76429
  },
76346
76430
  path: {
76347
76431
  title: "Screenshot (simple)",
76432
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
76348
76433
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
76349
76434
  type: "string",
76350
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76435
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76351
76436
  transform: [
76352
76437
  "trim"
76353
76438
  ]
@@ -76605,9 +76690,10 @@ var init_schemas = __esm({
76605
76690
  schemas: {
76606
76691
  path: {
76607
76692
  title: "Screenshot (simple)",
76693
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
76608
76694
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
76609
76695
  type: "string",
76610
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76696
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76611
76697
  transform: [
76612
76698
  "trim"
76613
76699
  ]
@@ -76841,9 +76927,10 @@ var init_schemas = __esm({
76841
76927
  },
76842
76928
  path: {
76843
76929
  title: "Screenshot (simple)",
76930
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
76844
76931
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
76845
76932
  type: "string",
76846
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76933
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76847
76934
  transform: [
76848
76935
  "trim"
76849
76936
  ]
@@ -77317,9 +77404,10 @@ var init_schemas = __esm({
77317
77404
  },
77318
77405
  path: {
77319
77406
  title: "Screenshot (simple)",
77407
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
77320
77408
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
77321
77409
  type: "string",
77322
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
77410
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
77323
77411
  transform: [
77324
77412
  "trim"
77325
77413
  ]
@@ -77701,6 +77789,7 @@ var init_schemas = __esm({
77701
77789
  "image.png",
77702
77790
  "static/images/image.png",
77703
77791
  "/User/manny/projects/doc-detective/static/images/image.png",
77792
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
77704
77793
  "https://example.com/static/images/image.png",
77705
77794
  {
77706
77795
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -80135,10 +80224,11 @@ var init_schemas = __esm({
80135
80224
  },
80136
80225
  overwrite: {
80137
80226
  type: "string",
80138
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
80227
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
80139
80228
  enum: [
80140
80229
  "true",
80141
- "false"
80230
+ "false",
80231
+ "aboveVariation"
80142
80232
  ]
80143
80233
  },
80144
80234
  name: {
@@ -80196,6 +80286,38 @@ var init_schemas = __esm({
80196
80286
  }
80197
80287
  }
80198
80288
  ]
80289
+ },
80290
+ checkpoints: {
80291
+ title: "Recording checkpoints",
80292
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
80293
+ anyOf: [
80294
+ {
80295
+ type: "boolean",
80296
+ title: "Recording checkpoints (boolean)",
80297
+ description: "If `true`, enables checkpoints with default settings."
80298
+ },
80299
+ {
80300
+ type: "object",
80301
+ title: "Recording checkpoints (detailed)",
80302
+ additionalProperties: false,
80303
+ properties: {
80304
+ maxVariation: {
80305
+ type: "number",
80306
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
80307
+ default: 0.05,
80308
+ minimum: 0,
80309
+ maximum: 1
80310
+ },
80311
+ directory: {
80312
+ type: "string",
80313
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
80314
+ transform: [
80315
+ "trim"
80316
+ ]
80317
+ }
80318
+ }
80319
+ }
80320
+ ]
80199
80321
  }
80200
80322
  },
80201
80323
  title: "Record (detailed)"
@@ -80457,10 +80579,11 @@ var init_schemas = __esm({
80457
80579
  },
80458
80580
  overwrite: {
80459
80581
  type: "string",
80460
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
80582
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
80461
80583
  enum: [
80462
80584
  "true",
80463
- "false"
80585
+ "false",
80586
+ "aboveVariation"
80464
80587
  ]
80465
80588
  },
80466
80589
  name: {
@@ -80518,6 +80641,38 @@ var init_schemas = __esm({
80518
80641
  }
80519
80642
  }
80520
80643
  ]
80644
+ },
80645
+ checkpoints: {
80646
+ title: "Recording checkpoints",
80647
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
80648
+ anyOf: [
80649
+ {
80650
+ type: "boolean",
80651
+ title: "Recording checkpoints (boolean)",
80652
+ description: "If `true`, enables checkpoints with default settings."
80653
+ },
80654
+ {
80655
+ type: "object",
80656
+ title: "Recording checkpoints (detailed)",
80657
+ additionalProperties: false,
80658
+ properties: {
80659
+ maxVariation: {
80660
+ type: "number",
80661
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
80662
+ default: 0.05,
80663
+ minimum: 0,
80664
+ maximum: 1
80665
+ },
80666
+ directory: {
80667
+ type: "string",
80668
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
80669
+ transform: [
80670
+ "trim"
80671
+ ]
80672
+ }
80673
+ }
80674
+ }
80675
+ ]
80521
80676
  }
80522
80677
  },
80523
80678
  title: "Record (detailed)"
@@ -80584,6 +80739,17 @@ var init_schemas = __esm({
80584
80739
  path: "results.mp4",
80585
80740
  engine: "ffmpeg"
80586
80741
  },
80742
+ {
80743
+ path: "results.mp4",
80744
+ checkpoints: true
80745
+ },
80746
+ {
80747
+ path: "results.mp4",
80748
+ checkpoints: {
80749
+ maxVariation: 0.02,
80750
+ directory: "baselines/results"
80751
+ }
80752
+ },
80587
80753
  {
80588
80754
  path: "results.mp4",
80589
80755
  engine: {
@@ -106420,10 +106586,11 @@ var init_schemas = __esm({
106420
106586
  },
106421
106587
  overwrite: {
106422
106588
  type: "string",
106423
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
106589
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
106424
106590
  enum: [
106425
106591
  "true",
106426
- "false"
106592
+ "false",
106593
+ "aboveVariation"
106427
106594
  ]
106428
106595
  },
106429
106596
  name: {
@@ -106481,6 +106648,38 @@ var init_schemas = __esm({
106481
106648
  }
106482
106649
  }
106483
106650
  ]
106651
+ },
106652
+ checkpoints: {
106653
+ title: "Recording checkpoints",
106654
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
106655
+ anyOf: [
106656
+ {
106657
+ type: "boolean",
106658
+ title: "Recording checkpoints (boolean)",
106659
+ description: "If `true`, enables checkpoints with default settings."
106660
+ },
106661
+ {
106662
+ type: "object",
106663
+ title: "Recording checkpoints (detailed)",
106664
+ additionalProperties: false,
106665
+ properties: {
106666
+ maxVariation: {
106667
+ type: "number",
106668
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
106669
+ default: 0.05,
106670
+ minimum: 0,
106671
+ maximum: 1
106672
+ },
106673
+ directory: {
106674
+ type: "string",
106675
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
106676
+ transform: [
106677
+ "trim"
106678
+ ]
106679
+ }
106680
+ }
106681
+ }
106682
+ ]
106484
106683
  }
106485
106684
  },
106486
106685
  title: "Record (detailed)"
@@ -106742,10 +106941,11 @@ var init_schemas = __esm({
106742
106941
  },
106743
106942
  overwrite: {
106744
106943
  type: "string",
106745
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
106944
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
106746
106945
  enum: [
106747
106946
  "true",
106748
- "false"
106947
+ "false",
106948
+ "aboveVariation"
106749
106949
  ]
106750
106950
  },
106751
106951
  name: {
@@ -106803,6 +107003,38 @@ var init_schemas = __esm({
106803
107003
  }
106804
107004
  }
106805
107005
  ]
107006
+ },
107007
+ checkpoints: {
107008
+ title: "Recording checkpoints",
107009
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
107010
+ anyOf: [
107011
+ {
107012
+ type: "boolean",
107013
+ title: "Recording checkpoints (boolean)",
107014
+ description: "If `true`, enables checkpoints with default settings."
107015
+ },
107016
+ {
107017
+ type: "object",
107018
+ title: "Recording checkpoints (detailed)",
107019
+ additionalProperties: false,
107020
+ properties: {
107021
+ maxVariation: {
107022
+ type: "number",
107023
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
107024
+ default: 0.05,
107025
+ minimum: 0,
107026
+ maximum: 1
107027
+ },
107028
+ directory: {
107029
+ type: "string",
107030
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
107031
+ transform: [
107032
+ "trim"
107033
+ ]
107034
+ }
107035
+ }
107036
+ }
107037
+ ]
106806
107038
  }
106807
107039
  },
106808
107040
  title: "Record (detailed)"
@@ -106869,6 +107101,17 @@ var init_schemas = __esm({
106869
107101
  path: "results.mp4",
106870
107102
  engine: "ffmpeg"
106871
107103
  },
107104
+ {
107105
+ path: "results.mp4",
107106
+ checkpoints: true
107107
+ },
107108
+ {
107109
+ path: "results.mp4",
107110
+ checkpoints: {
107111
+ maxVariation: 0.02,
107112
+ directory: "baselines/results"
107113
+ }
107114
+ },
106872
107115
  {
106873
107116
  path: "results.mp4",
106874
107117
  engine: {
@@ -135035,9 +135278,10 @@ var init_schemas = __esm({
135035
135278
  anyOf: [
135036
135279
  {
135037
135280
  title: "Screenshot (simple)",
135281
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
135038
135282
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
135039
135283
  type: "string",
135040
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135284
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135041
135285
  transform: [
135042
135286
  "trim"
135043
135287
  ]
@@ -135271,9 +135515,10 @@ var init_schemas = __esm({
135271
135515
  },
135272
135516
  path: {
135273
135517
  title: "Screenshot (simple)",
135518
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
135274
135519
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
135275
135520
  type: "string",
135276
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135521
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135277
135522
  transform: [
135278
135523
  "trim"
135279
135524
  ]
@@ -135531,9 +135776,10 @@ var init_schemas = __esm({
135531
135776
  schemas: {
135532
135777
  path: {
135533
135778
  title: "Screenshot (simple)",
135779
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
135534
135780
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
135535
135781
  type: "string",
135536
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135782
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135537
135783
  transform: [
135538
135784
  "trim"
135539
135785
  ]
@@ -135767,9 +136013,10 @@ var init_schemas = __esm({
135767
136013
  },
135768
136014
  path: {
135769
136015
  title: "Screenshot (simple)",
136016
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
135770
136017
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
135771
136018
  type: "string",
135772
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136019
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135773
136020
  transform: [
135774
136021
  "trim"
135775
136022
  ]
@@ -136243,9 +136490,10 @@ var init_schemas = __esm({
136243
136490
  },
136244
136491
  path: {
136245
136492
  title: "Screenshot (simple)",
136493
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
136246
136494
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
136247
136495
  type: "string",
136248
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136496
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136249
136497
  transform: [
136250
136498
  "trim"
136251
136499
  ]
@@ -136627,6 +136875,7 @@ var init_schemas = __esm({
136627
136875
  "image.png",
136628
136876
  "static/images/image.png",
136629
136877
  "/User/manny/projects/doc-detective/static/images/image.png",
136878
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
136630
136879
  "https://example.com/static/images/image.png",
136631
136880
  {
136632
136881
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -139061,10 +139310,11 @@ var init_schemas = __esm({
139061
139310
  },
139062
139311
  overwrite: {
139063
139312
  type: "string",
139064
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
139313
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
139065
139314
  enum: [
139066
139315
  "true",
139067
- "false"
139316
+ "false",
139317
+ "aboveVariation"
139068
139318
  ]
139069
139319
  },
139070
139320
  name: {
@@ -139122,6 +139372,38 @@ var init_schemas = __esm({
139122
139372
  }
139123
139373
  }
139124
139374
  ]
139375
+ },
139376
+ checkpoints: {
139377
+ title: "Recording checkpoints",
139378
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
139379
+ anyOf: [
139380
+ {
139381
+ type: "boolean",
139382
+ title: "Recording checkpoints (boolean)",
139383
+ description: "If `true`, enables checkpoints with default settings."
139384
+ },
139385
+ {
139386
+ type: "object",
139387
+ title: "Recording checkpoints (detailed)",
139388
+ additionalProperties: false,
139389
+ properties: {
139390
+ maxVariation: {
139391
+ type: "number",
139392
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
139393
+ default: 0.05,
139394
+ minimum: 0,
139395
+ maximum: 1
139396
+ },
139397
+ directory: {
139398
+ type: "string",
139399
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
139400
+ transform: [
139401
+ "trim"
139402
+ ]
139403
+ }
139404
+ }
139405
+ }
139406
+ ]
139125
139407
  }
139126
139408
  },
139127
139409
  title: "Record (detailed)"
@@ -139383,10 +139665,11 @@ var init_schemas = __esm({
139383
139665
  },
139384
139666
  overwrite: {
139385
139667
  type: "string",
139386
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
139668
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
139387
139669
  enum: [
139388
139670
  "true",
139389
- "false"
139671
+ "false",
139672
+ "aboveVariation"
139390
139673
  ]
139391
139674
  },
139392
139675
  name: {
@@ -139444,6 +139727,38 @@ var init_schemas = __esm({
139444
139727
  }
139445
139728
  }
139446
139729
  ]
139730
+ },
139731
+ checkpoints: {
139732
+ title: "Recording checkpoints",
139733
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
139734
+ anyOf: [
139735
+ {
139736
+ type: "boolean",
139737
+ title: "Recording checkpoints (boolean)",
139738
+ description: "If `true`, enables checkpoints with default settings."
139739
+ },
139740
+ {
139741
+ type: "object",
139742
+ title: "Recording checkpoints (detailed)",
139743
+ additionalProperties: false,
139744
+ properties: {
139745
+ maxVariation: {
139746
+ type: "number",
139747
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
139748
+ default: 0.05,
139749
+ minimum: 0,
139750
+ maximum: 1
139751
+ },
139752
+ directory: {
139753
+ type: "string",
139754
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
139755
+ transform: [
139756
+ "trim"
139757
+ ]
139758
+ }
139759
+ }
139760
+ }
139761
+ ]
139447
139762
  }
139448
139763
  },
139449
139764
  title: "Record (detailed)"
@@ -139510,6 +139825,17 @@ var init_schemas = __esm({
139510
139825
  path: "results.mp4",
139511
139826
  engine: "ffmpeg"
139512
139827
  },
139828
+ {
139829
+ path: "results.mp4",
139830
+ checkpoints: true
139831
+ },
139832
+ {
139833
+ path: "results.mp4",
139834
+ checkpoints: {
139835
+ maxVariation: 0.02,
139836
+ directory: "baselines/results"
139837
+ }
139838
+ },
139513
139839
  {
139514
139840
  path: "results.mp4",
139515
139841
  engine: {
@@ -180674,9 +181000,10 @@ var init_schemas = __esm({
180674
181000
  anyOf: [
180675
181001
  {
180676
181002
  title: "Screenshot (simple)",
181003
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
180677
181004
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
180678
181005
  type: "string",
180679
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181006
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
180680
181007
  transform: [
180681
181008
  "trim"
180682
181009
  ]
@@ -180910,9 +181237,10 @@ var init_schemas = __esm({
180910
181237
  },
180911
181238
  path: {
180912
181239
  title: "Screenshot (simple)",
181240
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
180913
181241
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
180914
181242
  type: "string",
180915
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181243
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
180916
181244
  transform: [
180917
181245
  "trim"
180918
181246
  ]
@@ -181170,9 +181498,10 @@ var init_schemas = __esm({
181170
181498
  schemas: {
181171
181499
  path: {
181172
181500
  title: "Screenshot (simple)",
181501
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
181173
181502
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
181174
181503
  type: "string",
181175
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181504
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181176
181505
  transform: [
181177
181506
  "trim"
181178
181507
  ]
@@ -181406,9 +181735,10 @@ var init_schemas = __esm({
181406
181735
  },
181407
181736
  path: {
181408
181737
  title: "Screenshot (simple)",
181738
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
181409
181739
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
181410
181740
  type: "string",
181411
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181741
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181412
181742
  transform: [
181413
181743
  "trim"
181414
181744
  ]
@@ -181882,9 +182212,10 @@ var init_schemas = __esm({
181882
182212
  },
181883
182213
  path: {
181884
182214
  title: "Screenshot (simple)",
182215
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
181885
182216
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
181886
182217
  type: "string",
181887
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
182218
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181888
182219
  transform: [
181889
182220
  "trim"
181890
182221
  ]
@@ -182266,6 +182597,7 @@ var init_schemas = __esm({
182266
182597
  "image.png",
182267
182598
  "static/images/image.png",
182268
182599
  "/User/manny/projects/doc-detective/static/images/image.png",
182600
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
182269
182601
  "https://example.com/static/images/image.png",
182270
182602
  {
182271
182603
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -184700,10 +185032,11 @@ var init_schemas = __esm({
184700
185032
  },
184701
185033
  overwrite: {
184702
185034
  type: "string",
184703
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
185035
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
184704
185036
  enum: [
184705
185037
  "true",
184706
- "false"
185038
+ "false",
185039
+ "aboveVariation"
184707
185040
  ]
184708
185041
  },
184709
185042
  name: {
@@ -184761,6 +185094,38 @@ var init_schemas = __esm({
184761
185094
  }
184762
185095
  }
184763
185096
  ]
185097
+ },
185098
+ checkpoints: {
185099
+ title: "Recording checkpoints",
185100
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
185101
+ anyOf: [
185102
+ {
185103
+ type: "boolean",
185104
+ title: "Recording checkpoints (boolean)",
185105
+ description: "If `true`, enables checkpoints with default settings."
185106
+ },
185107
+ {
185108
+ type: "object",
185109
+ title: "Recording checkpoints (detailed)",
185110
+ additionalProperties: false,
185111
+ properties: {
185112
+ maxVariation: {
185113
+ type: "number",
185114
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
185115
+ default: 0.05,
185116
+ minimum: 0,
185117
+ maximum: 1
185118
+ },
185119
+ directory: {
185120
+ type: "string",
185121
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
185122
+ transform: [
185123
+ "trim"
185124
+ ]
185125
+ }
185126
+ }
185127
+ }
185128
+ ]
184764
185129
  }
184765
185130
  },
184766
185131
  title: "Record (detailed)"
@@ -185022,10 +185387,11 @@ var init_schemas = __esm({
185022
185387
  },
185023
185388
  overwrite: {
185024
185389
  type: "string",
185025
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
185390
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
185026
185391
  enum: [
185027
185392
  "true",
185028
- "false"
185393
+ "false",
185394
+ "aboveVariation"
185029
185395
  ]
185030
185396
  },
185031
185397
  name: {
@@ -185083,6 +185449,38 @@ var init_schemas = __esm({
185083
185449
  }
185084
185450
  }
185085
185451
  ]
185452
+ },
185453
+ checkpoints: {
185454
+ title: "Recording checkpoints",
185455
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
185456
+ anyOf: [
185457
+ {
185458
+ type: "boolean",
185459
+ title: "Recording checkpoints (boolean)",
185460
+ description: "If `true`, enables checkpoints with default settings."
185461
+ },
185462
+ {
185463
+ type: "object",
185464
+ title: "Recording checkpoints (detailed)",
185465
+ additionalProperties: false,
185466
+ properties: {
185467
+ maxVariation: {
185468
+ type: "number",
185469
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
185470
+ default: 0.05,
185471
+ minimum: 0,
185472
+ maximum: 1
185473
+ },
185474
+ directory: {
185475
+ type: "string",
185476
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
185477
+ transform: [
185478
+ "trim"
185479
+ ]
185480
+ }
185481
+ }
185482
+ }
185483
+ ]
185086
185484
  }
185087
185485
  },
185088
185486
  title: "Record (detailed)"
@@ -185149,6 +185547,17 @@ var init_schemas = __esm({
185149
185547
  path: "results.mp4",
185150
185548
  engine: "ffmpeg"
185151
185549
  },
185550
+ {
185551
+ path: "results.mp4",
185552
+ checkpoints: true
185553
+ },
185554
+ {
185555
+ path: "results.mp4",
185556
+ checkpoints: {
185557
+ maxVariation: 0.02,
185558
+ directory: "baselines/results"
185559
+ }
185560
+ },
185152
185561
  {
185153
185562
  path: "results.mp4",
185154
185563
  engine: {
@@ -228052,9 +228461,10 @@ var init_schemas = __esm({
228052
228461
  anyOf: [
228053
228462
  {
228054
228463
  title: "Screenshot (simple)",
228464
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
228055
228465
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
228056
228466
  type: "string",
228057
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228467
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228058
228468
  transform: [
228059
228469
  "trim"
228060
228470
  ]
@@ -228288,9 +228698,10 @@ var init_schemas = __esm({
228288
228698
  },
228289
228699
  path: {
228290
228700
  title: "Screenshot (simple)",
228701
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
228291
228702
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
228292
228703
  type: "string",
228293
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228704
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228294
228705
  transform: [
228295
228706
  "trim"
228296
228707
  ]
@@ -228548,9 +228959,10 @@ var init_schemas = __esm({
228548
228959
  schemas: {
228549
228960
  path: {
228550
228961
  title: "Screenshot (simple)",
228962
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
228551
228963
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
228552
228964
  type: "string",
228553
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228965
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228554
228966
  transform: [
228555
228967
  "trim"
228556
228968
  ]
@@ -228784,9 +229196,10 @@ var init_schemas = __esm({
228784
229196
  },
228785
229197
  path: {
228786
229198
  title: "Screenshot (simple)",
229199
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
228787
229200
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
228788
229201
  type: "string",
228789
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229202
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228790
229203
  transform: [
228791
229204
  "trim"
228792
229205
  ]
@@ -229260,9 +229673,10 @@ var init_schemas = __esm({
229260
229673
  },
229261
229674
  path: {
229262
229675
  title: "Screenshot (simple)",
229676
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
229263
229677
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
229264
229678
  type: "string",
229265
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229679
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229266
229680
  transform: [
229267
229681
  "trim"
229268
229682
  ]
@@ -229644,6 +230058,7 @@ var init_schemas = __esm({
229644
230058
  "image.png",
229645
230059
  "static/images/image.png",
229646
230060
  "/User/manny/projects/doc-detective/static/images/image.png",
230061
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
229647
230062
  "https://example.com/static/images/image.png",
229648
230063
  {
229649
230064
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -232078,10 +232493,11 @@ var init_schemas = __esm({
232078
232493
  },
232079
232494
  overwrite: {
232080
232495
  type: "string",
232081
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
232496
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
232082
232497
  enum: [
232083
232498
  "true",
232084
- "false"
232499
+ "false",
232500
+ "aboveVariation"
232085
232501
  ]
232086
232502
  },
232087
232503
  name: {
@@ -232139,6 +232555,38 @@ var init_schemas = __esm({
232139
232555
  }
232140
232556
  }
232141
232557
  ]
232558
+ },
232559
+ checkpoints: {
232560
+ title: "Recording checkpoints",
232561
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
232562
+ anyOf: [
232563
+ {
232564
+ type: "boolean",
232565
+ title: "Recording checkpoints (boolean)",
232566
+ description: "If `true`, enables checkpoints with default settings."
232567
+ },
232568
+ {
232569
+ type: "object",
232570
+ title: "Recording checkpoints (detailed)",
232571
+ additionalProperties: false,
232572
+ properties: {
232573
+ maxVariation: {
232574
+ type: "number",
232575
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
232576
+ default: 0.05,
232577
+ minimum: 0,
232578
+ maximum: 1
232579
+ },
232580
+ directory: {
232581
+ type: "string",
232582
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
232583
+ transform: [
232584
+ "trim"
232585
+ ]
232586
+ }
232587
+ }
232588
+ }
232589
+ ]
232142
232590
  }
232143
232591
  },
232144
232592
  title: "Record (detailed)"
@@ -232400,10 +232848,11 @@ var init_schemas = __esm({
232400
232848
  },
232401
232849
  overwrite: {
232402
232850
  type: "string",
232403
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
232851
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
232404
232852
  enum: [
232405
232853
  "true",
232406
- "false"
232854
+ "false",
232855
+ "aboveVariation"
232407
232856
  ]
232408
232857
  },
232409
232858
  name: {
@@ -232461,6 +232910,38 @@ var init_schemas = __esm({
232461
232910
  }
232462
232911
  }
232463
232912
  ]
232913
+ },
232914
+ checkpoints: {
232915
+ title: "Recording checkpoints",
232916
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
232917
+ anyOf: [
232918
+ {
232919
+ type: "boolean",
232920
+ title: "Recording checkpoints (boolean)",
232921
+ description: "If `true`, enables checkpoints with default settings."
232922
+ },
232923
+ {
232924
+ type: "object",
232925
+ title: "Recording checkpoints (detailed)",
232926
+ additionalProperties: false,
232927
+ properties: {
232928
+ maxVariation: {
232929
+ type: "number",
232930
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
232931
+ default: 0.05,
232932
+ minimum: 0,
232933
+ maximum: 1
232934
+ },
232935
+ directory: {
232936
+ type: "string",
232937
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
232938
+ transform: [
232939
+ "trim"
232940
+ ]
232941
+ }
232942
+ }
232943
+ }
232944
+ ]
232464
232945
  }
232465
232946
  },
232466
232947
  title: "Record (detailed)"
@@ -232527,6 +233008,17 @@ var init_schemas = __esm({
232527
233008
  path: "results.mp4",
232528
233009
  engine: "ffmpeg"
232529
233010
  },
233011
+ {
233012
+ path: "results.mp4",
233013
+ checkpoints: true
233014
+ },
233015
+ {
233016
+ path: "results.mp4",
233017
+ checkpoints: {
233018
+ maxVariation: 0.02,
233019
+ directory: "baselines/results"
233020
+ }
233021
+ },
232530
233022
  {
232531
233023
  path: "results.mp4",
232532
233024
  engine: {
@@ -272953,9 +273445,10 @@ var init_schemas = __esm({
272953
273445
  anyOf: [
272954
273446
  {
272955
273447
  title: "Screenshot (simple)",
273448
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
272956
273449
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
272957
273450
  type: "string",
272958
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273451
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
272959
273452
  transform: [
272960
273453
  "trim"
272961
273454
  ]
@@ -273189,9 +273682,10 @@ var init_schemas = __esm({
273189
273682
  },
273190
273683
  path: {
273191
273684
  title: "Screenshot (simple)",
273685
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
273192
273686
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
273193
273687
  type: "string",
273194
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273688
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273195
273689
  transform: [
273196
273690
  "trim"
273197
273691
  ]
@@ -273449,9 +273943,10 @@ var init_schemas = __esm({
273449
273943
  schemas: {
273450
273944
  path: {
273451
273945
  title: "Screenshot (simple)",
273946
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
273452
273947
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
273453
273948
  type: "string",
273454
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273949
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273455
273950
  transform: [
273456
273951
  "trim"
273457
273952
  ]
@@ -273685,9 +274180,10 @@ var init_schemas = __esm({
273685
274180
  },
273686
274181
  path: {
273687
274182
  title: "Screenshot (simple)",
274183
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
273688
274184
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
273689
274185
  type: "string",
273690
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274186
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273691
274187
  transform: [
273692
274188
  "trim"
273693
274189
  ]
@@ -274161,9 +274657,10 @@ var init_schemas = __esm({
274161
274657
  },
274162
274658
  path: {
274163
274659
  title: "Screenshot (simple)",
274660
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
274164
274661
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
274165
274662
  type: "string",
274166
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274663
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274167
274664
  transform: [
274168
274665
  "trim"
274169
274666
  ]
@@ -274545,6 +275042,7 @@ var init_schemas = __esm({
274545
275042
  "image.png",
274546
275043
  "static/images/image.png",
274547
275044
  "/User/manny/projects/doc-detective/static/images/image.png",
275045
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
274548
275046
  "https://example.com/static/images/image.png",
274549
275047
  {
274550
275048
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -276979,10 +277477,11 @@ var init_schemas = __esm({
276979
277477
  },
276980
277478
  overwrite: {
276981
277479
  type: "string",
276982
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
277480
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
276983
277481
  enum: [
276984
277482
  "true",
276985
- "false"
277483
+ "false",
277484
+ "aboveVariation"
276986
277485
  ]
276987
277486
  },
276988
277487
  name: {
@@ -277040,6 +277539,38 @@ var init_schemas = __esm({
277040
277539
  }
277041
277540
  }
277042
277541
  ]
277542
+ },
277543
+ checkpoints: {
277544
+ title: "Recording checkpoints",
277545
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
277546
+ anyOf: [
277547
+ {
277548
+ type: "boolean",
277549
+ title: "Recording checkpoints (boolean)",
277550
+ description: "If `true`, enables checkpoints with default settings."
277551
+ },
277552
+ {
277553
+ type: "object",
277554
+ title: "Recording checkpoints (detailed)",
277555
+ additionalProperties: false,
277556
+ properties: {
277557
+ maxVariation: {
277558
+ type: "number",
277559
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
277560
+ default: 0.05,
277561
+ minimum: 0,
277562
+ maximum: 1
277563
+ },
277564
+ directory: {
277565
+ type: "string",
277566
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
277567
+ transform: [
277568
+ "trim"
277569
+ ]
277570
+ }
277571
+ }
277572
+ }
277573
+ ]
277043
277574
  }
277044
277575
  },
277045
277576
  title: "Record (detailed)"
@@ -277301,10 +277832,11 @@ var init_schemas = __esm({
277301
277832
  },
277302
277833
  overwrite: {
277303
277834
  type: "string",
277304
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
277835
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
277305
277836
  enum: [
277306
277837
  "true",
277307
- "false"
277838
+ "false",
277839
+ "aboveVariation"
277308
277840
  ]
277309
277841
  },
277310
277842
  name: {
@@ -277362,6 +277894,38 @@ var init_schemas = __esm({
277362
277894
  }
277363
277895
  }
277364
277896
  ]
277897
+ },
277898
+ checkpoints: {
277899
+ title: "Recording checkpoints",
277900
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
277901
+ anyOf: [
277902
+ {
277903
+ type: "boolean",
277904
+ title: "Recording checkpoints (boolean)",
277905
+ description: "If `true`, enables checkpoints with default settings."
277906
+ },
277907
+ {
277908
+ type: "object",
277909
+ title: "Recording checkpoints (detailed)",
277910
+ additionalProperties: false,
277911
+ properties: {
277912
+ maxVariation: {
277913
+ type: "number",
277914
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
277915
+ default: 0.05,
277916
+ minimum: 0,
277917
+ maximum: 1
277918
+ },
277919
+ directory: {
277920
+ type: "string",
277921
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
277922
+ transform: [
277923
+ "trim"
277924
+ ]
277925
+ }
277926
+ }
277927
+ }
277928
+ ]
277365
277929
  }
277366
277930
  },
277367
277931
  title: "Record (detailed)"
@@ -277428,6 +277992,17 @@ var init_schemas = __esm({
277428
277992
  path: "results.mp4",
277429
277993
  engine: "ffmpeg"
277430
277994
  },
277995
+ {
277996
+ path: "results.mp4",
277997
+ checkpoints: true
277998
+ },
277999
+ {
278000
+ path: "results.mp4",
278001
+ checkpoints: {
278002
+ maxVariation: 0.02,
278003
+ directory: "baselines/results"
278004
+ }
278005
+ },
277431
278006
  {
277432
278007
  path: "results.mp4",
277433
278008
  engine: {
@@ -320815,9 +321390,10 @@ var init_schemas = __esm({
320815
321390
  anyOf: [
320816
321391
  {
320817
321392
  title: "Screenshot (simple)",
321393
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
320818
321394
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
320819
321395
  type: "string",
320820
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321396
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
320821
321397
  transform: [
320822
321398
  "trim"
320823
321399
  ]
@@ -321051,9 +321627,10 @@ var init_schemas = __esm({
321051
321627
  },
321052
321628
  path: {
321053
321629
  title: "Screenshot (simple)",
321630
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
321054
321631
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
321055
321632
  type: "string",
321056
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321633
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321057
321634
  transform: [
321058
321635
  "trim"
321059
321636
  ]
@@ -321311,9 +321888,10 @@ var init_schemas = __esm({
321311
321888
  schemas: {
321312
321889
  path: {
321313
321890
  title: "Screenshot (simple)",
321891
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
321314
321892
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
321315
321893
  type: "string",
321316
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321894
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321317
321895
  transform: [
321318
321896
  "trim"
321319
321897
  ]
@@ -321547,9 +322125,10 @@ var init_schemas = __esm({
321547
322125
  },
321548
322126
  path: {
321549
322127
  title: "Screenshot (simple)",
322128
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
321550
322129
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
321551
322130
  type: "string",
321552
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322131
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321553
322132
  transform: [
321554
322133
  "trim"
321555
322134
  ]
@@ -322023,9 +322602,10 @@ var init_schemas = __esm({
322023
322602
  },
322024
322603
  path: {
322025
322604
  title: "Screenshot (simple)",
322605
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
322026
322606
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
322027
322607
  type: "string",
322028
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322608
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322029
322609
  transform: [
322030
322610
  "trim"
322031
322611
  ]
@@ -322407,6 +322987,7 @@ var init_schemas = __esm({
322407
322987
  "image.png",
322408
322988
  "static/images/image.png",
322409
322989
  "/User/manny/projects/doc-detective/static/images/image.png",
322990
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
322410
322991
  "https://example.com/static/images/image.png",
322411
322992
  {
322412
322993
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -324841,10 +325422,11 @@ var init_schemas = __esm({
324841
325422
  },
324842
325423
  overwrite: {
324843
325424
  type: "string",
324844
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
325425
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
324845
325426
  enum: [
324846
325427
  "true",
324847
- "false"
325428
+ "false",
325429
+ "aboveVariation"
324848
325430
  ]
324849
325431
  },
324850
325432
  name: {
@@ -324902,6 +325484,38 @@ var init_schemas = __esm({
324902
325484
  }
324903
325485
  }
324904
325486
  ]
325487
+ },
325488
+ checkpoints: {
325489
+ title: "Recording checkpoints",
325490
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
325491
+ anyOf: [
325492
+ {
325493
+ type: "boolean",
325494
+ title: "Recording checkpoints (boolean)",
325495
+ description: "If `true`, enables checkpoints with default settings."
325496
+ },
325497
+ {
325498
+ type: "object",
325499
+ title: "Recording checkpoints (detailed)",
325500
+ additionalProperties: false,
325501
+ properties: {
325502
+ maxVariation: {
325503
+ type: "number",
325504
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
325505
+ default: 0.05,
325506
+ minimum: 0,
325507
+ maximum: 1
325508
+ },
325509
+ directory: {
325510
+ type: "string",
325511
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
325512
+ transform: [
325513
+ "trim"
325514
+ ]
325515
+ }
325516
+ }
325517
+ }
325518
+ ]
324905
325519
  }
324906
325520
  },
324907
325521
  title: "Record (detailed)"
@@ -325163,10 +325777,11 @@ var init_schemas = __esm({
325163
325777
  },
325164
325778
  overwrite: {
325165
325779
  type: "string",
325166
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
325780
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
325167
325781
  enum: [
325168
325782
  "true",
325169
- "false"
325783
+ "false",
325784
+ "aboveVariation"
325170
325785
  ]
325171
325786
  },
325172
325787
  name: {
@@ -325224,6 +325839,38 @@ var init_schemas = __esm({
325224
325839
  }
325225
325840
  }
325226
325841
  ]
325842
+ },
325843
+ checkpoints: {
325844
+ title: "Recording checkpoints",
325845
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
325846
+ anyOf: [
325847
+ {
325848
+ type: "boolean",
325849
+ title: "Recording checkpoints (boolean)",
325850
+ description: "If `true`, enables checkpoints with default settings."
325851
+ },
325852
+ {
325853
+ type: "object",
325854
+ title: "Recording checkpoints (detailed)",
325855
+ additionalProperties: false,
325856
+ properties: {
325857
+ maxVariation: {
325858
+ type: "number",
325859
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
325860
+ default: 0.05,
325861
+ minimum: 0,
325862
+ maximum: 1
325863
+ },
325864
+ directory: {
325865
+ type: "string",
325866
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
325867
+ transform: [
325868
+ "trim"
325869
+ ]
325870
+ }
325871
+ }
325872
+ }
325873
+ ]
325227
325874
  }
325228
325875
  },
325229
325876
  title: "Record (detailed)"
@@ -325290,6 +325937,17 @@ var init_schemas = __esm({
325290
325937
  path: "results.mp4",
325291
325938
  engine: "ffmpeg"
325292
325939
  },
325940
+ {
325941
+ path: "results.mp4",
325942
+ checkpoints: true
325943
+ },
325944
+ {
325945
+ path: "results.mp4",
325946
+ checkpoints: {
325947
+ maxVariation: 0.02,
325948
+ directory: "baselines/results"
325949
+ }
325950
+ },
325293
325951
  {
325294
325952
  path: "results.mp4",
325295
325953
  engine: {
@@ -365716,9 +366374,10 @@ var init_schemas = __esm({
365716
366374
  anyOf: [
365717
366375
  {
365718
366376
  title: "Screenshot (simple)",
366377
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
365719
366378
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
365720
366379
  type: "string",
365721
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366380
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
365722
366381
  transform: [
365723
366382
  "trim"
365724
366383
  ]
@@ -365952,9 +366611,10 @@ var init_schemas = __esm({
365952
366611
  },
365953
366612
  path: {
365954
366613
  title: "Screenshot (simple)",
366614
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
365955
366615
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
365956
366616
  type: "string",
365957
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366617
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
365958
366618
  transform: [
365959
366619
  "trim"
365960
366620
  ]
@@ -366212,9 +366872,10 @@ var init_schemas = __esm({
366212
366872
  schemas: {
366213
366873
  path: {
366214
366874
  title: "Screenshot (simple)",
366875
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
366215
366876
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
366216
366877
  type: "string",
366217
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366878
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366218
366879
  transform: [
366219
366880
  "trim"
366220
366881
  ]
@@ -366448,9 +367109,10 @@ var init_schemas = __esm({
366448
367109
  },
366449
367110
  path: {
366450
367111
  title: "Screenshot (simple)",
367112
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
366451
367113
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
366452
367114
  type: "string",
366453
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367115
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366454
367116
  transform: [
366455
367117
  "trim"
366456
367118
  ]
@@ -366924,9 +367586,10 @@ var init_schemas = __esm({
366924
367586
  },
366925
367587
  path: {
366926
367588
  title: "Screenshot (simple)",
367589
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
366927
367590
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
366928
367591
  type: "string",
366929
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367592
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366930
367593
  transform: [
366931
367594
  "trim"
366932
367595
  ]
@@ -367308,6 +367971,7 @@ var init_schemas = __esm({
367308
367971
  "image.png",
367309
367972
  "static/images/image.png",
367310
367973
  "/User/manny/projects/doc-detective/static/images/image.png",
367974
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
367311
367975
  "https://example.com/static/images/image.png",
367312
367976
  {
367313
367977
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -369742,10 +370406,11 @@ var init_schemas = __esm({
369742
370406
  },
369743
370407
  overwrite: {
369744
370408
  type: "string",
369745
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
370409
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
369746
370410
  enum: [
369747
370411
  "true",
369748
- "false"
370412
+ "false",
370413
+ "aboveVariation"
369749
370414
  ]
369750
370415
  },
369751
370416
  name: {
@@ -369803,6 +370468,38 @@ var init_schemas = __esm({
369803
370468
  }
369804
370469
  }
369805
370470
  ]
370471
+ },
370472
+ checkpoints: {
370473
+ title: "Recording checkpoints",
370474
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
370475
+ anyOf: [
370476
+ {
370477
+ type: "boolean",
370478
+ title: "Recording checkpoints (boolean)",
370479
+ description: "If `true`, enables checkpoints with default settings."
370480
+ },
370481
+ {
370482
+ type: "object",
370483
+ title: "Recording checkpoints (detailed)",
370484
+ additionalProperties: false,
370485
+ properties: {
370486
+ maxVariation: {
370487
+ type: "number",
370488
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
370489
+ default: 0.05,
370490
+ minimum: 0,
370491
+ maximum: 1
370492
+ },
370493
+ directory: {
370494
+ type: "string",
370495
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
370496
+ transform: [
370497
+ "trim"
370498
+ ]
370499
+ }
370500
+ }
370501
+ }
370502
+ ]
369806
370503
  }
369807
370504
  },
369808
370505
  title: "Record (detailed)"
@@ -370064,10 +370761,11 @@ var init_schemas = __esm({
370064
370761
  },
370065
370762
  overwrite: {
370066
370763
  type: "string",
370067
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
370764
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
370068
370765
  enum: [
370069
370766
  "true",
370070
- "false"
370767
+ "false",
370768
+ "aboveVariation"
370071
370769
  ]
370072
370770
  },
370073
370771
  name: {
@@ -370125,6 +370823,38 @@ var init_schemas = __esm({
370125
370823
  }
370126
370824
  }
370127
370825
  ]
370826
+ },
370827
+ checkpoints: {
370828
+ title: "Recording checkpoints",
370829
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
370830
+ anyOf: [
370831
+ {
370832
+ type: "boolean",
370833
+ title: "Recording checkpoints (boolean)",
370834
+ description: "If `true`, enables checkpoints with default settings."
370835
+ },
370836
+ {
370837
+ type: "object",
370838
+ title: "Recording checkpoints (detailed)",
370839
+ additionalProperties: false,
370840
+ properties: {
370841
+ maxVariation: {
370842
+ type: "number",
370843
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
370844
+ default: 0.05,
370845
+ minimum: 0,
370846
+ maximum: 1
370847
+ },
370848
+ directory: {
370849
+ type: "string",
370850
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
370851
+ transform: [
370852
+ "trim"
370853
+ ]
370854
+ }
370855
+ }
370856
+ }
370857
+ ]
370128
370858
  }
370129
370859
  },
370130
370860
  title: "Record (detailed)"
@@ -370191,6 +370921,17 @@ var init_schemas = __esm({
370191
370921
  path: "results.mp4",
370192
370922
  engine: "ffmpeg"
370193
370923
  },
370924
+ {
370925
+ path: "results.mp4",
370926
+ checkpoints: true
370927
+ },
370928
+ {
370929
+ path: "results.mp4",
370930
+ checkpoints: {
370931
+ maxVariation: 0.02,
370932
+ directory: "baselines/results"
370933
+ }
370934
+ },
370194
370935
  {
370195
370936
  path: "results.mp4",
370196
370937
  engine: {
@@ -385727,9 +386468,10 @@ var init_schemas = __esm({
385727
386468
  anyOf: [
385728
386469
  {
385729
386470
  title: "Screenshot (simple)",
386471
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
385730
386472
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
385731
386473
  type: "string",
385732
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386474
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
385733
386475
  transform: [
385734
386476
  "trim"
385735
386477
  ]
@@ -385963,9 +386705,10 @@ var init_schemas = __esm({
385963
386705
  },
385964
386706
  path: {
385965
386707
  title: "Screenshot (simple)",
386708
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
385966
386709
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
385967
386710
  type: "string",
385968
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386711
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
385969
386712
  transform: [
385970
386713
  "trim"
385971
386714
  ]
@@ -386223,9 +386966,10 @@ var init_schemas = __esm({
386223
386966
  schemas: {
386224
386967
  path: {
386225
386968
  title: "Screenshot (simple)",
386969
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
386226
386970
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
386227
386971
  type: "string",
386228
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386972
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386229
386973
  transform: [
386230
386974
  "trim"
386231
386975
  ]
@@ -386459,9 +387203,10 @@ var init_schemas = __esm({
386459
387203
  },
386460
387204
  path: {
386461
387205
  title: "Screenshot (simple)",
387206
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
386462
387207
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
386463
387208
  type: "string",
386464
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
387209
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386465
387210
  transform: [
386466
387211
  "trim"
386467
387212
  ]
@@ -386935,9 +387680,10 @@ var init_schemas = __esm({
386935
387680
  },
386936
387681
  path: {
386937
387682
  title: "Screenshot (simple)",
387683
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
386938
387684
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
386939
387685
  type: "string",
386940
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
387686
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386941
387687
  transform: [
386942
387688
  "trim"
386943
387689
  ]
@@ -387319,6 +388065,7 @@ var init_schemas = __esm({
387319
388065
  "image.png",
387320
388066
  "static/images/image.png",
387321
388067
  "/User/manny/projects/doc-detective/static/images/image.png",
388068
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
387322
388069
  "https://example.com/static/images/image.png",
387323
388070
  {
387324
388071
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -416902,9 +417649,10 @@ var init_schemas = __esm({
416902
417649
  anyOf: [
416903
417650
  {
416904
417651
  title: "Screenshot (simple)",
417652
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
416905
417653
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
416906
417654
  type: "string",
416907
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417655
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
416908
417656
  transform: [
416909
417657
  "trim"
416910
417658
  ]
@@ -417138,9 +417886,10 @@ var init_schemas = __esm({
417138
417886
  },
417139
417887
  path: {
417140
417888
  title: "Screenshot (simple)",
417889
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
417141
417890
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
417142
417891
  type: "string",
417143
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417892
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417144
417893
  transform: [
417145
417894
  "trim"
417146
417895
  ]
@@ -417398,9 +418147,10 @@ var init_schemas = __esm({
417398
418147
  schemas: {
417399
418148
  path: {
417400
418149
  title: "Screenshot (simple)",
418150
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
417401
418151
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
417402
418152
  type: "string",
417403
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418153
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417404
418154
  transform: [
417405
418155
  "trim"
417406
418156
  ]
@@ -417634,9 +418384,10 @@ var init_schemas = __esm({
417634
418384
  },
417635
418385
  path: {
417636
418386
  title: "Screenshot (simple)",
418387
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
417637
418388
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
417638
418389
  type: "string",
417639
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418390
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417640
418391
  transform: [
417641
418392
  "trim"
417642
418393
  ]
@@ -418110,9 +418861,10 @@ var init_schemas = __esm({
418110
418861
  },
418111
418862
  path: {
418112
418863
  title: "Screenshot (simple)",
418864
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
418113
418865
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
418114
418866
  type: "string",
418115
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418867
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418116
418868
  transform: [
418117
418869
  "trim"
418118
418870
  ]
@@ -418494,6 +419246,7 @@ var init_schemas = __esm({
418494
419246
  "image.png",
418495
419247
  "static/images/image.png",
418496
419248
  "/User/manny/projects/doc-detective/static/images/image.png",
419249
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
418497
419250
  "https://example.com/static/images/image.png",
418498
419251
  {
418499
419252
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -420928,10 +421681,11 @@ var init_schemas = __esm({
420928
421681
  },
420929
421682
  overwrite: {
420930
421683
  type: "string",
420931
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
421684
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
420932
421685
  enum: [
420933
421686
  "true",
420934
- "false"
421687
+ "false",
421688
+ "aboveVariation"
420935
421689
  ]
420936
421690
  },
420937
421691
  name: {
@@ -420989,6 +421743,38 @@ var init_schemas = __esm({
420989
421743
  }
420990
421744
  }
420991
421745
  ]
421746
+ },
421747
+ checkpoints: {
421748
+ title: "Recording checkpoints",
421749
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
421750
+ anyOf: [
421751
+ {
421752
+ type: "boolean",
421753
+ title: "Recording checkpoints (boolean)",
421754
+ description: "If `true`, enables checkpoints with default settings."
421755
+ },
421756
+ {
421757
+ type: "object",
421758
+ title: "Recording checkpoints (detailed)",
421759
+ additionalProperties: false,
421760
+ properties: {
421761
+ maxVariation: {
421762
+ type: "number",
421763
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
421764
+ default: 0.05,
421765
+ minimum: 0,
421766
+ maximum: 1
421767
+ },
421768
+ directory: {
421769
+ type: "string",
421770
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
421771
+ transform: [
421772
+ "trim"
421773
+ ]
421774
+ }
421775
+ }
421776
+ }
421777
+ ]
420992
421778
  }
420993
421779
  },
420994
421780
  title: "Record (detailed)"
@@ -421250,10 +422036,11 @@ var init_schemas = __esm({
421250
422036
  },
421251
422037
  overwrite: {
421252
422038
  type: "string",
421253
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
422039
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
421254
422040
  enum: [
421255
422041
  "true",
421256
- "false"
422042
+ "false",
422043
+ "aboveVariation"
421257
422044
  ]
421258
422045
  },
421259
422046
  name: {
@@ -421311,6 +422098,38 @@ var init_schemas = __esm({
421311
422098
  }
421312
422099
  }
421313
422100
  ]
422101
+ },
422102
+ checkpoints: {
422103
+ title: "Recording checkpoints",
422104
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
422105
+ anyOf: [
422106
+ {
422107
+ type: "boolean",
422108
+ title: "Recording checkpoints (boolean)",
422109
+ description: "If `true`, enables checkpoints with default settings."
422110
+ },
422111
+ {
422112
+ type: "object",
422113
+ title: "Recording checkpoints (detailed)",
422114
+ additionalProperties: false,
422115
+ properties: {
422116
+ maxVariation: {
422117
+ type: "number",
422118
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
422119
+ default: 0.05,
422120
+ minimum: 0,
422121
+ maximum: 1
422122
+ },
422123
+ directory: {
422124
+ type: "string",
422125
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
422126
+ transform: [
422127
+ "trim"
422128
+ ]
422129
+ }
422130
+ }
422131
+ }
422132
+ ]
421314
422133
  }
421315
422134
  },
421316
422135
  title: "Record (detailed)"
@@ -421377,6 +422196,17 @@ var init_schemas = __esm({
421377
422196
  path: "results.mp4",
421378
422197
  engine: "ffmpeg"
421379
422198
  },
422199
+ {
422200
+ path: "results.mp4",
422201
+ checkpoints: true
422202
+ },
422203
+ {
422204
+ path: "results.mp4",
422205
+ checkpoints: {
422206
+ maxVariation: 0.02,
422207
+ directory: "baselines/results"
422208
+ }
422209
+ },
421380
422210
  {
421381
422211
  path: "results.mp4",
421382
422212
  engine: {
@@ -461803,9 +462633,10 @@ var init_schemas = __esm({
461803
462633
  anyOf: [
461804
462634
  {
461805
462635
  title: "Screenshot (simple)",
462636
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
461806
462637
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
461807
462638
  type: "string",
461808
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462639
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
461809
462640
  transform: [
461810
462641
  "trim"
461811
462642
  ]
@@ -462039,9 +462870,10 @@ var init_schemas = __esm({
462039
462870
  },
462040
462871
  path: {
462041
462872
  title: "Screenshot (simple)",
462873
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
462042
462874
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
462043
462875
  type: "string",
462044
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462876
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462045
462877
  transform: [
462046
462878
  "trim"
462047
462879
  ]
@@ -462299,9 +463131,10 @@ var init_schemas = __esm({
462299
463131
  schemas: {
462300
463132
  path: {
462301
463133
  title: "Screenshot (simple)",
463134
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
462302
463135
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
462303
463136
  type: "string",
462304
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463137
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462305
463138
  transform: [
462306
463139
  "trim"
462307
463140
  ]
@@ -462535,9 +463368,10 @@ var init_schemas = __esm({
462535
463368
  },
462536
463369
  path: {
462537
463370
  title: "Screenshot (simple)",
463371
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
462538
463372
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
462539
463373
  type: "string",
462540
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463374
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462541
463375
  transform: [
462542
463376
  "trim"
462543
463377
  ]
@@ -463011,9 +463845,10 @@ var init_schemas = __esm({
463011
463845
  },
463012
463846
  path: {
463013
463847
  title: "Screenshot (simple)",
463848
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
463014
463849
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
463015
463850
  type: "string",
463016
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463851
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463017
463852
  transform: [
463018
463853
  "trim"
463019
463854
  ]
@@ -463395,6 +464230,7 @@ var init_schemas = __esm({
463395
464230
  "image.png",
463396
464231
  "static/images/image.png",
463397
464232
  "/User/manny/projects/doc-detective/static/images/image.png",
464233
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
463398
464234
  "https://example.com/static/images/image.png",
463399
464235
  {
463400
464236
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -465829,10 +466665,11 @@ var init_schemas = __esm({
465829
466665
  },
465830
466666
  overwrite: {
465831
466667
  type: "string",
465832
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
466668
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
465833
466669
  enum: [
465834
466670
  "true",
465835
- "false"
466671
+ "false",
466672
+ "aboveVariation"
465836
466673
  ]
465837
466674
  },
465838
466675
  name: {
@@ -465890,6 +466727,38 @@ var init_schemas = __esm({
465890
466727
  }
465891
466728
  }
465892
466729
  ]
466730
+ },
466731
+ checkpoints: {
466732
+ title: "Recording checkpoints",
466733
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
466734
+ anyOf: [
466735
+ {
466736
+ type: "boolean",
466737
+ title: "Recording checkpoints (boolean)",
466738
+ description: "If `true`, enables checkpoints with default settings."
466739
+ },
466740
+ {
466741
+ type: "object",
466742
+ title: "Recording checkpoints (detailed)",
466743
+ additionalProperties: false,
466744
+ properties: {
466745
+ maxVariation: {
466746
+ type: "number",
466747
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
466748
+ default: 0.05,
466749
+ minimum: 0,
466750
+ maximum: 1
466751
+ },
466752
+ directory: {
466753
+ type: "string",
466754
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
466755
+ transform: [
466756
+ "trim"
466757
+ ]
466758
+ }
466759
+ }
466760
+ }
466761
+ ]
465893
466762
  }
465894
466763
  },
465895
466764
  title: "Record (detailed)"
@@ -466151,10 +467020,11 @@ var init_schemas = __esm({
466151
467020
  },
466152
467021
  overwrite: {
466153
467022
  type: "string",
466154
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
467023
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
466155
467024
  enum: [
466156
467025
  "true",
466157
- "false"
467026
+ "false",
467027
+ "aboveVariation"
466158
467028
  ]
466159
467029
  },
466160
467030
  name: {
@@ -466212,6 +467082,38 @@ var init_schemas = __esm({
466212
467082
  }
466213
467083
  }
466214
467084
  ]
467085
+ },
467086
+ checkpoints: {
467087
+ title: "Recording checkpoints",
467088
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
467089
+ anyOf: [
467090
+ {
467091
+ type: "boolean",
467092
+ title: "Recording checkpoints (boolean)",
467093
+ description: "If `true`, enables checkpoints with default settings."
467094
+ },
467095
+ {
467096
+ type: "object",
467097
+ title: "Recording checkpoints (detailed)",
467098
+ additionalProperties: false,
467099
+ properties: {
467100
+ maxVariation: {
467101
+ type: "number",
467102
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
467103
+ default: 0.05,
467104
+ minimum: 0,
467105
+ maximum: 1
467106
+ },
467107
+ directory: {
467108
+ type: "string",
467109
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
467110
+ transform: [
467111
+ "trim"
467112
+ ]
467113
+ }
467114
+ }
467115
+ }
467116
+ ]
466215
467117
  }
466216
467118
  },
466217
467119
  title: "Record (detailed)"
@@ -466278,6 +467180,17 @@ var init_schemas = __esm({
466278
467180
  path: "results.mp4",
466279
467181
  engine: "ffmpeg"
466280
467182
  },
467183
+ {
467184
+ path: "results.mp4",
467185
+ checkpoints: true
467186
+ },
467187
+ {
467188
+ path: "results.mp4",
467189
+ checkpoints: {
467190
+ maxVariation: 0.02,
467191
+ directory: "baselines/results"
467192
+ }
467193
+ },
466281
467194
  {
466282
467195
  path: "results.mp4",
466283
467196
  engine: {
@@ -508396,9 +509309,10 @@ var init_schemas = __esm({
508396
509309
  anyOf: [
508397
509310
  {
508398
509311
  title: "Screenshot (simple)",
509312
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
508399
509313
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
508400
509314
  type: "string",
508401
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509315
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508402
509316
  transform: [
508403
509317
  "trim"
508404
509318
  ]
@@ -508632,9 +509546,10 @@ var init_schemas = __esm({
508632
509546
  },
508633
509547
  path: {
508634
509548
  title: "Screenshot (simple)",
509549
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
508635
509550
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
508636
509551
  type: "string",
508637
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509552
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508638
509553
  transform: [
508639
509554
  "trim"
508640
509555
  ]
@@ -508892,9 +509807,10 @@ var init_schemas = __esm({
508892
509807
  schemas: {
508893
509808
  path: {
508894
509809
  title: "Screenshot (simple)",
509810
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
508895
509811
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
508896
509812
  type: "string",
508897
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509813
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508898
509814
  transform: [
508899
509815
  "trim"
508900
509816
  ]
@@ -509128,9 +510044,10 @@ var init_schemas = __esm({
509128
510044
  },
509129
510045
  path: {
509130
510046
  title: "Screenshot (simple)",
510047
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
509131
510048
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
509132
510049
  type: "string",
509133
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
510050
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509134
510051
  transform: [
509135
510052
  "trim"
509136
510053
  ]
@@ -509604,9 +510521,10 @@ var init_schemas = __esm({
509604
510521
  },
509605
510522
  path: {
509606
510523
  title: "Screenshot (simple)",
510524
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
509607
510525
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
509608
510526
  type: "string",
509609
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
510527
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509610
510528
  transform: [
509611
510529
  "trim"
509612
510530
  ]
@@ -509988,6 +510906,7 @@ var init_schemas = __esm({
509988
510906
  "image.png",
509989
510907
  "static/images/image.png",
509990
510908
  "/User/manny/projects/doc-detective/static/images/image.png",
510909
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
509991
510910
  "https://example.com/static/images/image.png",
509992
510911
  {
509993
510912
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -512422,10 +513341,11 @@ var init_schemas = __esm({
512422
513341
  },
512423
513342
  overwrite: {
512424
513343
  type: "string",
512425
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
513344
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
512426
513345
  enum: [
512427
513346
  "true",
512428
- "false"
513347
+ "false",
513348
+ "aboveVariation"
512429
513349
  ]
512430
513350
  },
512431
513351
  name: {
@@ -512483,6 +513403,38 @@ var init_schemas = __esm({
512483
513403
  }
512484
513404
  }
512485
513405
  ]
513406
+ },
513407
+ checkpoints: {
513408
+ title: "Recording checkpoints",
513409
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
513410
+ anyOf: [
513411
+ {
513412
+ type: "boolean",
513413
+ title: "Recording checkpoints (boolean)",
513414
+ description: "If `true`, enables checkpoints with default settings."
513415
+ },
513416
+ {
513417
+ type: "object",
513418
+ title: "Recording checkpoints (detailed)",
513419
+ additionalProperties: false,
513420
+ properties: {
513421
+ maxVariation: {
513422
+ type: "number",
513423
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
513424
+ default: 0.05,
513425
+ minimum: 0,
513426
+ maximum: 1
513427
+ },
513428
+ directory: {
513429
+ type: "string",
513430
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
513431
+ transform: [
513432
+ "trim"
513433
+ ]
513434
+ }
513435
+ }
513436
+ }
513437
+ ]
512486
513438
  }
512487
513439
  },
512488
513440
  title: "Record (detailed)"
@@ -512744,10 +513696,11 @@ var init_schemas = __esm({
512744
513696
  },
512745
513697
  overwrite: {
512746
513698
  type: "string",
512747
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
513699
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
512748
513700
  enum: [
512749
513701
  "true",
512750
- "false"
513702
+ "false",
513703
+ "aboveVariation"
512751
513704
  ]
512752
513705
  },
512753
513706
  name: {
@@ -512805,6 +513758,38 @@ var init_schemas = __esm({
512805
513758
  }
512806
513759
  }
512807
513760
  ]
513761
+ },
513762
+ checkpoints: {
513763
+ title: "Recording checkpoints",
513764
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
513765
+ anyOf: [
513766
+ {
513767
+ type: "boolean",
513768
+ title: "Recording checkpoints (boolean)",
513769
+ description: "If `true`, enables checkpoints with default settings."
513770
+ },
513771
+ {
513772
+ type: "object",
513773
+ title: "Recording checkpoints (detailed)",
513774
+ additionalProperties: false,
513775
+ properties: {
513776
+ maxVariation: {
513777
+ type: "number",
513778
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
513779
+ default: 0.05,
513780
+ minimum: 0,
513781
+ maximum: 1
513782
+ },
513783
+ directory: {
513784
+ type: "string",
513785
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
513786
+ transform: [
513787
+ "trim"
513788
+ ]
513789
+ }
513790
+ }
513791
+ }
513792
+ ]
512808
513793
  }
512809
513794
  },
512810
513795
  title: "Record (detailed)"
@@ -512871,6 +513856,17 @@ var init_schemas = __esm({
512871
513856
  path: "results.mp4",
512872
513857
  engine: "ffmpeg"
512873
513858
  },
513859
+ {
513860
+ path: "results.mp4",
513861
+ checkpoints: true
513862
+ },
513863
+ {
513864
+ path: "results.mp4",
513865
+ checkpoints: {
513866
+ maxVariation: 0.02,
513867
+ directory: "baselines/results"
513868
+ }
513869
+ },
512874
513870
  {
512875
513871
  path: "results.mp4",
512876
513872
  engine: {
@@ -556642,9 +557638,10 @@ var init_schemas = __esm({
556642
557638
  anyOf: [
556643
557639
  {
556644
557640
  title: "Screenshot (simple)",
557641
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
556645
557642
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
556646
557643
  type: "string",
556647
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557644
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
556648
557645
  transform: [
556649
557646
  "trim"
556650
557647
  ]
@@ -556878,9 +557875,10 @@ var init_schemas = __esm({
556878
557875
  },
556879
557876
  path: {
556880
557877
  title: "Screenshot (simple)",
557878
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
556881
557879
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
556882
557880
  type: "string",
556883
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557881
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
556884
557882
  transform: [
556885
557883
  "trim"
556886
557884
  ]
@@ -557138,9 +558136,10 @@ var init_schemas = __esm({
557138
558136
  schemas: {
557139
558137
  path: {
557140
558138
  title: "Screenshot (simple)",
558139
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
557141
558140
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
557142
558141
  type: "string",
557143
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
558142
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557144
558143
  transform: [
557145
558144
  "trim"
557146
558145
  ]
@@ -557374,9 +558373,10 @@ var init_schemas = __esm({
557374
558373
  },
557375
558374
  path: {
557376
558375
  title: "Screenshot (simple)",
558376
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
557377
558377
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
557378
558378
  type: "string",
557379
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
558379
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557380
558380
  transform: [
557381
558381
  "trim"
557382
558382
  ]
@@ -557850,9 +558850,10 @@ var init_schemas = __esm({
557850
558850
  },
557851
558851
  path: {
557852
558852
  title: "Screenshot (simple)",
558853
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
557853
558854
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
557854
558855
  type: "string",
557855
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
558856
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557856
558857
  transform: [
557857
558858
  "trim"
557858
558859
  ]
@@ -558234,6 +559235,7 @@ var init_schemas = __esm({
558234
559235
  "image.png",
558235
559236
  "static/images/image.png",
558236
559237
  "/User/manny/projects/doc-detective/static/images/image.png",
559238
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
558237
559239
  "https://example.com/static/images/image.png",
558238
559240
  {
558239
559241
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -560668,10 +561670,11 @@ var init_schemas = __esm({
560668
561670
  },
560669
561671
  overwrite: {
560670
561672
  type: "string",
560671
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
561673
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
560672
561674
  enum: [
560673
561675
  "true",
560674
- "false"
561676
+ "false",
561677
+ "aboveVariation"
560675
561678
  ]
560676
561679
  },
560677
561680
  name: {
@@ -560729,6 +561732,38 @@ var init_schemas = __esm({
560729
561732
  }
560730
561733
  }
560731
561734
  ]
561735
+ },
561736
+ checkpoints: {
561737
+ title: "Recording checkpoints",
561738
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
561739
+ anyOf: [
561740
+ {
561741
+ type: "boolean",
561742
+ title: "Recording checkpoints (boolean)",
561743
+ description: "If `true`, enables checkpoints with default settings."
561744
+ },
561745
+ {
561746
+ type: "object",
561747
+ title: "Recording checkpoints (detailed)",
561748
+ additionalProperties: false,
561749
+ properties: {
561750
+ maxVariation: {
561751
+ type: "number",
561752
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
561753
+ default: 0.05,
561754
+ minimum: 0,
561755
+ maximum: 1
561756
+ },
561757
+ directory: {
561758
+ type: "string",
561759
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
561760
+ transform: [
561761
+ "trim"
561762
+ ]
561763
+ }
561764
+ }
561765
+ }
561766
+ ]
560732
561767
  }
560733
561768
  },
560734
561769
  title: "Record (detailed)"
@@ -560990,10 +562025,11 @@ var init_schemas = __esm({
560990
562025
  },
560991
562026
  overwrite: {
560992
562027
  type: "string",
560993
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
562028
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
560994
562029
  enum: [
560995
562030
  "true",
560996
- "false"
562031
+ "false",
562032
+ "aboveVariation"
560997
562033
  ]
560998
562034
  },
560999
562035
  name: {
@@ -561051,6 +562087,38 @@ var init_schemas = __esm({
561051
562087
  }
561052
562088
  }
561053
562089
  ]
562090
+ },
562091
+ checkpoints: {
562092
+ title: "Recording checkpoints",
562093
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
562094
+ anyOf: [
562095
+ {
562096
+ type: "boolean",
562097
+ title: "Recording checkpoints (boolean)",
562098
+ description: "If `true`, enables checkpoints with default settings."
562099
+ },
562100
+ {
562101
+ type: "object",
562102
+ title: "Recording checkpoints (detailed)",
562103
+ additionalProperties: false,
562104
+ properties: {
562105
+ maxVariation: {
562106
+ type: "number",
562107
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
562108
+ default: 0.05,
562109
+ minimum: 0,
562110
+ maximum: 1
562111
+ },
562112
+ directory: {
562113
+ type: "string",
562114
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
562115
+ transform: [
562116
+ "trim"
562117
+ ]
562118
+ }
562119
+ }
562120
+ }
562121
+ ]
561054
562122
  }
561055
562123
  },
561056
562124
  title: "Record (detailed)"
@@ -561117,6 +562185,17 @@ var init_schemas = __esm({
561117
562185
  path: "results.mp4",
561118
562186
  engine: "ffmpeg"
561119
562187
  },
562188
+ {
562189
+ path: "results.mp4",
562190
+ checkpoints: true
562191
+ },
562192
+ {
562193
+ path: "results.mp4",
562194
+ checkpoints: {
562195
+ maxVariation: 0.02,
562196
+ directory: "baselines/results"
562197
+ }
562198
+ },
561120
562199
  {
561121
562200
  path: "results.mp4",
561122
562201
  engine: {
@@ -601543,9 +602622,10 @@ var init_schemas = __esm({
601543
602622
  anyOf: [
601544
602623
  {
601545
602624
  title: "Screenshot (simple)",
602625
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
601546
602626
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
601547
602627
  type: "string",
601548
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602628
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
601549
602629
  transform: [
601550
602630
  "trim"
601551
602631
  ]
@@ -601779,9 +602859,10 @@ var init_schemas = __esm({
601779
602859
  },
601780
602860
  path: {
601781
602861
  title: "Screenshot (simple)",
602862
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
601782
602863
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
601783
602864
  type: "string",
601784
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602865
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
601785
602866
  transform: [
601786
602867
  "trim"
601787
602868
  ]
@@ -602039,9 +603120,10 @@ var init_schemas = __esm({
602039
603120
  schemas: {
602040
603121
  path: {
602041
603122
  title: "Screenshot (simple)",
603123
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
602042
603124
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
602043
603125
  type: "string",
602044
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
603126
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602045
603127
  transform: [
602046
603128
  "trim"
602047
603129
  ]
@@ -602275,9 +603357,10 @@ var init_schemas = __esm({
602275
603357
  },
602276
603358
  path: {
602277
603359
  title: "Screenshot (simple)",
603360
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
602278
603361
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
602279
603362
  type: "string",
602280
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
603363
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602281
603364
  transform: [
602282
603365
  "trim"
602283
603366
  ]
@@ -602751,9 +603834,10 @@ var init_schemas = __esm({
602751
603834
  },
602752
603835
  path: {
602753
603836
  title: "Screenshot (simple)",
603837
+ $comment: "The pattern gates the target's EXTENSION, not its characters or location: any path ending in .png/.PNG is accepted, plus an http(s) URL to a PNG (optionally with a query string, for signed URLs) and a `$VAR` reference. It deliberately does not restrict the character set \u2014 an earlier pattern allowed any Windows absolute path but limited non-Windows paths to [A-Za-z0-9_./\\-], so ordinary macOS/Linux projects (a space, parentheses, '~', or non-ASCII anywhere in the path) failed validation, including for the absolute paths doc-detective synthesizes internally for autoScreenshot and recording checkpoints. That charset never bounded WHERE a screenshot could be written (traversal like ../../x.png always passed), so it only made non-Windows projects second-class. See ADR 01077.",
602754
603838
  description: "File path of the PNG file. Accepts absolute paths. If not specified, the file name is the ID of the step. If an `http(s)` URL is supplied, the remote image is downloaded and used as a read-only reference for comparison; the new capture is written to a local run-specific folder instead of being uploaded back to the URL.",
602755
603839
  type: "string",
602756
- pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
603840
+ pattern: "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602757
603841
  transform: [
602758
603842
  "trim"
602759
603843
  ]
@@ -603135,6 +604219,7 @@ var init_schemas = __esm({
603135
604219
  "image.png",
603136
604220
  "static/images/image.png",
603137
604221
  "/User/manny/projects/doc-detective/static/images/image.png",
604222
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
603138
604223
  "https://example.com/static/images/image.png",
603139
604224
  {
603140
604225
  path: "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -605569,10 +606654,11 @@ var init_schemas = __esm({
605569
606654
  },
605570
606655
  overwrite: {
605571
606656
  type: "string",
605572
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
606657
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
605573
606658
  enum: [
605574
606659
  "true",
605575
- "false"
606660
+ "false",
606661
+ "aboveVariation"
605576
606662
  ]
605577
606663
  },
605578
606664
  name: {
@@ -605630,6 +606716,38 @@ var init_schemas = __esm({
605630
606716
  }
605631
606717
  }
605632
606718
  ]
606719
+ },
606720
+ checkpoints: {
606721
+ title: "Recording checkpoints",
606722
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
606723
+ anyOf: [
606724
+ {
606725
+ type: "boolean",
606726
+ title: "Recording checkpoints (boolean)",
606727
+ description: "If `true`, enables checkpoints with default settings."
606728
+ },
606729
+ {
606730
+ type: "object",
606731
+ title: "Recording checkpoints (detailed)",
606732
+ additionalProperties: false,
606733
+ properties: {
606734
+ maxVariation: {
606735
+ type: "number",
606736
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
606737
+ default: 0.05,
606738
+ minimum: 0,
606739
+ maximum: 1
606740
+ },
606741
+ directory: {
606742
+ type: "string",
606743
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
606744
+ transform: [
606745
+ "trim"
606746
+ ]
606747
+ }
606748
+ }
606749
+ }
606750
+ ]
605633
606751
  }
605634
606752
  },
605635
606753
  title: "Record (detailed)"
@@ -605891,10 +607009,11 @@ var init_schemas = __esm({
605891
607009
  },
605892
607010
  overwrite: {
605893
607011
  type: "string",
605894
- description: "If `true`, overwrites the existing recording at `path` if it exists.",
607012
+ description: "If `true`, overwrites the existing recording at `path` if it exists. If `false`, skips the recording when the file already exists. If `aboveVariation`, always records, but replaces the existing file (and its checkpoint baselines) only when the span's checkpoint screenshots show it meaningfully changed \u2014 requires `checkpoints`, so it turns them on with defaults when `checkpoints` is omitted or `false`; set `checkpoints` to an object to tune `maxVariation` or `directory`.",
605895
607013
  enum: [
605896
607014
  "true",
605897
- "false"
607015
+ "false",
607016
+ "aboveVariation"
605898
607017
  ]
605899
607018
  },
605900
607019
  name: {
@@ -605952,6 +607071,38 @@ var init_schemas = __esm({
605952
607071
  }
605953
607072
  }
605954
607073
  ]
607074
+ },
607075
+ checkpoints: {
607076
+ title: "Recording checkpoints",
607077
+ description: "Capture a checkpoint screenshot after every step while this recording is active and compare each against a persistent baseline stored beside the recording (`<path>.checkpoints/` by default). Baselines seed on the first run; on later runs, per-checkpoint variation is reported in the `stopRecord` step's outputs, and variation beyond `maxVariation` surfaces as a WARNING. If `false` or unset, no checkpoints are captured.",
607078
+ anyOf: [
607079
+ {
607080
+ type: "boolean",
607081
+ title: "Recording checkpoints (boolean)",
607082
+ description: "If `true`, enables checkpoints with default settings."
607083
+ },
607084
+ {
607085
+ type: "object",
607086
+ title: "Recording checkpoints (detailed)",
607087
+ additionalProperties: false,
607088
+ properties: {
607089
+ maxVariation: {
607090
+ type: "number",
607091
+ description: "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
607092
+ default: 0.05,
607093
+ minimum: 0,
607094
+ maximum: 1
607095
+ },
607096
+ directory: {
607097
+ type: "string",
607098
+ description: "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
607099
+ transform: [
607100
+ "trim"
607101
+ ]
607102
+ }
607103
+ }
607104
+ }
607105
+ ]
605955
607106
  }
605956
607107
  },
605957
607108
  title: "Record (detailed)"
@@ -606018,6 +607169,17 @@ var init_schemas = __esm({
606018
607169
  path: "results.mp4",
606019
607170
  engine: "ffmpeg"
606020
607171
  },
607172
+ {
607173
+ path: "results.mp4",
607174
+ checkpoints: true
607175
+ },
607176
+ {
607177
+ path: "results.mp4",
607178
+ checkpoints: {
607179
+ maxVariation: 0.02,
607180
+ directory: "baselines/results"
607181
+ }
607182
+ },
606021
607183
  {
606022
607184
  path: "results.mp4",
606023
607185
  engine: {
@@ -634667,12 +635829,12 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634667
635829
  }
634668
635830
  if (!force && existing && isStillFresh(existing.latestCheckedAt, now)) {
634669
635831
  if (existing.latestKnownVersion === existing.installedVersion) {
634670
- const path29 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
634671
- return { path: path29, version: existing.installedVersion, outdated: false };
635832
+ const path30 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
635833
+ return { path: path30, version: existing.installedVersion, outdated: false };
634672
635834
  }
634673
- const path28 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
635835
+ const path29 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
634674
635836
  logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${existing.latestKnownVersion}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
634675
- return { path: path28, version: existing.installedVersion, outdated: true };
635837
+ return { path: path29, version: existing.installedVersion, outdated: true };
634676
635838
  }
634677
635839
  let latest;
634678
635840
  try {
@@ -634680,8 +635842,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634680
635842
  } catch (err) {
634681
635843
  logger(`Channel resolution for ${name} skipped: ${String(err)}`, "debug");
634682
635844
  if (existing) {
634683
- const path28 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
634684
- return { path: path28, version: existing.installedVersion, outdated: false };
635845
+ const path29 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
635846
+ return { path: path29, version: existing.installedVersion, outdated: false };
634685
635847
  }
634686
635848
  throw err;
634687
635849
  }
@@ -634692,8 +635854,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634692
635854
  latestCheckedAt: now.toISOString()
634693
635855
  };
634694
635856
  writeInstalledRecord(record, ctx);
634695
- const path28 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
634696
- return { path: path28, version: latest, outdated: false };
635857
+ const path29 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
635858
+ return { path: path29, version: latest, outdated: false };
634697
635859
  }
634698
635860
  if (!force && existing) {
634699
635861
  record.browsers[name] = {
@@ -634703,8 +635865,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634703
635865
  };
634704
635866
  writeInstalledRecord(record, ctx);
634705
635867
  logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${latest}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
634706
- const path28 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
634707
- return { path: path28, version: existing.installedVersion, outdated: true };
635868
+ const path29 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
635869
+ return { path: path29, version: existing.installedVersion, outdated: true };
634708
635870
  }
634709
635871
  logger(`Installing ${name} ${latest} into ${cacheDir}`, "info");
634710
635872
  await browsersModule.install({
@@ -634712,9 +635874,9 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634712
635874
  buildId: latest,
634713
635875
  cacheDir
634714
635876
  });
634715
- let path27 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
634716
- if (name === "chromedriver" && isAllowedDriverPath(path27)) {
634717
- let verify = await verifyDriverBinary(name, path27, { exec: deps.verifyExec });
635877
+ let path28 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
635878
+ if (name === "chromedriver" && isAllowedDriverPath(path28)) {
635879
+ let verify = await verifyDriverBinary(name, path28, { exec: deps.verifyExec });
634718
635880
  if (!verify.ok) {
634719
635881
  logger(`Installed ${name} ${latest} failed validation (${verify.error}); reinstalling once.`, "warn");
634720
635882
  try {
@@ -634723,11 +635885,11 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634723
635885
  logger(`Failed to prune broken ${name} ${latest}: ${String(err)}`, "debug");
634724
635886
  }
634725
635887
  await browsersModule.install({ browser: name, buildId: latest, cacheDir });
634726
- path27 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
634727
- verify = await verifyDriverBinary(name, path27, { exec: deps.verifyExec });
635888
+ path28 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
635889
+ verify = await verifyDriverBinary(name, path28, { exec: deps.verifyExec });
634728
635890
  }
634729
635891
  if (!verify.ok) {
634730
- throw new Error(`${name} ${latest} is present but non-functional after a reinstall (${verify.error}). It may be a partial or corrupt download; delete ${path27} or reinstall.`);
635892
+ throw new Error(`${name} ${latest} is present but non-functional after a reinstall (${verify.error}). It may be a partial or corrupt download; delete ${path28} or reinstall.`);
634731
635893
  }
634732
635894
  }
634733
635895
  if (existing && existing.installedVersion !== latest) {
@@ -634748,7 +635910,7 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
634748
635910
  latestCheckedAt: now.toISOString()
634749
635911
  };
634750
635912
  writeInstalledRecord(record, ctx);
634751
- return { path: path27, version: latest, outdated: false };
635913
+ return { path: path28, version: latest, outdated: false };
634752
635914
  }
634753
635915
  async function locateExecutable(browsersModule, name, buildId, cacheDir, platform) {
634754
635916
  if (typeof browsersModule.computeExecutablePath === "function") {
@@ -635201,10 +636363,10 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
635201
636363
  if (!operationId) {
635202
636364
  throw new Error("OperationId is required.");
635203
636365
  }
635204
- for (const path27 in definition.paths) {
635205
- for (const method in definition.paths[path27]) {
635206
- if (definition.paths[path27][method].operationId === operationId) {
635207
- const operation = definition.paths[path27][method];
636366
+ for (const path28 in definition.paths) {
636367
+ for (const method in definition.paths[path28]) {
636368
+ if (definition.paths[path28][method].operationId === operationId) {
636369
+ const operation = definition.paths[path28][method];
635208
636370
  if (!server) {
635209
636371
  if (definition.servers && definition.servers.length > 0) {
635210
636372
  server = definition.servers[0].url;
@@ -635212,9 +636374,9 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
635212
636374
  throw new Error("No server URL provided and no servers defined in the OpenAPI definition.");
635213
636375
  }
635214
636376
  }
635215
- const example = await compileExample(operation, server + path27, responseCode, exampleKey);
636377
+ const example = await compileExample(operation, server + path28, responseCode, exampleKey);
635216
636378
  const schemas2 = getSchemas(operation, responseCode);
635217
- return { path: path27, method, definition: operation, schemas: schemas2, example };
636379
+ return { path: path28, method, definition: operation, schemas: schemas2, example };
635218
636380
  }
635219
636381
  }
635220
636382
  }
@@ -635235,15 +636397,15 @@ function getSchemas(definition = {}, responseCode = "") {
635235
636397
  schemas2.response = definition.responses[responseCode].content[Object.keys(definition.responses[responseCode].content)[0]].schema;
635236
636398
  return schemas2;
635237
636399
  }
635238
- async function compileExample(operation = {}, path27 = "", responseCode = "", exampleKey = "") {
636400
+ async function compileExample(operation = {}, path28 = "", responseCode = "", exampleKey = "") {
635239
636401
  if (!operation) {
635240
636402
  throw new Error("Operation is required.");
635241
636403
  }
635242
- if (!path27) {
636404
+ if (!path28) {
635243
636405
  throw new Error("Path is required.");
635244
636406
  }
635245
636407
  let example = {
635246
- url: path27,
636408
+ url: path28,
635247
636409
  request: { parameters: {}, headers: {}, body: {} },
635248
636410
  response: { headers: {}, body: {} }
635249
636411
  };
@@ -639194,11 +640356,11 @@ init_utils();
639194
640356
 
639195
640357
  // dist/core/tests.js
639196
640358
  var import_tree_kill3 = __toESM(require("tree-kill"), 1);
639197
- var import_node_fs29 = __toESM(require("node:fs"), 1);
640359
+ var import_node_fs30 = __toESM(require("node:fs"), 1);
639198
640360
  init_loader();
639199
640361
  init_browsers();
639200
640362
  init_browserStepKeys();
639201
- var import_node_os11 = __toESM(require("node:os"), 1);
640363
+ var import_node_os12 = __toESM(require("node:os"), 1);
639202
640364
  init_utils();
639203
640365
  var import_axios6 = __toESM(require("axios"), 1);
639204
640366
 
@@ -639940,22 +641102,22 @@ function computeWdaKey(xcode, driverVersion) {
639940
641102
  var RUNNER_APP_RELATIVE = import_node_path12.default.join("DerivedData", "Build", "Products", "Debug-iphonesimulator", "WebDriverAgentRunner-Runner.app");
639941
641103
  var PRODUCTS_MARKER = "products.json";
639942
641104
  var LAST_USED_STAMP = "last-used";
639943
- function readProductsMarker(keyDir, fs28) {
641105
+ function readProductsMarker(keyDir, fs29) {
639944
641106
  try {
639945
- const parsed = JSON.parse(String(fs28.readFileSync(import_node_path12.default.join(keyDir, PRODUCTS_MARKER))));
641107
+ const parsed = JSON.parse(String(fs29.readFileSync(import_node_path12.default.join(keyDir, PRODUCTS_MARKER))));
639946
641108
  if (typeof parsed?.key !== "string" || typeof parsed?.driverVersion !== "string") {
639947
641109
  return null;
639948
641110
  }
639949
- if (!fs28.existsSync(import_node_path12.default.join(keyDir, RUNNER_APP_RELATIVE)))
641111
+ if (!fs29.existsSync(import_node_path12.default.join(keyDir, RUNNER_APP_RELATIVE)))
639950
641112
  return null;
639951
641113
  return parsed;
639952
641114
  } catch {
639953
641115
  return null;
639954
641116
  }
639955
641117
  }
639956
- function touchLastUsed(keyDir, fs28, now) {
641118
+ function touchLastUsed(keyDir, fs29, now) {
639957
641119
  try {
639958
- fs28.writeFileSync(import_node_path12.default.join(keyDir, LAST_USED_STAMP), String(now()));
641120
+ fs29.writeFileSync(import_node_path12.default.join(keyDir, LAST_USED_STAMP), String(now()));
639959
641121
  } catch {
639960
641122
  }
639961
641123
  }
@@ -639977,7 +641139,7 @@ function probeXcodeVersionCached() {
639977
641139
  return cachedXcodeProbe;
639978
641140
  }
639979
641141
  function locateManagedWda(options = {}) {
639980
- const { ctx = {}, fs: fs28 = import_node_fs14.default, platform = process.platform, probeXcode = probeXcodeVersionCached, resolveDriverVersion = resolveHeavyDepVersion, now = Date.now } = options;
641142
+ const { ctx = {}, fs: fs29 = import_node_fs14.default, platform = process.platform, probeXcode = probeXcodeVersionCached, resolveDriverVersion = resolveHeavyDepVersion, now = Date.now } = options;
639981
641143
  try {
639982
641144
  if (platform !== "darwin")
639983
641145
  return null;
@@ -639994,10 +641156,10 @@ function locateManagedWda(options = {}) {
639994
641156
  const wdaRoot = import_node_path12.default.normalize(options.wdaRootDir ?? getWdaRoot(ctx));
639995
641157
  const key = computeWdaKey(xcode, driverVersion);
639996
641158
  const keyDir = import_node_path12.default.join(wdaRoot, key);
639997
- const marker = readProductsMarker(keyDir, fs28);
641159
+ const marker = readProductsMarker(keyDir, fs29);
639998
641160
  if (!marker || marker.key !== key)
639999
641161
  return null;
640000
- touchLastUsed(keyDir, fs28, now);
641162
+ touchLastUsed(keyDir, fs29, now);
640001
641163
  return { key, derivedDataPath: import_node_path12.default.join(keyDir, "DerivedData") };
640002
641164
  } catch {
640003
641165
  return null;
@@ -640604,9 +641766,9 @@ function hostAbi(arch = process.arch) {
640604
641766
  }
640605
641767
  var JRE_FEATURE_VERSION = "17";
640606
641768
  function jreDownloadUrl(platform = process.platform, arch = process.arch) {
640607
- const os13 = platform === "win32" ? "windows" : platform === "darwin" ? "mac" : "linux";
641769
+ const os14 = platform === "win32" ? "windows" : platform === "darwin" ? "mac" : "linux";
640608
641770
  const adoptiumArch = arch === "arm64" ? "aarch64" : "x64";
640609
- return `https://api.adoptium.net/v3/binary/latest/${JRE_FEATURE_VERSION}/ga/${os13}/${adoptiumArch}/jre/hotspot/normal/eclipse`;
641771
+ return `https://api.adoptium.net/v3/binary/latest/${JRE_FEATURE_VERSION}/ga/${os14}/${adoptiumArch}/jre/hotspot/normal/eclipse`;
640610
641772
  }
640611
641773
  function jreArchiveFilename(platform = process.platform) {
640612
641774
  return platform === "win32" ? "jre.zip" : "jre.tar.gz";
@@ -641374,8 +642536,8 @@ async function hostHasKvm() {
641374
642536
  if (process.platform !== "linux")
641375
642537
  return false;
641376
642538
  try {
641377
- const fs28 = await import("node:fs");
641378
- fs28.accessSync("/dev/kvm", fs28.constants.R_OK | fs28.constants.W_OK);
642539
+ const fs29 = await import("node:fs");
642540
+ fs29.accessSync("/dev/kvm", fs29.constants.R_OK | fs29.constants.W_OK);
641379
642541
  return true;
641380
642542
  } catch {
641381
642543
  return false;
@@ -643304,11 +644466,14 @@ function safeContextId(contextId) {
643304
644466
  const hash = import_node_crypto6.default.createHash("sha1").update(raw).digest("hex").slice(0, 8);
643305
644467
  return `${base}-${hash}`;
643306
644468
  }
644469
+ function recordingProcessToken() {
644470
+ return String(process.pid);
644471
+ }
643307
644472
  function browserCaptureTitle(contextId) {
643308
- return `RECORD_ME_${safeContextId(contextId)}`;
644473
+ return `RECORD_ME_${recordingProcessToken()}_${safeContextId(contextId)}`;
643309
644474
  }
643310
644475
  function browserDownloadDir(contextId) {
643311
- return import_node_path15.default.join(import_node_os7.default.tmpdir(), "doc-detective", "recordings", safeContextId(contextId));
644476
+ return import_node_path15.default.join(import_node_os7.default.tmpdir(), "doc-detective", "recordings", `${safeContextId(contextId)}-${recordingProcessToken()}`);
643312
644477
  }
643313
644478
  function engineFields(record) {
643314
644479
  const engine = record && typeof record === "object" ? record.engine : void 0;
@@ -644035,11 +645200,11 @@ function hasUnresolvedMetaReference(expression, context) {
644035
645200
  }
644036
645201
  return false;
644037
645202
  }
644038
- function getMetaValue(path27, context) {
645203
+ function getMetaValue(path28, context) {
644039
645204
  if (!context) {
644040
645205
  return void 0;
644041
645206
  }
644042
- const [basePath, jsonPointer] = path27.split("#");
645207
+ const [basePath, jsonPointer] = path28.split("#");
644043
645208
  const resolvedPath = resolvePathTemplateVariables(basePath, context);
644044
645209
  let value = getNestedProperty(context, resolvedPath);
644045
645210
  if (jsonPointer && value) {
@@ -644056,9 +645221,9 @@ function getMetaValue(path27, context) {
644056
645221
  }
644057
645222
  return value;
644058
645223
  }
644059
- function resolvePathTemplateVariables(path27, context) {
645224
+ function resolvePathTemplateVariables(path28, context) {
644060
645225
  const templateRegex = /\{\{(\w+)\}\}/g;
644061
- return path27.replace(templateRegex, (match, varName) => {
645226
+ return path28.replace(templateRegex, (match, varName) => {
644062
645227
  if (context && context.id && varName === "id") {
644063
645228
  return context.id;
644064
645229
  }
@@ -644095,10 +645260,10 @@ async function resolveEmbeddedExpressions(str, context) {
644095
645260
  parts.push(str.slice(lastIdx));
644096
645261
  return parts.join("");
644097
645262
  }
644098
- function getNestedProperty(obj, path27) {
644099
- if (!obj || !path27)
645263
+ function getNestedProperty(obj, path28) {
645264
+ if (!obj || !path28)
644100
645265
  return void 0;
644101
- const parts = path27.split(".");
645266
+ const parts = path28.split(".");
644102
645267
  let current = obj;
644103
645268
  for (const part of parts) {
644104
645269
  if (current === null || current === void 0)
@@ -646327,7 +647492,7 @@ function aspectRatiosMatch(a, b) {
646327
647492
  const rb = b.width / b.height;
646328
647493
  return Math.abs(ra - rb) / Math.max(ra, rb) <= 0.05;
646329
647494
  }
646330
- async function saveScreenshot({ config, step, driver, appSession }) {
647495
+ async function saveScreenshot({ config, step, driver, appSession, internal }) {
646331
647496
  let result = {
646332
647497
  status: "PASS",
646333
647498
  description: "Saved screenshot.",
@@ -646483,7 +647648,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646483
647648
  }
646484
647649
  } else {
646485
647650
  dir = import_node_path17.default.dirname(step.screenshot.path);
646486
- if (!import_node_fs20.default.existsSync(dir)) {
647651
+ if (!internal?.compareOnly && !import_node_fs20.default.existsSync(dir)) {
646487
647652
  import_node_fs20.default.mkdirSync(dir, { recursive: true });
646488
647653
  }
646489
647654
  if (import_node_fs20.default.existsSync(filePath)) {
@@ -646695,6 +647860,10 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646695
647860
  if (!writeFinalPng(filePath))
646696
647861
  return result;
646697
647862
  }
647863
+ if (internal?.compareOnly) {
647864
+ if (!writeFinalPng(internal.capturePath))
647865
+ return result;
647866
+ }
646698
647867
  if (existFilePath) {
646699
647868
  if (step.screenshot.overwrite == "true" && !isUrlPath) {
646700
647869
  if (!writeFinalPng(existFilePath))
@@ -646765,7 +647934,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646765
647934
  severity: "warning"
646766
647935
  });
646767
647936
  if (fractionalDiff > step.screenshot.maxVariation) {
646768
- if (step.screenshot.overwrite == "aboveVariation" && !isUrlPath) {
647937
+ if (step.screenshot.overwrite == "aboveVariation" && !isUrlPath && !internal?.compareOnly) {
646769
647938
  if (!writeFinalPng(existFilePath))
646770
647939
  return result;
646771
647940
  }
@@ -646773,6 +647942,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646773
647942
  if (isUrlPath) {
646774
647943
  result.outputs.screenshotPath = filePath;
646775
647944
  result.outputs.referenceUrl = redactedUrl;
647945
+ } else if (internal?.compareOnly) {
647946
+ result.outputs.screenshotPath = internal.capturePath;
646776
647947
  } else {
646777
647948
  result.outputs.changed = true;
646778
647949
  result.outputs.screenshotPath = existFilePath;
@@ -646796,25 +647967,248 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646796
647967
  }
646797
647968
  }
646798
647969
  if (!result.outputs.screenshotPath) {
646799
- if (!writeFinalPng(filePath))
646800
- return result;
646801
- result.outputs.screenshotPath = filePath;
646802
- result.outputs.changed = true;
646803
- if (step.screenshot.sourceIntegration) {
646804
- result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
647970
+ if (internal?.compareOnly) {
647971
+ result.outputs.screenshotPath = internal.capturePath;
647972
+ result.outputs.baselineMissing = true;
647973
+ } else {
647974
+ if (!writeFinalPng(filePath))
647975
+ return result;
647976
+ result.outputs.screenshotPath = filePath;
647977
+ result.outputs.changed = true;
647978
+ if (step.screenshot.sourceIntegration) {
647979
+ result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
647980
+ }
646805
647981
  }
646806
647982
  }
646807
647983
  return await evaluateApplicable();
646808
647984
  }
646809
647985
 
647986
+ // dist/core/tests/recordingCheckpoints.js
647987
+ var import_node_fs21 = __toESM(require("node:fs"), 1);
647988
+ var import_node_os8 = __toESM(require("node:os"), 1);
647989
+ var import_node_path18 = __toESM(require("node:path"), 1);
647990
+ var import_node_crypto7 = require("node:crypto");
647991
+ init_utils();
647992
+ init_browserStepKeys();
647993
+ function capPathSegment(segment, max = 32) {
647994
+ if (segment.length <= max)
647995
+ return segment;
647996
+ const hash = (0, import_node_crypto7.createHash)("sha1").update(segment).digest("hex").slice(0, 8);
647997
+ const tail = segment.slice(segment.length - (max - hash.length - 1));
647998
+ return `${hash}-${tail}`;
647999
+ }
648000
+ function stepArtifactFileName({ step, stepIndex, stepCount, testId }) {
648001
+ const action = BROWSER_STEP_KEYS.find((key) => typeof step[key] !== "undefined") || "step";
648002
+ const sanitizedTestId = sanitizeFilesystemName(String(testId ?? ""), "test");
648003
+ const stepIdString = sanitizeFilesystemName(String(step.stepId ?? ""), "step");
648004
+ const stepRef = capPathSegment(stepIdString.startsWith(`${sanitizedTestId}~`) ? stepIdString.slice(sanitizedTestId.length + 1) : stepIdString);
648005
+ const pad = Math.max(2, String(stepCount).length);
648006
+ return `${String(stepIndex + 1).padStart(pad, "0")}-${action}-${stepRef}.png`;
648007
+ }
648008
+ function recordingCheckpointsEnabled(record) {
648009
+ if (record?.overwrite === "aboveVariation")
648010
+ return true;
648011
+ return record?.checkpoints !== void 0 && record?.checkpoints !== false;
648012
+ }
648013
+ function resolveCheckpointsConfig({ record, targetPath, handleId }) {
648014
+ let raw = record?.checkpoints;
648015
+ if (record?.overwrite === "aboveVariation" && (raw === void 0 || raw === false)) {
648016
+ raw = true;
648017
+ }
648018
+ if (raw === void 0 || raw === false)
648019
+ return null;
648020
+ const opts = raw === true ? {} : raw;
648021
+ const baselineDir = opts.directory ? import_node_path18.default.resolve(import_node_path18.default.dirname(targetPath), opts.directory) : `${targetPath}.checkpoints`;
648022
+ return {
648023
+ maxVariation: opts.maxVariation ?? 0.05,
648024
+ baselineDir,
648025
+ stagingDir: import_node_path18.default.join(import_node_os8.default.tmpdir(), "doc-detective", "checkpoints", String(handleId)),
648026
+ entries: []
648027
+ };
648028
+ }
648029
+ function computeSpanVerdict({ entries, baselineDir, maxVariation, targetExists }) {
648030
+ const reasons = [];
648031
+ const drifted = entries.filter((e) => typeof e.variation === "number" && e.variation > maxVariation);
648032
+ if (drifted.length > 0) {
648033
+ reasons.push(`${drifted.length} checkpoint(s) drifted beyond maxVariation (${maxVariation}): pixel variation up to ${Math.max(...drifted.map((e) => e.variation)).toFixed(3)}`);
648034
+ }
648035
+ const missing = entries.filter((e) => e.baselineMissing);
648036
+ if (missing.length > 0) {
648037
+ reasons.push(`${missing.length} checkpoint(s) have no baseline (step added or renamed)`);
648038
+ }
648039
+ const errored = entries.filter((e) => e.error);
648040
+ if (errored.length > 0) {
648041
+ reasons.push(`${errored.length} checkpoint(s) couldn't be compared (capture or comparison error)`);
648042
+ }
648043
+ let orphans = [];
648044
+ try {
648045
+ const expected = new Set(entries.map((e) => e.fileName));
648046
+ orphans = import_node_fs21.default.readdirSync(baselineDir).filter((name) => name.endsWith(".png") && !expected.has(name));
648047
+ } catch {
648048
+ }
648049
+ if (orphans.length > 0) {
648050
+ reasons.push(`${orphans.length} baseline(s) have no matching step (step removed or renamed)`);
648051
+ }
648052
+ if (!targetExists) {
648053
+ reasons.push("the recording file is missing");
648054
+ }
648055
+ return { changed: reasons.length > 0, reasons, orphans };
648056
+ }
648057
+ function promoteRecordingSpan({ config, stagingTarget, targetPath, entries, orphans, baselineDir }) {
648058
+ const backupPath = `${targetPath}.promote-backup`;
648059
+ const targetExisted = import_node_fs21.default.existsSync(targetPath);
648060
+ try {
648061
+ import_node_fs21.default.rmSync(backupPath, { force: true });
648062
+ } catch {
648063
+ }
648064
+ try {
648065
+ if (targetExisted)
648066
+ import_node_fs21.default.renameSync(targetPath, backupPath);
648067
+ import_node_fs21.default.renameSync(stagingTarget, targetPath);
648068
+ } catch (error) {
648069
+ log(config, "warning", `Couldn't promote the refreshed recording over ${targetPath}; keeping the existing recording. ${error?.message ?? error}`);
648070
+ try {
648071
+ if (targetExisted && !import_node_fs21.default.existsSync(targetPath)) {
648072
+ import_node_fs21.default.renameSync(backupPath, targetPath);
648073
+ }
648074
+ import_node_fs21.default.rmSync(stagingTarget, { force: true });
648075
+ } catch {
648076
+ }
648077
+ return { videoPromoted: false, seededBaselines: 0, baselineFailures: 0 };
648078
+ }
648079
+ try {
648080
+ import_node_fs21.default.rmSync(backupPath, { force: true });
648081
+ } catch {
648082
+ }
648083
+ try {
648084
+ import_node_fs21.default.mkdirSync(baselineDir, { recursive: true });
648085
+ } catch {
648086
+ }
648087
+ let seededBaselines = 0;
648088
+ let baselineFailures = 0;
648089
+ for (const entry of entries) {
648090
+ if (entry.error)
648091
+ continue;
648092
+ try {
648093
+ const tempPath = `${entry.baselinePath}.tmp`;
648094
+ import_node_fs21.default.copyFileSync(entry.stagingPath, tempPath);
648095
+ import_node_fs21.default.renameSync(tempPath, entry.baselinePath);
648096
+ if (entry.baselineMissing)
648097
+ seededBaselines++;
648098
+ } catch (error) {
648099
+ baselineFailures++;
648100
+ log(config, "warning", `Couldn't update checkpoint baseline ${entry.baselinePath}: ${error?.message ?? error}`);
648101
+ }
648102
+ }
648103
+ for (const orphan of orphans) {
648104
+ try {
648105
+ import_node_fs21.default.unlinkSync(import_node_path18.default.join(baselineDir, orphan));
648106
+ } catch (error) {
648107
+ baselineFailures++;
648108
+ log(config, "warning", `Couldn't remove orphaned checkpoint baseline ${orphan}: ${error?.message ?? error}`);
648109
+ }
648110
+ }
648111
+ return { videoPromoted: true, seededBaselines, baselineFailures };
648112
+ }
648113
+ function buildCheckpointOutputs(entries) {
648114
+ let maxCheckpointVariation = 0;
648115
+ for (const entry of entries) {
648116
+ if (typeof entry.variation === "number") {
648117
+ maxCheckpointVariation = Math.max(maxCheckpointVariation, entry.variation);
648118
+ }
648119
+ }
648120
+ return {
648121
+ checkpoints: entries.map((entry) => ({
648122
+ fileName: entry.fileName,
648123
+ ...typeof entry.variation === "number" ? { variation: entry.variation } : {},
648124
+ ...entry.baselineMissing ? { baselineMissing: true } : {},
648125
+ ...entry.error ? { error: entry.error } : {}
648126
+ })),
648127
+ maxCheckpointVariation,
648128
+ checkpointErrors: entries.filter((entry) => entry.error).length
648129
+ };
648130
+ }
648131
+ async function captureRecordingCheckpoints({ config, driver, recordingHost, step, stepStatus, stepIndex, stepCount, testId, appSession }) {
648132
+ const recordings = recordingHost?.state?.recordings;
648133
+ if (!Array.isArray(recordings) || recordings.length === 0)
648134
+ return;
648135
+ const qualifying = recordings.filter((h) => h?.checkpoints && !h.synthetic);
648136
+ if (qualifying.length === 0)
648137
+ return;
648138
+ if (!driver) {
648139
+ log(config, "debug", "Recording checkpoints skipped: no browser driver in this context.");
648140
+ return;
648141
+ }
648142
+ if (stepStatus === "FAIL") {
648143
+ for (const handle of qualifying) {
648144
+ handle.checkpoints.spanDirty = true;
648145
+ }
648146
+ log(config, "debug", `Recording checkpoint skipped for failed step ${step.stepId}; span marked dirty.`);
648147
+ return;
648148
+ }
648149
+ for (const handle of qualifying) {
648150
+ const checkpoints = handle.checkpoints;
648151
+ const fileName = stepArtifactFileName({
648152
+ step,
648153
+ stepIndex,
648154
+ stepCount,
648155
+ testId
648156
+ });
648157
+ const entry = {
648158
+ fileName,
648159
+ stagingPath: import_node_path18.default.join(checkpoints.stagingDir, fileName),
648160
+ baselinePath: import_node_path18.default.join(checkpoints.baselineDir, fileName)
648161
+ };
648162
+ try {
648163
+ import_node_fs21.default.mkdirSync(checkpoints.stagingDir, { recursive: true });
648164
+ const screenshotStep = {
648165
+ stepId: `${step.stepId}_checkpoint`,
648166
+ description: "Recording checkpoint screenshot",
648167
+ screenshot: {
648168
+ path: entry.baselinePath,
648169
+ maxVariation: checkpoints.maxVariation,
648170
+ overwrite: "aboveVariation"
648171
+ }
648172
+ };
648173
+ const captureResult = await saveScreenshot({
648174
+ config,
648175
+ step: screenshotStep,
648176
+ driver,
648177
+ appSession,
648178
+ internal: { compareOnly: true, capturePath: entry.stagingPath }
648179
+ });
648180
+ if (captureResult.status !== "PASS" && captureResult.status !== "WARNING") {
648181
+ entry.error = captureResult.description;
648182
+ log(config, "warning", `Recording checkpoint failed after step ${step.stepId}: ${captureResult.description}`);
648183
+ } else {
648184
+ if (typeof captureResult.outputs?.variation === "number") {
648185
+ entry.variation = captureResult.outputs.variation;
648186
+ }
648187
+ if (captureResult.outputs?.baselineMissing) {
648188
+ entry.baselineMissing = true;
648189
+ }
648190
+ }
648191
+ } catch (error) {
648192
+ entry.error = String(error?.message ?? error);
648193
+ log(config, "warning", `Recording checkpoint failed after step ${step.stepId}: ${entry.error}`);
648194
+ }
648195
+ const existingIndex = checkpoints.entries.findIndex((e) => e.fileName === fileName);
648196
+ if (existingIndex >= 0) {
648197
+ checkpoints.entries[existingIndex] = entry;
648198
+ } else {
648199
+ checkpoints.entries.push(entry);
648200
+ }
648201
+ }
648202
+ }
648203
+
646810
648204
  // dist/core/tests/startRecording.js
646811
648205
  init_validate();
646812
648206
  init_utils();
646813
648207
  var import_node_child_process12 = require("node:child_process");
646814
- var import_node_crypto7 = require("node:crypto");
646815
- var import_node_path18 = __toESM(require("node:path"), 1);
646816
- var import_node_fs21 = __toESM(require("node:fs"), 1);
646817
- var import_node_os8 = __toESM(require("node:os"), 1);
648208
+ var import_node_crypto8 = require("node:crypto");
648209
+ var import_node_path19 = __toESM(require("node:path"), 1);
648210
+ var import_node_fs22 = __toESM(require("node:fs"), 1);
648211
+ var import_node_os9 = __toESM(require("node:os"), 1);
646818
648212
  async function startRecording({ config, context, step, driver, recordingHost, appSession, deps = {} }) {
646819
648213
  let result = {
646820
648214
  status: "PASS",
@@ -646882,7 +648276,7 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
646882
648276
  if (typeof step.record.path === "undefined") {
646883
648277
  step.record.path = `${step.stepId}.mp4`;
646884
648278
  if (step.record.directory) {
646885
- step.record.path = import_node_path18.default.resolve(step.record.directory, step.record.path);
648279
+ step.record.path = import_node_path19.default.resolve(step.record.directory, step.record.path);
646886
648280
  }
646887
648281
  }
646888
648282
  step.record = {
@@ -646890,18 +648284,26 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
646890
648284
  overwrite: step.record.overwrite || "false"
646891
648285
  };
646892
648286
  let filePath = step.record.path;
646893
- const baseName = import_node_path18.default.basename(filePath, import_node_path18.default.extname(filePath));
646894
- const dir = import_node_path18.default.dirname(step.record.path);
646895
- if (!import_node_fs21.default.existsSync(dir)) {
646896
- import_node_fs21.default.mkdirSync(dir, { recursive: true });
648287
+ const baseName = import_node_path19.default.basename(filePath, import_node_path19.default.extname(filePath));
648288
+ const dir = import_node_path19.default.dirname(step.record.path);
648289
+ if (!import_node_fs22.default.existsSync(dir)) {
648290
+ import_node_fs22.default.mkdirSync(dir, { recursive: true });
646897
648291
  }
646898
- if (import_node_fs21.default.existsSync(filePath) && step.record.overwrite == "false") {
648292
+ const phantomRecordingResult = (skipDescription, skipReason = "headless") => {
646899
648293
  result.status = "SKIPPED";
646900
- result.description = `File already exists: ${filePath}`;
648294
+ if (recordingCheckpointsEnabled(step.record)) {
648295
+ result.description = `${skipDescription} Running checkpoint comparisons only, to detect whether the recording is stale.`;
648296
+ result.recording = { type: "phantom", targetPath: filePath, skipReason };
648297
+ } else {
648298
+ result.description = skipDescription;
648299
+ }
646901
648300
  return result;
648301
+ };
648302
+ if (import_node_fs22.default.existsSync(filePath) && step.record.overwrite == "false") {
648303
+ return phantomRecordingResult(`File already exists: ${filePath}`, "targetExists");
646902
648304
  }
646903
648305
  const normalizeActiveTarget = (p) => {
646904
- const resolved = import_node_path18.default.resolve(p);
648306
+ const resolved = import_node_path19.default.resolve(p);
646905
648307
  return process.platform === "win32" || process.platform === "darwin" ? resolved.toLowerCase() : resolved;
646906
648308
  };
646907
648309
  const normalizedTarget = normalizeActiveTarget(filePath);
@@ -646978,9 +648380,7 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
646978
648380
  }
646979
648381
  if (plan.name === "browser") {
646980
648382
  if (context.browser?.headless) {
646981
- result.status = "SKIPPED";
646982
- result.description = `Recording isn't supported in headless mode with the browser engine. Use the ffmpeg engine to record headless.`;
646983
- return result;
648383
+ return phantomRecordingResult(`Recording isn't supported in headless mode with the browser engine. Use the ffmpeg engine to record headless.`);
646984
648384
  }
646985
648385
  if (context.browser?.name !== "chrome") {
646986
648386
  result.status = "SKIPPED";
@@ -646989,13 +648389,13 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
646989
648389
  }
646990
648390
  const captureTitle = browserCaptureTitle(context.contextId);
646991
648391
  const downloadDir = browserDownloadDir(context.contextId);
646992
- if (!import_node_fs21.default.existsSync(downloadDir)) {
646993
- import_node_fs21.default.mkdirSync(downloadDir, { recursive: true });
648392
+ if (!import_node_fs22.default.existsSync(downloadDir)) {
648393
+ import_node_fs22.default.mkdirSync(downloadDir, { recursive: true });
646994
648394
  }
646995
- const downloadPath = import_node_path18.default.join(downloadDir, `${baseName}.webm`);
646996
- if (import_node_fs21.default.existsSync(downloadPath)) {
648395
+ const downloadPath = import_node_path19.default.join(downloadDir, `${baseName}.webm`);
648396
+ if (import_node_fs22.default.existsSync(downloadPath)) {
646997
648397
  try {
646998
- import_node_fs21.default.unlinkSync(downloadPath);
648398
+ import_node_fs22.default.unlinkSync(downloadPath);
646999
648399
  } catch {
647000
648400
  }
647001
648401
  }
@@ -647107,9 +648507,7 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
647107
648507
  return result;
647108
648508
  }
647109
648509
  if (!appRef && context.browser?.headless && !context.__display) {
647110
- result.status = "SKIPPED";
647111
- result.description = `Recording isn't supported in headless mode without a virtual display (Xvfb).`;
647112
- return result;
648510
+ return phantomRecordingResult(`Recording isn't supported in headless mode without a virtual display (Xvfb).`);
647113
648511
  }
647114
648512
  let crop = null;
647115
648513
  let appCropRect = null;
@@ -647135,10 +648533,10 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
647135
648533
  } catch {
647136
648534
  }
647137
648535
  }
647138
- const tempDir = import_node_path18.default.join(import_node_os8.default.tmpdir(), "doc-detective", "recordings");
647139
- if (!import_node_fs21.default.existsSync(tempDir))
647140
- import_node_fs21.default.mkdirSync(tempDir, { recursive: true });
647141
- const tempPath = import_node_path18.default.join(tempDir, `${safeContextId(context.contextId)}-${baseName}-${(0, import_node_crypto7.randomUUID)().slice(0, 8)}.mkv`);
648536
+ const tempDir = import_node_path19.default.join(import_node_os9.default.tmpdir(), "doc-detective", "recordings");
648537
+ if (!import_node_fs22.default.existsSync(tempDir))
648538
+ import_node_fs22.default.mkdirSync(tempDir, { recursive: true });
648539
+ const tempPath = import_node_path19.default.join(tempDir, `${safeContextId(context.contextId)}-${baseName}-${(0, import_node_crypto8.randomUUID)().slice(0, 8)}.mkv`);
647142
648540
  let ffmpegPath;
647143
648541
  try {
647144
648542
  ffmpegPath = await (deps.getFfmpegPath ?? getFfmpegPath)({
@@ -647209,10 +648607,10 @@ async function startRecording({ config, context, step, driver, recordingHost, ap
647209
648607
  init_validate();
647210
648608
  init_utils();
647211
648609
  var import_node_child_process13 = require("node:child_process");
647212
- var import_node_crypto8 = require("node:crypto");
647213
- var import_node_path19 = __toESM(require("node:path"), 1);
647214
- var import_node_fs22 = __toESM(require("node:fs"), 1);
647215
- var import_node_os9 = __toESM(require("node:os"), 1);
648610
+ var import_node_crypto9 = require("node:crypto");
648611
+ var import_node_path20 = __toESM(require("node:path"), 1);
648612
+ var import_node_fs23 = __toESM(require("node:fs"), 1);
648613
+ var import_node_os10 = __toESM(require("node:os"), 1);
647216
648614
  async function stopRecording({ config, step, driver, deps = {} }) {
647217
648615
  let result = {
647218
648616
  status: "PASS",
@@ -647266,8 +648664,49 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647266
648664
  if (idx !== -1)
647267
648665
  recordings.splice(idx, 1);
647268
648666
  };
648667
+ const discardCheckpointStaging = () => {
648668
+ const dir = recording?.checkpoints?.stagingDir;
648669
+ if (!dir)
648670
+ return;
648671
+ try {
648672
+ import_node_fs23.default.rmSync(dir, { recursive: true, force: true });
648673
+ } catch {
648674
+ }
648675
+ };
648676
+ if (recording.type === "phantom") {
648677
+ dropHandle();
648678
+ const phantomCheckpoints = recording.checkpoints;
648679
+ const entries = phantomCheckpoints?.entries ?? [];
648680
+ discardCheckpointStaging();
648681
+ const skipCause = recording.skipReason === "targetExists" ? `Recording skipped (the target already exists)` : `Recording skipped (headless)`;
648682
+ const refreshRemedy = recording.skipReason === "targetExists" ? `Delete it, or set \`overwrite\` to "true" or "aboveVariation", to refresh it.` : `Recording is skipped in headless mode; re-run headed to refresh it.`;
648683
+ if (phantomCheckpoints?.spanDirty || entries.length === 0) {
648684
+ result.status = "SKIPPED";
648685
+ result.description = `${skipCause}; the span didn't produce a complete checkpoint set, so staleness couldn't be determined.`;
648686
+ return result;
648687
+ }
648688
+ const verdict = computeSpanVerdict({
648689
+ entries,
648690
+ baselineDir: phantomCheckpoints?.baselineDir ?? "",
648691
+ maxVariation: phantomCheckpoints?.maxVariation ?? 0.05,
648692
+ targetExists: import_node_fs23.default.existsSync(recording.targetPath)
648693
+ });
648694
+ result.outputs = {
648695
+ stale: verdict.changed,
648696
+ ...buildCheckpointOutputs(entries)
648697
+ };
648698
+ if (verdict.changed) {
648699
+ result.status = "WARNING";
648700
+ result.description = `The recording at ${recording.targetPath} appears stale \u2014 ${verdict.reasons.join("; ")}. ${refreshRemedy}`;
648701
+ } else {
648702
+ result.status = "SKIPPED";
648703
+ result.description = `${skipCause}; checkpoints match their baselines, so the recording appears current.`;
648704
+ }
648705
+ return result;
648706
+ }
647269
648707
  if (recording.type === "appium-pending") {
647270
648708
  dropHandle();
648709
+ discardCheckpointStaging();
647271
648710
  if (recording.startError) {
647272
648711
  result.status = recording.startSkip ? "SKIPPED" : "FAIL";
647273
648712
  result.description = recording.startError;
@@ -647277,6 +648716,9 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647277
648716
  result.description = "The device recording never started (no app surface opened a device session), so there is nothing to save.";
647278
648717
  return result;
647279
648718
  }
648719
+ const isAboveVariation = recording.overwrite === "aboveVariation";
648720
+ const finalTargetPath = recording.targetPath;
648721
+ const writeTargetPath = isAboveVariation ? import_node_path20.default.join(import_node_path20.default.dirname(finalTargetPath), `.${import_node_path20.default.basename(finalTargetPath, import_node_path20.default.extname(finalTargetPath))}.staging${import_node_path20.default.extname(finalTargetPath)}`) : finalTargetPath;
647280
648722
  try {
647281
648723
  if (recording.type === "MediaRecorder") {
647282
648724
  let returnTab = null;
@@ -647303,6 +648745,7 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647303
648745
  result.description = "Recording was not properly started. The recorder object doesn't exist in the browser context.";
647304
648746
  await closeRecorderTabAndRestoreFocus();
647305
648747
  dropHandle();
648748
+ discardCheckpointStaging();
647306
648749
  return result;
647307
648750
  }
647308
648751
  await driver.execute(() => {
@@ -647314,13 +648757,14 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647314
648757
  result.description = "Recording download timed out.";
647315
648758
  await closeRecorderTabAndRestoreFocus();
647316
648759
  dropHandle();
648760
+ discardCheckpointStaging();
647317
648761
  return result;
647318
648762
  }
647319
648763
  await closeRecorderTabAndRestoreFocus();
647320
648764
  await transcode({
647321
648765
  config,
647322
648766
  sourcePath: recording.downloadPath,
647323
- targetPath: recording.targetPath,
648767
+ targetPath: writeTargetPath,
647324
648768
  deleteSource: true
647325
648769
  });
647326
648770
  dropHandle();
@@ -647378,7 +648822,7 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647378
648822
  await transcode({
647379
648823
  config,
647380
648824
  sourcePath: recording.tempPath,
647381
- targetPath: recording.targetPath,
648825
+ targetPath: writeTargetPath,
647382
648826
  deleteSource: true,
647383
648827
  crop
647384
648828
  });
@@ -647389,6 +648833,7 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647389
648833
  result.status = "FAIL";
647390
648834
  result.description = "The device recording returned no data; nothing was saved.";
647391
648835
  dropHandle();
648836
+ discardCheckpointStaging();
647392
648837
  return result;
647393
648838
  }
647394
648839
  let buffer;
@@ -647398,26 +648843,28 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647398
648843
  result.status = "FAIL";
647399
648844
  result.description = `Couldn't buffer the device recording (${Math.round(b64.length / 1024 / 1024)} MB base64) \u2014 device recordings transfer in one payload, so long recordings can exhaust memory. Keep device recordings short (they cap at 30 minutes). ${error?.message ?? error}`;
647400
648845
  dropHandle();
648846
+ discardCheckpointStaging();
647401
648847
  return result;
647402
648848
  }
647403
648849
  if (buffer.length === 0) {
647404
648850
  result.status = "FAIL";
647405
648851
  result.description = "The device recording decoded to an empty payload (no data); nothing was saved.";
647406
648852
  dropHandle();
648853
+ discardCheckpointStaging();
647407
648854
  return result;
647408
648855
  }
647409
- if (import_node_path19.default.extname(recording.targetPath) === ".mp4") {
647410
- import_node_fs22.default.writeFileSync(recording.targetPath, buffer);
648856
+ if (import_node_path20.default.extname(writeTargetPath) === ".mp4") {
648857
+ import_node_fs23.default.writeFileSync(writeTargetPath, buffer);
647411
648858
  } else {
647412
- const tempDir = import_node_path19.default.join(import_node_os9.default.tmpdir(), "doc-detective", "recordings");
647413
- if (!import_node_fs22.default.existsSync(tempDir))
647414
- import_node_fs22.default.mkdirSync(tempDir, { recursive: true });
647415
- const tempPath = import_node_path19.default.join(tempDir, `device-${(0, import_node_crypto8.randomUUID)().slice(0, 8)}.mp4`);
647416
- import_node_fs22.default.writeFileSync(tempPath, buffer);
648859
+ const tempDir = import_node_path20.default.join(import_node_os10.default.tmpdir(), "doc-detective", "recordings");
648860
+ if (!import_node_fs23.default.existsSync(tempDir))
648861
+ import_node_fs23.default.mkdirSync(tempDir, { recursive: true });
648862
+ const tempPath = import_node_path20.default.join(tempDir, `device-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}.mp4`);
648863
+ import_node_fs23.default.writeFileSync(tempPath, buffer);
647417
648864
  await transcode({
647418
648865
  config,
647419
648866
  sourcePath: tempPath,
647420
- targetPath: recording.targetPath,
648867
+ targetPath: writeTargetPath,
647421
648868
  deleteSource: true
647422
648869
  });
647423
648870
  }
@@ -647427,15 +648874,100 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647427
648874
  result.status = "FAIL";
647428
648875
  result.description = `Couldn't stop recording. ${error}`;
647429
648876
  dropHandle();
648877
+ discardCheckpointStaging();
648878
+ if (isAboveVariation) {
648879
+ try {
648880
+ import_node_fs23.default.rmSync(writeTargetPath, { force: true });
648881
+ } catch {
648882
+ }
648883
+ }
647430
648884
  return result;
647431
648885
  }
647432
- result.outputs = {
647433
- recordingPath: import_node_path19.default.resolve(recording.targetPath),
647434
- format: import_node_path19.default.extname(recording.targetPath).slice(1)
647435
- };
648886
+ const checkpoints = recording.checkpoints;
648887
+ let seededBaselines = 0;
648888
+ let promoteFailed = false;
648889
+ let baselineFailures = 0;
648890
+ result.outputs = {};
648891
+ if (isAboveVariation) {
648892
+ const entries = checkpoints?.entries ?? [];
648893
+ const targetExists = import_node_fs23.default.existsSync(finalTargetPath);
648894
+ const discardStagedVideo = () => {
648895
+ try {
648896
+ import_node_fs23.default.rmSync(writeTargetPath, { force: true });
648897
+ } catch {
648898
+ }
648899
+ };
648900
+ const indeterminate = checkpoints?.spanDirty || entries.length === 0;
648901
+ if (indeterminate && targetExists) {
648902
+ discardStagedVideo();
648903
+ result.outputs.changed = false;
648904
+ log(config, "warning", checkpoints?.spanDirty ? `Recording kept unchanged (${finalTargetPath}): a step failed during the span, so drift couldn't be judged.` : `Recording kept unchanged (${finalTargetPath}): no checkpoints were captured this run, so drift couldn't be judged.`);
648905
+ } else if (indeterminate) {
648906
+ const promoted = promoteRecordingSpan({
648907
+ config,
648908
+ stagingTarget: writeTargetPath,
648909
+ targetPath: finalTargetPath,
648910
+ entries,
648911
+ orphans: [],
648912
+ baselineDir: checkpoints?.baselineDir ?? ""
648913
+ });
648914
+ seededBaselines = promoted.seededBaselines;
648915
+ result.outputs.changed = promoted.videoPromoted;
648916
+ promoteFailed = !promoted.videoPromoted;
648917
+ baselineFailures = promoted.baselineFailures;
648918
+ if (promoted.videoPromoted) {
648919
+ result.outputs.changeReasons = ["the recording file is missing"];
648920
+ }
648921
+ } else {
648922
+ const verdict = computeSpanVerdict({
648923
+ entries,
648924
+ baselineDir: checkpoints?.baselineDir ?? "",
648925
+ maxVariation: checkpoints?.maxVariation ?? 0.05,
648926
+ targetExists
648927
+ });
648928
+ if (verdict.changed) {
648929
+ const promoted = promoteRecordingSpan({
648930
+ config,
648931
+ stagingTarget: writeTargetPath,
648932
+ targetPath: finalTargetPath,
648933
+ entries,
648934
+ orphans: verdict.orphans,
648935
+ baselineDir: checkpoints?.baselineDir ?? ""
648936
+ });
648937
+ seededBaselines = promoted.seededBaselines;
648938
+ result.outputs.changed = promoted.videoPromoted;
648939
+ promoteFailed = !promoted.videoPromoted;
648940
+ baselineFailures = promoted.baselineFailures;
648941
+ if (promoted.videoPromoted) {
648942
+ result.outputs.changeReasons = verdict.reasons;
648943
+ log(config, "info", `Recording refreshed (${finalTargetPath}): ${verdict.reasons.join("; ")}`);
648944
+ }
648945
+ } else {
648946
+ discardStagedVideo();
648947
+ result.outputs.changed = false;
648948
+ }
648949
+ }
648950
+ } else if (checkpoints?.entries?.length) {
648951
+ for (const entry of checkpoints.entries) {
648952
+ if (checkpoints.spanDirty)
648953
+ break;
648954
+ if (entry.baselineMissing && !entry.error) {
648955
+ try {
648956
+ import_node_fs23.default.mkdirSync(checkpoints.baselineDir, { recursive: true });
648957
+ import_node_fs23.default.copyFileSync(entry.stagingPath, entry.baselinePath, import_node_fs23.default.constants.COPYFILE_EXCL);
648958
+ seededBaselines++;
648959
+ } catch (error) {
648960
+ entry.error = error?.code === "EEXIST" ? `A baseline appeared at ${entry.baselinePath} after this checkpoint was captured; left it untouched.` : String(error?.message ?? error);
648961
+ log(config, "warning", `Couldn't seed checkpoint baseline ${entry.baselinePath}: ${entry.error}`);
648962
+ }
648963
+ }
648964
+ }
648965
+ }
648966
+ result.outputs.recordingPath = import_node_path20.default.resolve(finalTargetPath);
648967
+ result.outputs.format = import_node_path20.default.extname(finalTargetPath).slice(1);
647436
648968
  const meta = await probeVideoMetadata({
647437
648969
  cacheDir: config?.cacheDir,
647438
- filePath: recording.targetPath
648970
+ filePath: finalTargetPath
647439
648971
  });
647440
648972
  if (meta?.duration !== void 0)
647441
648973
  result.outputs.duration = meta.duration;
@@ -647446,7 +648978,45 @@ async function stopRecording({ config, step, driver, deps = {} }) {
647446
648978
  if (meta?.fps !== void 0)
647447
648979
  result.outputs.fps = meta.fps;
647448
648980
  if (!meta || Object.keys(meta).length === 0) {
647449
- log(config, "debug", `Couldn't probe recording metadata for ${recording.targetPath}.`);
648981
+ log(config, "debug", `Couldn't probe recording metadata for ${finalTargetPath}.`);
648982
+ }
648983
+ if (checkpoints?.entries?.length) {
648984
+ Object.assign(result.outputs, buildCheckpointOutputs(checkpoints.entries));
648985
+ result.outputs.seededBaselines = seededBaselines;
648986
+ const ctx = buildConditionContext({ outputs: result.outputs });
648987
+ const { assertions, status } = await evaluateImplicitAssertions([
648988
+ {
648989
+ statement: `$$outputs.maxCheckpointVariation <= ${checkpoints.maxVariation}`,
648990
+ severity: "warning"
648991
+ },
648992
+ {
648993
+ statement: `$$outputs.checkpointErrors == 0`,
648994
+ severity: "warning"
648995
+ }
648996
+ ], ctx);
648997
+ result.assertions = assertions;
648998
+ result.status = status;
648999
+ if (status === "WARNING") {
649000
+ result.description += !isAboveVariation ? ` One or more checkpoints drifted beyond maxVariation (${checkpoints.maxVariation}) or couldn't be compared \u2014 the recorded flow's content may have changed since its baselines were captured.` : result.outputs.changed ? ` One or more checkpoints drifted beyond maxVariation (${checkpoints.maxVariation}) or couldn't be compared \u2014 the recording was refreshed to match the current content.` : ` One or more checkpoints drifted beyond maxVariation (${checkpoints.maxVariation}) or couldn't be compared \u2014 the existing recording was kept.`;
649001
+ }
649002
+ if (baselineFailures > 0) {
649003
+ result.outputs.baselineFailures = baselineFailures;
649004
+ }
649005
+ try {
649006
+ import_node_fs23.default.rmSync(checkpoints.stagingDir, { recursive: true, force: true });
649007
+ } catch {
649008
+ }
649009
+ }
649010
+ if (promoteFailed) {
649011
+ if (!import_node_fs23.default.existsSync(finalTargetPath)) {
649012
+ result.status = "FAIL";
649013
+ result.description = `Couldn't save the recording to ${finalTargetPath}: promoting the captured video failed and no previous recording exists at that path.`;
649014
+ delete result.outputs.recordingPath;
649015
+ delete result.outputs.format;
649016
+ } else {
649017
+ result.status = "WARNING";
649018
+ result.description += ` The captured video couldn't replace ${finalTargetPath}, so the existing (now stale) recording was kept.`;
649019
+ }
647450
649020
  }
647451
649021
  return result;
647452
649022
  }
@@ -647460,7 +649030,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
647460
649030
  const cy = `max(0\\,min(${crop.y}\\,ih-${ch}))`;
647461
649031
  filters.push(`crop=w=${cw}:h=${ch}:x=${cx}:y=${cy}`);
647462
649032
  }
647463
- if (import_node_path19.default.extname(targetPath) === ".gif") {
649033
+ if (import_node_path20.default.extname(targetPath) === ".gif") {
647464
649034
  filters.push("scale=iw:-1:flags=lanczos");
647465
649035
  }
647466
649036
  if (filters.length > 0) {
@@ -647478,7 +649048,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
647478
649048
  if (code === 0) {
647479
649049
  if (deleteSource && sourcePath !== targetPath) {
647480
649050
  try {
647481
- import_node_fs22.default.unlinkSync(sourcePath);
649051
+ import_node_fs23.default.unlinkSync(sourcePath);
647482
649052
  } catch {
647483
649053
  }
647484
649054
  }
@@ -647497,7 +649067,7 @@ async function waitForStableFile(filePath, maxSeconds) {
647497
649067
  for (let i = 0; i < deadline; i++) {
647498
649068
  let size = -1;
647499
649069
  try {
647500
- size = import_node_fs22.default.statSync(filePath).size;
649070
+ size = import_node_fs23.default.statSync(filePath).size;
647501
649071
  } catch {
647502
649072
  size = -1;
647503
649073
  }
@@ -647537,8 +649107,8 @@ async function loadVariables({ step }) {
647537
649107
  // dist/core/tests/saveCookie.js
647538
649108
  init_validate();
647539
649109
  init_utils();
647540
- var import_node_path20 = __toESM(require("node:path"), 1);
647541
- var import_node_fs23 = __toESM(require("node:fs"), 1);
649110
+ var import_node_path21 = __toESM(require("node:path"), 1);
649111
+ var import_node_fs24 = __toESM(require("node:fs"), 1);
647542
649112
  async function saveCookie({ config, step, driver }) {
647543
649113
  let result = {
647544
649114
  status: "PASS",
@@ -647555,7 +649125,7 @@ async function saveCookie({ config, step, driver }) {
647555
649125
  let cookieName, filePath, directory, overwrite, domain, variable;
647556
649126
  if (typeof step.saveCookie === "string") {
647557
649127
  if (step.saveCookie.endsWith(".txt")) {
647558
- cookieName = import_node_path20.default.basename(step.saveCookie, ".txt");
649128
+ cookieName = import_node_path21.default.basename(step.saveCookie, ".txt");
647559
649129
  filePath = step.saveCookie;
647560
649130
  } else {
647561
649131
  cookieName = step.saveCookie;
@@ -647604,15 +649174,15 @@ async function saveCookie({ config, step, driver }) {
647604
649174
  }
647605
649175
  if (filePath) {
647606
649176
  const outputDirectory = directory || config.output || process.cwd();
647607
- const fullPath = import_node_path20.default.resolve(outputDirectory, filePath);
647608
- if (import_node_fs23.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
649177
+ const fullPath = import_node_path21.default.resolve(outputDirectory, filePath);
649178
+ if (import_node_fs24.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
647609
649179
  result.status = "FAIL";
647610
649180
  result.description = `File '${fullPath}' already exists and overwrite is not enabled.`;
647611
649181
  return result;
647612
649182
  }
647613
- const dir = import_node_path20.default.dirname(fullPath);
647614
- if (!import_node_fs23.default.existsSync(dir)) {
647615
- import_node_fs23.default.mkdirSync(dir, { recursive: true });
649183
+ const dir = import_node_path21.default.dirname(fullPath);
649184
+ if (!import_node_fs24.default.existsSync(dir)) {
649185
+ import_node_fs24.default.mkdirSync(dir, { recursive: true });
647616
649186
  }
647617
649187
  if (targetCookie) {
647618
649188
  const netscapeCookie = formatCookieForNetscape(targetCookie);
@@ -647620,11 +649190,11 @@ async function saveCookie({ config, step, driver }) {
647620
649190
  # This is a cookie file saved by Doc Detective
647621
649191
  ${netscapeCookie}
647622
649192
  `;
647623
- import_node_fs23.default.writeFileSync(fullPath, content, "utf8");
649193
+ import_node_fs24.default.writeFileSync(fullPath, content, "utf8");
647624
649194
  result.description = `Saved cookie '${cookieName}' to '${fullPath}'.`;
647625
649195
  log(config, "debug", `Saved cookie '${cookieName}' to file: ${fullPath}`);
647626
649196
  } else {
647627
- import_node_fs23.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
649197
+ import_node_fs24.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
647628
649198
  result.description = `Created empty cookie file at '${fullPath}'.`;
647629
649199
  log(config, "debug", `Created empty cookie file: ${fullPath}`);
647630
649200
  }
@@ -647651,8 +649221,8 @@ function formatCookieForNetscape(cookie) {
647651
649221
  // dist/core/tests/loadCookie.js
647652
649222
  init_validate();
647653
649223
  init_utils();
647654
- var import_node_path21 = __toESM(require("node:path"), 1);
647655
- var import_node_fs24 = __toESM(require("node:fs"), 1);
649224
+ var import_node_path22 = __toESM(require("node:path"), 1);
649225
+ var import_node_fs25 = __toESM(require("node:fs"), 1);
647656
649226
  async function loadCookie({ config, step, driver }) {
647657
649227
  let result = {
647658
649228
  status: "PASS",
@@ -647669,7 +649239,7 @@ async function loadCookie({ config, step, driver }) {
647669
649239
  let cookieName, filePath, directory, domain, variable;
647670
649240
  if (typeof step.loadCookie === "string") {
647671
649241
  if (step.loadCookie.endsWith(".txt")) {
647672
- cookieName = import_node_path21.default.basename(step.loadCookie, ".txt");
649242
+ cookieName = import_node_path22.default.basename(step.loadCookie, ".txt");
647673
649243
  filePath = step.loadCookie;
647674
649244
  } else {
647675
649245
  cookieName = step.loadCookie;
@@ -647682,9 +649252,9 @@ async function loadCookie({ config, step, driver }) {
647682
649252
  domain = step.loadCookie.domain;
647683
649253
  variable = step.loadCookie.variable;
647684
649254
  if (filePath && !cookieName) {
647685
- const ext = import_node_path21.default.extname(filePath).toLowerCase();
649255
+ const ext = import_node_path22.default.extname(filePath).toLowerCase();
647686
649256
  if (ext === ".txt") {
647687
- cookieName = import_node_path21.default.basename(filePath, ext);
649257
+ cookieName = import_node_path22.default.basename(filePath, ext);
647688
649258
  }
647689
649259
  }
647690
649260
  }
@@ -647707,14 +649277,14 @@ async function loadCookie({ config, step, driver }) {
647707
649277
  }
647708
649278
  } else if (filePath) {
647709
649279
  const inputDirectory = directory || config.output || process.cwd();
647710
- const fullPath = import_node_path21.default.resolve(inputDirectory, filePath);
647711
- if (!import_node_fs24.default.existsSync(fullPath)) {
649280
+ const fullPath = import_node_path22.default.resolve(inputDirectory, filePath);
649281
+ if (!import_node_fs25.default.existsSync(fullPath)) {
647712
649282
  result.status = "FAIL";
647713
649283
  result.description = `Cookie file '${fullPath}' not found`;
647714
649284
  return result;
647715
649285
  }
647716
649286
  try {
647717
- const fileContent = import_node_fs24.default.readFileSync(fullPath, "utf8");
649287
+ const fileContent = import_node_fs25.default.readFileSync(fullPath, "utf8");
647718
649288
  const cookies = parseNetscapeCookieFile(fileContent);
647719
649289
  if (cookies.length === 0) {
647720
649290
  result.status = "FAIL";
@@ -647880,8 +649450,8 @@ function isDomainCompatible(currentDomain, cookieDomain) {
647880
649450
  // dist/core/tests/httpRequest.js
647881
649451
  init_validate();
647882
649452
  var import_axios5 = __toESM(require("axios"), 1);
647883
- var import_node_fs25 = __toESM(require("node:fs"), 1);
647884
- var import_node_path22 = __toESM(require("node:path"), 1);
649453
+ var import_node_fs26 = __toESM(require("node:fs"), 1);
649454
+ var import_node_path23 = __toESM(require("node:path"), 1);
647885
649455
  var import_ajv2 = __toESM(require("ajv"), 1);
647886
649456
  init_openapi();
647887
649457
  init_utils();
@@ -647911,9 +649481,9 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
647911
649481
  delete step.httpRequest.openApi.definition;
647912
649482
  } else if (openApiDefinitions.length > 0) {
647913
649483
  findOperation: for (const openApiConfig of openApiDefinitions) {
647914
- for (const path27 in openApiConfig.definition.paths) {
647915
- for (const method in openApiConfig.definition.paths[path27]) {
647916
- if (openApiConfig.definition.paths[path27][method].operationId === step.httpRequest.openApi.operationId) {
649484
+ for (const path28 in openApiConfig.definition.paths) {
649485
+ for (const method in openApiConfig.definition.paths[path28]) {
649486
+ if (openApiConfig.definition.paths[path28][method].operationId === step.httpRequest.openApi.operationId) {
647917
649487
  openApiDefinition = openApiConfig.definition;
647918
649488
  step.httpRequest.openApi = {
647919
649489
  ...openApiConfig,
@@ -648227,20 +649797,20 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
648227
649797
  }
648228
649798
  }
648229
649799
  if (step.httpRequest.path) {
648230
- const dir = import_node_path22.default.dirname(step.httpRequest.path);
648231
- if (!import_node_fs25.default.existsSync(dir)) {
648232
- import_node_fs25.default.mkdirSync(dir, { recursive: true });
649800
+ const dir = import_node_path23.default.dirname(step.httpRequest.path);
649801
+ if (!import_node_fs26.default.existsSync(dir)) {
649802
+ import_node_fs26.default.mkdirSync(dir, { recursive: true });
648233
649803
  }
648234
649804
  let filePath = step.httpRequest.path;
648235
649805
  log(config, "debug", `Saving output to file: ${filePath}`);
648236
- if (!import_node_fs25.default.existsSync(filePath)) {
648237
- await import_node_fs25.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
649806
+ if (!import_node_fs26.default.existsSync(filePath)) {
649807
+ await import_node_fs26.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
648238
649808
  descriptions.push(`Saved output to file.`);
648239
649809
  } else {
648240
649810
  if (step.httpRequest.overwrite == "false") {
648241
649811
  descriptions.push(`Didn't save output. File already exists.`);
648242
649812
  }
648243
- const existingFile = import_node_fs25.default.readFileSync(filePath, "utf8");
649813
+ const existingFile = import_node_fs26.default.readFileSync(filePath, "utf8");
648244
649814
  const fractionalDiff = calculateFractionalDifference(existingFile, JSON.stringify(response.data, null, 2));
648245
649815
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
648246
649816
  result.outputs.variation = fractionalDiff;
@@ -648250,13 +649820,13 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
648250
649820
  });
648251
649821
  if (fractionalDiff > step.httpRequest.maxVariation) {
648252
649822
  if (step.httpRequest.overwrite == "aboveVariation") {
648253
- await import_node_fs25.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
649823
+ await import_node_fs26.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
648254
649824
  descriptions.push(`Saved response to file.`);
648255
649825
  }
648256
649826
  descriptions.push(`The difference between the existing saved response and the new response (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.httpRequest.maxVariation}).`);
648257
649827
  } else {
648258
649828
  if (step.httpRequest.overwrite == "true") {
648259
- import_node_fs25.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
649829
+ import_node_fs26.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
648260
649830
  descriptions.push(`Saved response to file.`);
648261
649831
  }
648262
649832
  }
@@ -648269,8 +649839,8 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
648269
649839
  result.description = descriptions.join(" ").trim();
648270
649840
  return result;
648271
649841
  }
648272
- function fieldExistsAtPath(obj, path27) {
648273
- const segments = path27.match(/[^.[\]]+/g);
649842
+ function fieldExistsAtPath(obj, path28) {
649843
+ const segments = path28.match(/[^.[\]]+/g);
648274
649844
  if (!segments) {
648275
649845
  return false;
648276
649846
  }
@@ -648460,9 +650030,9 @@ async function clickElement({ config, step, driver, appSession }) {
648460
650030
  // dist/core/tests/runCode.js
648461
650031
  init_validate();
648462
650032
  init_utils();
648463
- var import_node_fs26 = __toESM(require("node:fs"), 1);
648464
- var import_node_path23 = __toESM(require("node:path"), 1);
648465
- var import_node_os10 = __toESM(require("node:os"), 1);
650033
+ var import_node_fs27 = __toESM(require("node:fs"), 1);
650034
+ var import_node_path24 = __toESM(require("node:path"), 1);
650035
+ var import_node_os11 = __toESM(require("node:os"), 1);
648466
650036
  function createTempScript(code, language) {
648467
650037
  let extension;
648468
650038
  switch (language) {
@@ -648485,10 +650055,10 @@ function createTempScript(code, language) {
648485
650055
  default:
648486
650056
  extension = "";
648487
650057
  }
648488
- const tmpDir = import_node_os10.default.tmpdir();
648489
- const tmpFile = import_node_path23.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
650058
+ const tmpDir = import_node_os11.default.tmpdir();
650059
+ const tmpFile = import_node_path24.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
648490
650060
  try {
648491
- import_node_fs26.default.writeFileSync(tmpFile, code);
650061
+ import_node_fs27.default.writeFileSync(tmpFile, code);
648492
650062
  } catch (error) {
648493
650063
  throw new Error(`Failed to create temporary script: ${error.message}`);
648494
650064
  }
@@ -648547,9 +650117,9 @@ async function runCode({ config, step, driver, processRegistry }) {
648547
650117
  }
648548
650118
  let command = step.runCode.command;
648549
650119
  const wantsBash = command === "bash" || step.runCode.language?.toLowerCase() === "bash";
648550
- const interpreterShell = wantsBash ? "bash" : import_node_os10.default.platform() === "win32" ? "cmd" : "bash";
650120
+ const interpreterShell = wantsBash ? "bash" : import_node_os11.default.platform() === "win32" ? "cmd" : "bash";
648551
650121
  let commandPreverified = false;
648552
- if (import_node_os10.default.platform() === "win32" && command === "bash") {
650122
+ if (import_node_os11.default.platform() === "win32" && command === "bash") {
648553
650123
  try {
648554
650124
  command = await resolveShellExecutable("bash", {
648555
650125
  cacheDir: config?.cacheDir
@@ -648561,7 +650131,7 @@ async function runCode({ config, step, driver, processRegistry }) {
648561
650131
  return result;
648562
650132
  }
648563
650133
  }
648564
- if (import_node_os10.default.platform() === "win32" && interpreterShell === "bash") {
650134
+ if (import_node_os11.default.platform() === "win32" && interpreterShell === "bash") {
648565
650135
  command = command.replace(/\\/g, "/");
648566
650136
  if (/\s/.test(command) && !command.startsWith('"')) {
648567
650137
  command = `"${command}"`;
@@ -648576,7 +650146,7 @@ async function runCode({ config, step, driver, processRegistry }) {
648576
650146
  }
648577
650147
  }
648578
650148
  let scriptArg = scriptPath;
648579
- if (import_node_os10.default.platform() === "win32")
650149
+ if (import_node_os11.default.platform() === "win32")
648580
650150
  scriptArg = scriptArg.replace(/\\/g, "/");
648581
650151
  if (/\s/.test(scriptArg))
648582
650152
  scriptArg = `"${scriptArg}"`;
@@ -648593,7 +650163,7 @@ async function runCode({ config, step, driver, processRegistry }) {
648593
650163
  if (typeof step.runCode.stdio !== "undefined")
648594
650164
  runShellOptions.stdio = step.runCode.stdio;
648595
650165
  if (typeof step.runCode.path !== "undefined") {
648596
- runShellOptions.path = step.runCode.directory ? import_node_path23.default.join(step.runCode.directory, step.runCode.path) : step.runCode.path;
650166
+ runShellOptions.path = step.runCode.directory ? import_node_path24.default.join(step.runCode.directory, step.runCode.path) : step.runCode.path;
648597
650167
  }
648598
650168
  if (step.runCode.background) {
648599
650169
  runShellOptions.background = step.runCode.background;
@@ -648622,7 +650192,7 @@ async function runCode({ config, step, driver, processRegistry }) {
648622
650192
  } finally {
648623
650193
  if (!deferTempCleanup) {
648624
650194
  try {
648625
- import_node_fs26.default.unlinkSync(scriptPath);
650195
+ import_node_fs27.default.unlinkSync(scriptPath);
648626
650196
  log(config, "debug", `Removed temporary script: ${scriptPath}`);
648627
650197
  } catch (error) {
648628
650198
  log(config, "warning", `Failed to remove temporary script: ${scriptPath}`);
@@ -648636,7 +650206,7 @@ async function runCode({ config, step, driver, processRegistry }) {
648636
650206
  init_validate();
648637
650207
  init_utils();
648638
650208
  var import_tree_kill2 = __toESM(require("tree-kill"), 1);
648639
- var import_node_fs27 = __toESM(require("node:fs"), 1);
650209
+ var import_node_fs28 = __toESM(require("node:fs"), 1);
648640
650210
  async function closeSurface({ config, step, driver, processRegistry, appSession }) {
648641
650211
  const result = {
648642
650212
  status: "PASS",
@@ -648785,7 +650355,7 @@ async function closeSurface({ config, step, driver, processRegistry, appSession
648785
650355
  }
648786
650356
  if (entry.tempPath) {
648787
650357
  try {
648788
- import_node_fs27.default.unlinkSync(entry.tempPath);
650358
+ import_node_fs28.default.unlinkSync(entry.tempPath);
648789
650359
  } catch {
648790
650360
  }
648791
650361
  }
@@ -649042,7 +650612,7 @@ async function startSurfaceStep({ config, step, platform, driver, processRegistr
649042
650612
  }
649043
650613
 
649044
650614
  // dist/core/tests/mobileBrowser.js
649045
- var import_node_path24 = __toESM(require("node:path"), 1);
650615
+ var import_node_path25 = __toESM(require("node:path"), 1);
649046
650616
  var SUPPORTED_MOBILE_BROWSER = {
649047
650617
  android: "chrome",
649048
650618
  ios: "safari"
@@ -649121,7 +650691,7 @@ function buildMobileBrowserCapabilities({ platform, udid, cacheDir, timeout, loc
649121
650691
  // --allow-insecure=uiautomator2:chromedriver_autodownload. The
649122
650692
  // download lands in the Doc Detective cache so later runs reuse it.
649123
650693
  "appium:chromedriverAutodownload": true,
649124
- "appium:chromedriverExecutableDir": import_node_path24.default.join(cacheDir, MOBILE_CHROMEDRIVER_DIR)
650694
+ "appium:chromedriverExecutableDir": import_node_path25.default.join(cacheDir, MOBILE_CHROMEDRIVER_DIR)
649125
650695
  };
649126
650696
  }
649127
650697
  const capabilities = {
@@ -649691,8 +651261,8 @@ function buildAcquireSimulatorDeps(log3) {
649691
651261
  // dist/core/tests/runBrowserScript.js
649692
651262
  init_validate();
649693
651263
  init_utils();
649694
- var import_node_fs28 = __toESM(require("node:fs"), 1);
649695
- var import_node_path25 = __toESM(require("node:path"), 1);
651264
+ var import_node_fs29 = __toESM(require("node:fs"), 1);
651265
+ var import_node_path26 = __toESM(require("node:path"), 1);
649696
651266
  async function runBrowserScript({ config, step, driver }) {
649697
651267
  const result = {
649698
651268
  status: "PASS",
@@ -649767,16 +651337,16 @@ async function runBrowserScript({ config, step, driver }) {
649767
651337
  }
649768
651338
  if (step.runBrowserScript.path) {
649769
651339
  try {
649770
- const dir = import_node_path25.default.dirname(step.runBrowserScript.path);
649771
- if (!import_node_fs28.default.existsSync(dir)) {
649772
- import_node_fs28.default.mkdirSync(dir, { recursive: true });
651340
+ const dir = import_node_path26.default.dirname(step.runBrowserScript.path);
651341
+ if (!import_node_fs29.default.existsSync(dir)) {
651342
+ import_node_fs29.default.mkdirSync(dir, { recursive: true });
649773
651343
  }
649774
651344
  const filePath = step.runBrowserScript.path;
649775
651345
  log(config, "debug", `Saving script result to file: ${filePath}`);
649776
- if (!import_node_fs28.default.existsSync(filePath)) {
649777
- import_node_fs28.default.writeFileSync(filePath, serialized);
651346
+ if (!import_node_fs29.default.existsSync(filePath)) {
651347
+ import_node_fs29.default.writeFileSync(filePath, serialized);
649778
651348
  } else {
649779
- const existingFile = import_node_fs28.default.readFileSync(filePath, "utf8");
651349
+ const existingFile = import_node_fs29.default.readFileSync(filePath, "utf8");
649780
651350
  const fractionalDiff = calculateFractionalDifference(existingFile, serialized);
649781
651351
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
649782
651352
  result.outputs.variation = fractionalDiff;
@@ -649786,7 +651356,7 @@ async function runBrowserScript({ config, step, driver }) {
649786
651356
  });
649787
651357
  if (fractionalDiff > step.runBrowserScript.maxVariation) {
649788
651358
  if (step.runBrowserScript.overwrite == "aboveVariation" || step.runBrowserScript.overwrite == "true") {
649789
- import_node_fs28.default.writeFileSync(filePath, serialized);
651359
+ import_node_fs29.default.writeFileSync(filePath, serialized);
649790
651360
  descriptions.push(`Saved output to file.`);
649791
651361
  } else {
649792
651362
  descriptions.push(`Didn't overwrite the existing file.`);
@@ -649794,7 +651364,7 @@ async function runBrowserScript({ config, step, driver }) {
649794
651364
  descriptions.push(`The difference between the existing output and the new output (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.runBrowserScript.maxVariation}).`);
649795
651365
  } else {
649796
651366
  if (step.runBrowserScript.overwrite == "true") {
649797
- import_node_fs28.default.writeFileSync(filePath, serialized);
651367
+ import_node_fs29.default.writeFileSync(filePath, serialized);
649798
651368
  descriptions.push(`Saved output to file.`);
649799
651369
  }
649800
651370
  }
@@ -649962,9 +651532,9 @@ async function dragAndDropElement({ config, step, driver }) {
649962
651532
  }
649963
651533
 
649964
651534
  // dist/core/tests.js
649965
- var import_node_path26 = __toESM(require("node:path"), 1);
651535
+ var import_node_path27 = __toESM(require("node:path"), 1);
649966
651536
  var import_node_child_process15 = require("node:child_process");
649967
- var import_node_crypto9 = require("node:crypto");
651537
+ var import_node_crypto10 = require("node:crypto");
649968
651538
  init_appium();
649969
651539
  init_config();
649970
651540
 
@@ -650116,7 +651686,7 @@ function getIntegrationConfig(config, sourceIntegration) {
650116
651686
  var import_node_http2 = __toESM(require("node:http"), 1);
650117
651687
  var import_node_https3 = __toESM(require("node:https"), 1);
650118
651688
  var import_node_url6 = require("node:url");
650119
- var __dirname5 = import_node_path26.default.dirname((0, import_node_url6.fileURLToPath)(importMetaUrl));
651689
+ var __dirname5 = import_node_path27.default.dirname((0, import_node_url6.fileURLToPath)(importMetaUrl));
650120
651690
  var KNOWN_BROWSERS = ["firefox", "chrome", "safari", "webkit"];
650121
651691
  function killTree(pid, timeoutMs = 5e3) {
650122
651692
  return new Promise((resolve) => {
@@ -650261,7 +651831,7 @@ function getDriverCapabilities({ runnerDetails, name, options }) {
650261
651831
  prefs: {
650262
651832
  // Per-context download dir keeps concurrent recordings from
650263
651833
  // colliding on the same .webm filename in a shared temp dir.
650264
- "download.default_directory": options.downloadDir || import_node_os11.default.tmpdir(),
651834
+ "download.default_directory": options.downloadDir || import_node_os12.default.tmpdir(),
650265
651835
  "download.prompt_for_download": false,
650266
651836
  "download.directory_upgrade": true
650267
651837
  },
@@ -650762,7 +652332,7 @@ async function runSpecs({ resolvedTests }) {
650762
652332
  const runDir = getRunOutputDir(config, {
650763
652333
  create: runArchivesArtifacts(config, specs)
650764
652334
  });
650765
- const runId = import_node_path26.default.basename(runDir);
652335
+ const runId = import_node_path27.default.basename(runDir);
650766
652336
  const report = {
650767
652337
  runId,
650768
652338
  runDir,
@@ -651004,7 +652574,7 @@ async function runSpecs({ resolvedTests }) {
651004
652574
  }
651005
652575
  if (entry?.tempPath) {
651006
652576
  try {
651007
- import_node_fs29.default.unlinkSync(entry.tempPath);
652577
+ import_node_fs30.default.unlinkSync(entry.tempPath);
651008
652578
  } catch {
651009
652579
  }
651010
652580
  }
@@ -651876,7 +653446,7 @@ function buildAutoRecordStep({ config, spec, test, context }) {
651876
653446
  return null;
651877
653447
  const runDir = getRunOutputDir(config, { create: false });
651878
653448
  const contextSegment = capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context"));
651879
- const recordPath = import_node_path26.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", contextSegment, "recordings", `${contextSegment}.mp4`);
653449
+ const recordPath = import_node_path27.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", contextSegment, "recordings", `${contextSegment}.mp4`);
651880
653450
  return {
651881
653451
  // Mobile-target contexts record the device screen through the app driver
651882
653452
  // (the internal device plan, resolved from the platform) — pinning ffmpeg
@@ -651893,28 +653463,21 @@ function buildAutoRecordStep({ config, spec, test, context }) {
651893
653463
  __autoRecord: true
651894
653464
  };
651895
653465
  }
651896
- function capPathSegment(segment, max = 32) {
651897
- if (segment.length <= max)
651898
- return segment;
651899
- const hash = (0, import_node_crypto9.createHash)("sha1").update(segment).digest("hex").slice(0, 8);
651900
- const tail = segment.slice(segment.length - (max - hash.length - 1));
651901
- return `${hash}-${tail}`;
651902
- }
651903
653466
  async function captureAutoScreenshot({ config, driver, spec, test, context, step, stepIndex, stepCount }) {
651904
653467
  try {
651905
- const action = BROWSER_STEP_KEYS.find((key) => typeof step[key] !== "undefined") || "step";
651906
- const sanitizedTestId = sanitizeFilesystemName(String(test.testId ?? ""), "test");
651907
653468
  const runDir = getRunOutputDir(config);
651908
- const dir = import_node_path26.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizedTestId), "contexts", capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context")), "screenshots");
651909
- const stepIdString = sanitizeFilesystemName(String(step.stepId ?? ""), "step");
651910
- const stepRef = capPathSegment(stepIdString.startsWith(`${sanitizedTestId}~`) ? stepIdString.slice(sanitizedTestId.length + 1) : stepIdString);
651911
- const pad = Math.max(2, String(stepCount).length);
651912
- const fileName = `${String(stepIndex + 1).padStart(pad, "0")}-${action}-${stepRef}.png`;
653469
+ const dir = import_node_path27.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context")), "screenshots");
653470
+ const fileName = stepArtifactFileName({
653471
+ step,
653472
+ stepIndex,
653473
+ stepCount,
653474
+ testId: test.testId
653475
+ });
651913
653476
  const screenshotStep = {
651914
653477
  stepId: `${step.stepId}_auto`,
651915
653478
  description: "Automatic post-step screenshot",
651916
653479
  screenshot: {
651917
- path: import_node_path26.default.join(dir, fileName),
653480
+ path: import_node_path27.default.join(dir, fileName),
651918
653481
  overwrite: "true"
651919
653482
  }
651920
653483
  };
@@ -651927,7 +653490,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
651927
653490
  log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${captureResult.description}`);
651928
653491
  return null;
651929
653492
  }
651930
- return import_node_path26.default.relative(runDir, screenshotStep.screenshot.path).split(import_node_path26.default.sep).join("/");
653493
+ return import_node_path27.default.relative(runDir, screenshotStep.screenshot.path).split(import_node_path27.default.sep).join("/");
651931
653494
  } catch (error) {
651932
653495
  log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${error?.message ?? error}`);
651933
653496
  return null;
@@ -652355,7 +653918,7 @@ ${JSON.stringify(context, null, 2)}`);
652355
653918
  const usedStepIds = new Set(context.steps.map((s) => s.stepId).filter(Boolean));
652356
653919
  for (const step of context.steps) {
652357
653920
  if (!step.stepId) {
652358
- const baseId = sanitizeFilesystemName(`${test.testId}~s${contentHash(step)}`, `step-${(0, import_node_crypto9.randomUUID)()}`);
653921
+ const baseId = sanitizeFilesystemName(`${test.testId}~s${contentHash(step)}`, `step-${(0, import_node_crypto10.randomUUID)()}`);
652359
653922
  let stepId = baseId;
652360
653923
  let suffix = 2;
652361
653924
  while (usedStepIds.has(stepId)) {
@@ -652550,6 +654113,19 @@ ${JSON.stringify(r, null, 2)}`);
652550
654113
  if (capturedPath)
652551
654114
  stepReport.autoScreenshot = capturedPath;
652552
654115
  }
654116
+ for (const checkpointDriver of sessionDrivers(browserSessions, driver)) {
654117
+ await captureRecordingCheckpoints({
654118
+ config,
654119
+ driver: checkpointDriver,
654120
+ recordingHost: checkpointDriver,
654121
+ step,
654122
+ stepStatus: stepReport.result,
654123
+ stepIndex,
654124
+ stepCount: context.steps.length,
654125
+ testId: test.testId,
654126
+ appSession
654127
+ });
654128
+ }
652553
654129
  pushStepReport(stepReport);
652554
654130
  if (routingDecision.action === "stop" && !step._fromAfter) {
652555
654131
  if (routingDecision.scope !== "test") {
@@ -652655,7 +654231,7 @@ async function stopAllRecordings({ config, context, driver, contextReport }) {
652655
654231
  stopRecord: true,
652656
654232
  __stopAny: true,
652657
654233
  description: "Stopping recording",
652658
- stepId: (0, import_node_crypto9.randomUUID)()
654234
+ stepId: (0, import_node_crypto10.randomUUID)()
652659
654235
  };
652660
654236
  try {
652661
654237
  const stepResult = await runStep({
@@ -652753,8 +654329,16 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
652753
654329
  if (!Array.isArray(recordingHost.state.recordings))
652754
654330
  recordingHost.state.recordings = [];
652755
654331
  const handle = actionResult.recording;
652756
- handle.id = handle.id ?? (0, import_node_crypto9.randomUUID)();
654332
+ handle.id = handle.id ?? (0, import_node_crypto10.randomUUID)();
652757
654333
  handle.name = handle.name ?? recordStepName(step.record);
654334
+ handle.overwrite = step.record?.overwrite;
654335
+ if (handle.targetPath) {
654336
+ handle.checkpoints = resolveCheckpointsConfig({
654337
+ record: step.record,
654338
+ targetPath: handle.targetPath,
654339
+ handleId: handle.id
654340
+ });
654341
+ }
652758
654342
  if (step.__autoRecord) {
652759
654343
  handle.synthetic = true;
652760
654344
  if (!driver && appSession && !isMobileTargetPlatform(context?.platform)) {
@@ -652897,7 +654481,7 @@ async function spawnAppiumServer(appiumEntry, config, display, extraEnv, extraAr
652897
654481
  } : process.env;
652898
654482
  const proc = (0, import_node_child_process15.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(port), ...extraArgs ?? []], {
652899
654483
  windowsHide: true,
652900
- cwd: import_node_path26.default.join(__dirname5, "../.."),
654484
+ cwd: import_node_path27.default.join(__dirname5, "../.."),
652901
654485
  env
652902
654486
  });
652903
654487
  proc.on("error", (err) => {
@@ -653055,7 +654639,7 @@ async function getRunner(options = {}) {
653055
654639
  }
653056
654640
  const appium = (0, import_node_child_process15.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(appiumPort)], {
653057
654641
  windowsHide: true,
653058
- cwd: import_node_path26.default.join(__dirname5, "../..")
654642
+ cwd: import_node_path27.default.join(__dirname5, "../..")
653059
654643
  });
653060
654644
  appium.on("error", (err) => {
653061
654645
  log(config, "warning", `Appium process error: ${err?.stack ?? err?.message ?? String(err)}`);
@@ -653114,7 +654698,7 @@ async function getRunner(options = {}) {
653114
654698
  }
653115
654699
 
653116
654700
  // dist/core/telem.js
653117
- var import_node_os12 = __toESM(require("node:os"), 1);
654701
+ var import_node_os13 = __toESM(require("node:os"), 1);
653118
654702
  init_utils();
653119
654703
  var import_posthog_node = require("posthog-node");
653120
654704
  var import_node_module2 = require("node:module");
@@ -653142,11 +654726,11 @@ function sendTelemetry(config, command, results) {
653142
654726
  telemetryData.dist_interface = telemetryData.dist_interface || "package";
653143
654727
  telemetryData.core_version = pkg.version;
653144
654728
  telemetryData.dist_version = telemetryData.dist_version || telemetryData.core_version;
653145
- telemetryData.core_platform = platformMap2[import_node_os12.default.platform()] || import_node_os12.default.platform();
654729
+ telemetryData.core_platform = platformMap2[import_node_os13.default.platform()] || import_node_os13.default.platform();
653146
654730
  telemetryData.dist_platform = telemetryData.dist_platform || telemetryData.core_platform;
653147
- telemetryData.core_platform_version = import_node_os12.default.release();
654731
+ telemetryData.core_platform_version = import_node_os13.default.release();
653148
654732
  telemetryData.dist_platform_version = telemetryData.dist_platform_version || telemetryData.core_platform_version;
653149
- telemetryData.core_platform_arch = import_node_os12.default.arch();
654733
+ telemetryData.core_platform_arch = import_node_os13.default.arch();
653150
654734
  telemetryData.dist_platform_arch = telemetryData.dist_platform_arch || telemetryData.core_platform_arch;
653151
654735
  telemetryData.core_deployment = telemetryData.core_deployment || "node";
653152
654736
  telemetryData.dist_deployment = telemetryData.dist_deployment || telemetryData.core_deployment;