doc-detective 4.32.0 → 4.34.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 (39) hide show
  1. package/dist/common/src/schemas/schemas.json +2688 -126
  2. package/dist/common/src/types/generated/record_v3.d.ts +48 -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 +48 -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 +96 -4
  7. package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
  8. package/dist/core/tests/ffmpegRecorder.d.ts +12 -1
  9. package/dist/core/tests/ffmpegRecorder.d.ts.map +1 -1
  10. package/dist/core/tests/ffmpegRecorder.js +141 -3
  11. package/dist/core/tests/ffmpegRecorder.js.map +1 -1
  12. package/dist/core/tests/recordingCheckpoints.d.ts +76 -0
  13. package/dist/core/tests/recordingCheckpoints.d.ts.map +1 -0
  14. package/dist/core/tests/recordingCheckpoints.js +335 -0
  15. package/dist/core/tests/recordingCheckpoints.js.map +1 -0
  16. package/dist/core/tests/saveScreenshot.d.ts +5 -1
  17. package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
  18. package/dist/core/tests/saveScreenshot.js +46 -12
  19. package/dist/core/tests/saveScreenshot.js.map +1 -1
  20. package/dist/core/tests/startRecording.d.ts.map +1 -1
  21. package/dist/core/tests/startRecording.js +26 -10
  22. package/dist/core/tests/startRecording.js.map +1 -1
  23. package/dist/core/tests/stopRecording.d.ts.map +1 -1
  24. package/dist/core/tests/stopRecording.js +381 -15
  25. package/dist/core/tests/stopRecording.js.map +1 -1
  26. package/dist/core/tests.d.ts.map +1 -1
  27. package/dist/core/tests.js +54 -37
  28. package/dist/core/tests.js.map +1 -1
  29. package/dist/hints/context.d.ts +1 -0
  30. package/dist/hints/context.d.ts.map +1 -1
  31. package/dist/hints/context.js +9 -0
  32. package/dist/hints/context.js.map +1 -1
  33. package/dist/hints/hints.d.ts.map +1 -1
  34. package/dist/hints/hints.js +15 -0
  35. package/dist/hints/hints.js.map +1 -1
  36. package/dist/hints/types.d.ts +8 -0
  37. package/dist/hints/types.d.ts.map +1 -1
  38. package/dist/index.cjs +3464 -325
  39. package/package.json +1 -1
@@ -30428,9 +30428,10 @@
30428
30428
  "anyOf": [
30429
30429
  {
30430
30430
  "title": "Screenshot (simple)",
30431
+ "$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 — 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.",
30431
30432
  "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.",
30432
30433
  "type": "string",
30433
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30434
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30434
30435
  "transform": [
30435
30436
  "trim"
30436
30437
  ]
@@ -30664,9 +30665,10 @@
30664
30665
  },
30665
30666
  "path": {
30666
30667
  "title": "Screenshot (simple)",
30668
+ "$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 — 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.",
30667
30669
  "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.",
30668
30670
  "type": "string",
30669
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30671
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30670
30672
  "transform": [
30671
30673
  "trim"
30672
30674
  ]
@@ -30924,9 +30926,10 @@
30924
30926
  "schemas": {
30925
30927
  "path": {
30926
30928
  "title": "Screenshot (simple)",
30929
+ "$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 — 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.",
30927
30930
  "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.",
30928
30931
  "type": "string",
30929
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30932
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
30930
30933
  "transform": [
30931
30934
  "trim"
30932
30935
  ]
@@ -31160,9 +31163,10 @@
31160
31163
  },
31161
31164
  "path": {
31162
31165
  "title": "Screenshot (simple)",
31166
+ "$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 — 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.",
31163
31167
  "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.",
31164
31168
  "type": "string",
31165
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31169
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31166
31170
  "transform": [
31167
31171
  "trim"
31168
31172
  ]
@@ -31636,9 +31640,10 @@
31636
31640
  },
31637
31641
  "path": {
31638
31642
  "title": "Screenshot (simple)",
31643
+ "$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 — 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.",
31639
31644
  "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.",
31640
31645
  "type": "string",
31641
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31646
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
31642
31647
  "transform": [
31643
31648
  "trim"
31644
31649
  ]
@@ -32020,6 +32025,7 @@
32020
32025
  "image.png",
32021
32026
  "static/images/image.png",
32022
32027
  "/User/manny/projects/doc-detective/static/images/image.png",
32028
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
32023
32029
  "https://example.com/static/images/image.png",
32024
32030
  {
32025
32031
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -34454,10 +34460,11 @@
34454
34460
  },
34455
34461
  "overwrite": {
34456
34462
  "type": "string",
34457
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
34463
+ "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 — 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`.",
34458
34464
  "enum": [
34459
34465
  "true",
34460
- "false"
34466
+ "false",
34467
+ "aboveVariation"
34461
34468
  ]
34462
34469
  },
34463
34470
  "name": {
@@ -34515,6 +34522,88 @@
34515
34522
  }
34516
34523
  }
34517
34524
  ]
34525
+ },
34526
+ "verify": {
34527
+ "title": "Recording verify guards",
34528
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
34529
+ "type": "object",
34530
+ "additionalProperties": false,
34531
+ "properties": {
34532
+ "minDuration": {
34533
+ "type": "number",
34534
+ "description": "Fail if the video is shorter than this many seconds.",
34535
+ "minimum": 0
34536
+ },
34537
+ "maxDuration": {
34538
+ "type": "number",
34539
+ "description": "Fail if the video is longer than this many seconds.",
34540
+ "minimum": 0
34541
+ },
34542
+ "resolution": {
34543
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
34544
+ "anyOf": [
34545
+ {
34546
+ "type": "boolean",
34547
+ "title": "Verify resolution (boolean)"
34548
+ },
34549
+ {
34550
+ "type": "object",
34551
+ "title": "Verify resolution (detailed)",
34552
+ "additionalProperties": false,
34553
+ "required": [
34554
+ "width",
34555
+ "height"
34556
+ ],
34557
+ "properties": {
34558
+ "width": {
34559
+ "type": "integer",
34560
+ "minimum": 1
34561
+ },
34562
+ "height": {
34563
+ "type": "integer",
34564
+ "minimum": 1
34565
+ }
34566
+ }
34567
+ }
34568
+ ]
34569
+ },
34570
+ "notBlack": {
34571
+ "type": "boolean",
34572
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
34573
+ }
34574
+ }
34575
+ },
34576
+ "checkpoints": {
34577
+ "title": "Recording checkpoints",
34578
+ "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.",
34579
+ "anyOf": [
34580
+ {
34581
+ "type": "boolean",
34582
+ "title": "Recording checkpoints (boolean)",
34583
+ "description": "If `true`, enables checkpoints with default settings."
34584
+ },
34585
+ {
34586
+ "type": "object",
34587
+ "title": "Recording checkpoints (detailed)",
34588
+ "additionalProperties": false,
34589
+ "properties": {
34590
+ "maxVariation": {
34591
+ "type": "number",
34592
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
34593
+ "default": 0.05,
34594
+ "minimum": 0,
34595
+ "maximum": 1
34596
+ },
34597
+ "directory": {
34598
+ "type": "string",
34599
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
34600
+ "transform": [
34601
+ "trim"
34602
+ ]
34603
+ }
34604
+ }
34605
+ }
34606
+ ]
34518
34607
  }
34519
34608
  },
34520
34609
  "title": "Record (detailed)"
@@ -34776,10 +34865,11 @@
34776
34865
  },
34777
34866
  "overwrite": {
34778
34867
  "type": "string",
34779
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
34868
+ "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 — 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`.",
34780
34869
  "enum": [
34781
34870
  "true",
34782
- "false"
34871
+ "false",
34872
+ "aboveVariation"
34783
34873
  ]
34784
34874
  },
34785
34875
  "name": {
@@ -34837,6 +34927,88 @@
34837
34927
  }
34838
34928
  }
34839
34929
  ]
34930
+ },
34931
+ "verify": {
34932
+ "title": "Recording verify guards",
34933
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
34934
+ "type": "object",
34935
+ "additionalProperties": false,
34936
+ "properties": {
34937
+ "minDuration": {
34938
+ "type": "number",
34939
+ "description": "Fail if the video is shorter than this many seconds.",
34940
+ "minimum": 0
34941
+ },
34942
+ "maxDuration": {
34943
+ "type": "number",
34944
+ "description": "Fail if the video is longer than this many seconds.",
34945
+ "minimum": 0
34946
+ },
34947
+ "resolution": {
34948
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
34949
+ "anyOf": [
34950
+ {
34951
+ "type": "boolean",
34952
+ "title": "Verify resolution (boolean)"
34953
+ },
34954
+ {
34955
+ "type": "object",
34956
+ "title": "Verify resolution (detailed)",
34957
+ "additionalProperties": false,
34958
+ "required": [
34959
+ "width",
34960
+ "height"
34961
+ ],
34962
+ "properties": {
34963
+ "width": {
34964
+ "type": "integer",
34965
+ "minimum": 1
34966
+ },
34967
+ "height": {
34968
+ "type": "integer",
34969
+ "minimum": 1
34970
+ }
34971
+ }
34972
+ }
34973
+ ]
34974
+ },
34975
+ "notBlack": {
34976
+ "type": "boolean",
34977
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
34978
+ }
34979
+ }
34980
+ },
34981
+ "checkpoints": {
34982
+ "title": "Recording checkpoints",
34983
+ "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.",
34984
+ "anyOf": [
34985
+ {
34986
+ "type": "boolean",
34987
+ "title": "Recording checkpoints (boolean)",
34988
+ "description": "If `true`, enables checkpoints with default settings."
34989
+ },
34990
+ {
34991
+ "type": "object",
34992
+ "title": "Recording checkpoints (detailed)",
34993
+ "additionalProperties": false,
34994
+ "properties": {
34995
+ "maxVariation": {
34996
+ "type": "number",
34997
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
34998
+ "default": 0.05,
34999
+ "minimum": 0,
35000
+ "maximum": 1
35001
+ },
35002
+ "directory": {
35003
+ "type": "string",
35004
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
35005
+ "transform": [
35006
+ "trim"
35007
+ ]
35008
+ }
35009
+ }
35010
+ }
35011
+ ]
34840
35012
  }
34841
35013
  },
34842
35014
  "title": "Record (detailed)"
@@ -34903,6 +35075,17 @@
34903
35075
  "path": "results.mp4",
34904
35076
  "engine": "ffmpeg"
34905
35077
  },
35078
+ {
35079
+ "path": "results.mp4",
35080
+ "checkpoints": true
35081
+ },
35082
+ {
35083
+ "path": "results.mp4",
35084
+ "checkpoints": {
35085
+ "maxVariation": 0.02,
35086
+ "directory": "baselines/results"
35087
+ }
35088
+ },
34906
35089
  {
34907
35090
  "path": "results.mp4",
34908
35091
  "engine": {
@@ -76067,9 +76250,10 @@
76067
76250
  "anyOf": [
76068
76251
  {
76069
76252
  "title": "Screenshot (simple)",
76253
+ "$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 — 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.",
76070
76254
  "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.",
76071
76255
  "type": "string",
76072
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76256
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76073
76257
  "transform": [
76074
76258
  "trim"
76075
76259
  ]
@@ -76303,9 +76487,10 @@
76303
76487
  },
76304
76488
  "path": {
76305
76489
  "title": "Screenshot (simple)",
76490
+ "$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 — 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.",
76306
76491
  "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.",
76307
76492
  "type": "string",
76308
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76493
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76309
76494
  "transform": [
76310
76495
  "trim"
76311
76496
  ]
@@ -76563,9 +76748,10 @@
76563
76748
  "schemas": {
76564
76749
  "path": {
76565
76750
  "title": "Screenshot (simple)",
76751
+ "$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 — 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.",
76566
76752
  "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.",
76567
76753
  "type": "string",
76568
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76754
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76569
76755
  "transform": [
76570
76756
  "trim"
76571
76757
  ]
@@ -76799,9 +76985,10 @@
76799
76985
  },
76800
76986
  "path": {
76801
76987
  "title": "Screenshot (simple)",
76988
+ "$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 — 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.",
76802
76989
  "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.",
76803
76990
  "type": "string",
76804
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76991
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
76805
76992
  "transform": [
76806
76993
  "trim"
76807
76994
  ]
@@ -77275,9 +77462,10 @@
77275
77462
  },
77276
77463
  "path": {
77277
77464
  "title": "Screenshot (simple)",
77465
+ "$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 — 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.",
77278
77466
  "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.",
77279
77467
  "type": "string",
77280
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
77468
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
77281
77469
  "transform": [
77282
77470
  "trim"
77283
77471
  ]
@@ -77659,6 +77847,7 @@
77659
77847
  "image.png",
77660
77848
  "static/images/image.png",
77661
77849
  "/User/manny/projects/doc-detective/static/images/image.png",
77850
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
77662
77851
  "https://example.com/static/images/image.png",
77663
77852
  {
77664
77853
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -80093,10 +80282,11 @@
80093
80282
  },
80094
80283
  "overwrite": {
80095
80284
  "type": "string",
80096
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
80285
+ "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 — 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`.",
80097
80286
  "enum": [
80098
80287
  "true",
80099
- "false"
80288
+ "false",
80289
+ "aboveVariation"
80100
80290
  ]
80101
80291
  },
80102
80292
  "name": {
@@ -80154,6 +80344,88 @@
80154
80344
  }
80155
80345
  }
80156
80346
  ]
80347
+ },
80348
+ "verify": {
80349
+ "title": "Recording verify guards",
80350
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
80351
+ "type": "object",
80352
+ "additionalProperties": false,
80353
+ "properties": {
80354
+ "minDuration": {
80355
+ "type": "number",
80356
+ "description": "Fail if the video is shorter than this many seconds.",
80357
+ "minimum": 0
80358
+ },
80359
+ "maxDuration": {
80360
+ "type": "number",
80361
+ "description": "Fail if the video is longer than this many seconds.",
80362
+ "minimum": 0
80363
+ },
80364
+ "resolution": {
80365
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
80366
+ "anyOf": [
80367
+ {
80368
+ "type": "boolean",
80369
+ "title": "Verify resolution (boolean)"
80370
+ },
80371
+ {
80372
+ "type": "object",
80373
+ "title": "Verify resolution (detailed)",
80374
+ "additionalProperties": false,
80375
+ "required": [
80376
+ "width",
80377
+ "height"
80378
+ ],
80379
+ "properties": {
80380
+ "width": {
80381
+ "type": "integer",
80382
+ "minimum": 1
80383
+ },
80384
+ "height": {
80385
+ "type": "integer",
80386
+ "minimum": 1
80387
+ }
80388
+ }
80389
+ }
80390
+ ]
80391
+ },
80392
+ "notBlack": {
80393
+ "type": "boolean",
80394
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
80395
+ }
80396
+ }
80397
+ },
80398
+ "checkpoints": {
80399
+ "title": "Recording checkpoints",
80400
+ "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.",
80401
+ "anyOf": [
80402
+ {
80403
+ "type": "boolean",
80404
+ "title": "Recording checkpoints (boolean)",
80405
+ "description": "If `true`, enables checkpoints with default settings."
80406
+ },
80407
+ {
80408
+ "type": "object",
80409
+ "title": "Recording checkpoints (detailed)",
80410
+ "additionalProperties": false,
80411
+ "properties": {
80412
+ "maxVariation": {
80413
+ "type": "number",
80414
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
80415
+ "default": 0.05,
80416
+ "minimum": 0,
80417
+ "maximum": 1
80418
+ },
80419
+ "directory": {
80420
+ "type": "string",
80421
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
80422
+ "transform": [
80423
+ "trim"
80424
+ ]
80425
+ }
80426
+ }
80427
+ }
80428
+ ]
80157
80429
  }
80158
80430
  },
80159
80431
  "title": "Record (detailed)"
@@ -80415,10 +80687,11 @@
80415
80687
  },
80416
80688
  "overwrite": {
80417
80689
  "type": "string",
80418
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
80690
+ "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 — 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`.",
80419
80691
  "enum": [
80420
80692
  "true",
80421
- "false"
80693
+ "false",
80694
+ "aboveVariation"
80422
80695
  ]
80423
80696
  },
80424
80697
  "name": {
@@ -80476,6 +80749,88 @@
80476
80749
  }
80477
80750
  }
80478
80751
  ]
80752
+ },
80753
+ "verify": {
80754
+ "title": "Recording verify guards",
80755
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
80756
+ "type": "object",
80757
+ "additionalProperties": false,
80758
+ "properties": {
80759
+ "minDuration": {
80760
+ "type": "number",
80761
+ "description": "Fail if the video is shorter than this many seconds.",
80762
+ "minimum": 0
80763
+ },
80764
+ "maxDuration": {
80765
+ "type": "number",
80766
+ "description": "Fail if the video is longer than this many seconds.",
80767
+ "minimum": 0
80768
+ },
80769
+ "resolution": {
80770
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
80771
+ "anyOf": [
80772
+ {
80773
+ "type": "boolean",
80774
+ "title": "Verify resolution (boolean)"
80775
+ },
80776
+ {
80777
+ "type": "object",
80778
+ "title": "Verify resolution (detailed)",
80779
+ "additionalProperties": false,
80780
+ "required": [
80781
+ "width",
80782
+ "height"
80783
+ ],
80784
+ "properties": {
80785
+ "width": {
80786
+ "type": "integer",
80787
+ "minimum": 1
80788
+ },
80789
+ "height": {
80790
+ "type": "integer",
80791
+ "minimum": 1
80792
+ }
80793
+ }
80794
+ }
80795
+ ]
80796
+ },
80797
+ "notBlack": {
80798
+ "type": "boolean",
80799
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
80800
+ }
80801
+ }
80802
+ },
80803
+ "checkpoints": {
80804
+ "title": "Recording checkpoints",
80805
+ "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.",
80806
+ "anyOf": [
80807
+ {
80808
+ "type": "boolean",
80809
+ "title": "Recording checkpoints (boolean)",
80810
+ "description": "If `true`, enables checkpoints with default settings."
80811
+ },
80812
+ {
80813
+ "type": "object",
80814
+ "title": "Recording checkpoints (detailed)",
80815
+ "additionalProperties": false,
80816
+ "properties": {
80817
+ "maxVariation": {
80818
+ "type": "number",
80819
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
80820
+ "default": 0.05,
80821
+ "minimum": 0,
80822
+ "maximum": 1
80823
+ },
80824
+ "directory": {
80825
+ "type": "string",
80826
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
80827
+ "transform": [
80828
+ "trim"
80829
+ ]
80830
+ }
80831
+ }
80832
+ }
80833
+ ]
80479
80834
  }
80480
80835
  },
80481
80836
  "title": "Record (detailed)"
@@ -80542,6 +80897,17 @@
80542
80897
  "path": "results.mp4",
80543
80898
  "engine": "ffmpeg"
80544
80899
  },
80900
+ {
80901
+ "path": "results.mp4",
80902
+ "checkpoints": true
80903
+ },
80904
+ {
80905
+ "path": "results.mp4",
80906
+ "checkpoints": {
80907
+ "maxVariation": 0.02,
80908
+ "directory": "baselines/results"
80909
+ }
80910
+ },
80545
80911
  {
80546
80912
  "path": "results.mp4",
80547
80913
  "engine": {
@@ -106378,10 +106744,11 @@
106378
106744
  },
106379
106745
  "overwrite": {
106380
106746
  "type": "string",
106381
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
106747
+ "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 — 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`.",
106382
106748
  "enum": [
106383
106749
  "true",
106384
- "false"
106750
+ "false",
106751
+ "aboveVariation"
106385
106752
  ]
106386
106753
  },
106387
106754
  "name": {
@@ -106439,6 +106806,88 @@
106439
106806
  }
106440
106807
  }
106441
106808
  ]
106809
+ },
106810
+ "verify": {
106811
+ "title": "Recording verify guards",
106812
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
106813
+ "type": "object",
106814
+ "additionalProperties": false,
106815
+ "properties": {
106816
+ "minDuration": {
106817
+ "type": "number",
106818
+ "description": "Fail if the video is shorter than this many seconds.",
106819
+ "minimum": 0
106820
+ },
106821
+ "maxDuration": {
106822
+ "type": "number",
106823
+ "description": "Fail if the video is longer than this many seconds.",
106824
+ "minimum": 0
106825
+ },
106826
+ "resolution": {
106827
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
106828
+ "anyOf": [
106829
+ {
106830
+ "type": "boolean",
106831
+ "title": "Verify resolution (boolean)"
106832
+ },
106833
+ {
106834
+ "type": "object",
106835
+ "title": "Verify resolution (detailed)",
106836
+ "additionalProperties": false,
106837
+ "required": [
106838
+ "width",
106839
+ "height"
106840
+ ],
106841
+ "properties": {
106842
+ "width": {
106843
+ "type": "integer",
106844
+ "minimum": 1
106845
+ },
106846
+ "height": {
106847
+ "type": "integer",
106848
+ "minimum": 1
106849
+ }
106850
+ }
106851
+ }
106852
+ ]
106853
+ },
106854
+ "notBlack": {
106855
+ "type": "boolean",
106856
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
106857
+ }
106858
+ }
106859
+ },
106860
+ "checkpoints": {
106861
+ "title": "Recording checkpoints",
106862
+ "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.",
106863
+ "anyOf": [
106864
+ {
106865
+ "type": "boolean",
106866
+ "title": "Recording checkpoints (boolean)",
106867
+ "description": "If `true`, enables checkpoints with default settings."
106868
+ },
106869
+ {
106870
+ "type": "object",
106871
+ "title": "Recording checkpoints (detailed)",
106872
+ "additionalProperties": false,
106873
+ "properties": {
106874
+ "maxVariation": {
106875
+ "type": "number",
106876
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
106877
+ "default": 0.05,
106878
+ "minimum": 0,
106879
+ "maximum": 1
106880
+ },
106881
+ "directory": {
106882
+ "type": "string",
106883
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
106884
+ "transform": [
106885
+ "trim"
106886
+ ]
106887
+ }
106888
+ }
106889
+ }
106890
+ ]
106442
106891
  }
106443
106892
  },
106444
106893
  "title": "Record (detailed)"
@@ -106700,10 +107149,11 @@
106700
107149
  },
106701
107150
  "overwrite": {
106702
107151
  "type": "string",
106703
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
107152
+ "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 — 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`.",
106704
107153
  "enum": [
106705
107154
  "true",
106706
- "false"
107155
+ "false",
107156
+ "aboveVariation"
106707
107157
  ]
106708
107158
  },
106709
107159
  "name": {
@@ -106761,6 +107211,88 @@
106761
107211
  }
106762
107212
  }
106763
107213
  ]
107214
+ },
107215
+ "verify": {
107216
+ "title": "Recording verify guards",
107217
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
107218
+ "type": "object",
107219
+ "additionalProperties": false,
107220
+ "properties": {
107221
+ "minDuration": {
107222
+ "type": "number",
107223
+ "description": "Fail if the video is shorter than this many seconds.",
107224
+ "minimum": 0
107225
+ },
107226
+ "maxDuration": {
107227
+ "type": "number",
107228
+ "description": "Fail if the video is longer than this many seconds.",
107229
+ "minimum": 0
107230
+ },
107231
+ "resolution": {
107232
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
107233
+ "anyOf": [
107234
+ {
107235
+ "type": "boolean",
107236
+ "title": "Verify resolution (boolean)"
107237
+ },
107238
+ {
107239
+ "type": "object",
107240
+ "title": "Verify resolution (detailed)",
107241
+ "additionalProperties": false,
107242
+ "required": [
107243
+ "width",
107244
+ "height"
107245
+ ],
107246
+ "properties": {
107247
+ "width": {
107248
+ "type": "integer",
107249
+ "minimum": 1
107250
+ },
107251
+ "height": {
107252
+ "type": "integer",
107253
+ "minimum": 1
107254
+ }
107255
+ }
107256
+ }
107257
+ ]
107258
+ },
107259
+ "notBlack": {
107260
+ "type": "boolean",
107261
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
107262
+ }
107263
+ }
107264
+ },
107265
+ "checkpoints": {
107266
+ "title": "Recording checkpoints",
107267
+ "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.",
107268
+ "anyOf": [
107269
+ {
107270
+ "type": "boolean",
107271
+ "title": "Recording checkpoints (boolean)",
107272
+ "description": "If `true`, enables checkpoints with default settings."
107273
+ },
107274
+ {
107275
+ "type": "object",
107276
+ "title": "Recording checkpoints (detailed)",
107277
+ "additionalProperties": false,
107278
+ "properties": {
107279
+ "maxVariation": {
107280
+ "type": "number",
107281
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
107282
+ "default": 0.05,
107283
+ "minimum": 0,
107284
+ "maximum": 1
107285
+ },
107286
+ "directory": {
107287
+ "type": "string",
107288
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
107289
+ "transform": [
107290
+ "trim"
107291
+ ]
107292
+ }
107293
+ }
107294
+ }
107295
+ ]
106764
107296
  }
106765
107297
  },
106766
107298
  "title": "Record (detailed)"
@@ -106827,6 +107359,17 @@
106827
107359
  "path": "results.mp4",
106828
107360
  "engine": "ffmpeg"
106829
107361
  },
107362
+ {
107363
+ "path": "results.mp4",
107364
+ "checkpoints": true
107365
+ },
107366
+ {
107367
+ "path": "results.mp4",
107368
+ "checkpoints": {
107369
+ "maxVariation": 0.02,
107370
+ "directory": "baselines/results"
107371
+ }
107372
+ },
106830
107373
  {
106831
107374
  "path": "results.mp4",
106832
107375
  "engine": {
@@ -134993,9 +135536,10 @@
134993
135536
  "anyOf": [
134994
135537
  {
134995
135538
  "title": "Screenshot (simple)",
135539
+ "$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 — 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.",
134996
135540
  "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.",
134997
135541
  "type": "string",
134998
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135542
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
134999
135543
  "transform": [
135000
135544
  "trim"
135001
135545
  ]
@@ -135229,9 +135773,10 @@
135229
135773
  },
135230
135774
  "path": {
135231
135775
  "title": "Screenshot (simple)",
135776
+ "$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 — 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.",
135232
135777
  "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.",
135233
135778
  "type": "string",
135234
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135779
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135235
135780
  "transform": [
135236
135781
  "trim"
135237
135782
  ]
@@ -135489,9 +136034,10 @@
135489
136034
  "schemas": {
135490
136035
  "path": {
135491
136036
  "title": "Screenshot (simple)",
136037
+ "$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 — 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.",
135492
136038
  "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.",
135493
136039
  "type": "string",
135494
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136040
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135495
136041
  "transform": [
135496
136042
  "trim"
135497
136043
  ]
@@ -135725,9 +136271,10 @@
135725
136271
  },
135726
136272
  "path": {
135727
136273
  "title": "Screenshot (simple)",
136274
+ "$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 — 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.",
135728
136275
  "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.",
135729
136276
  "type": "string",
135730
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136277
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
135731
136278
  "transform": [
135732
136279
  "trim"
135733
136280
  ]
@@ -136201,9 +136748,10 @@
136201
136748
  },
136202
136749
  "path": {
136203
136750
  "title": "Screenshot (simple)",
136751
+ "$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 — 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.",
136204
136752
  "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.",
136205
136753
  "type": "string",
136206
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136754
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
136207
136755
  "transform": [
136208
136756
  "trim"
136209
136757
  ]
@@ -136585,6 +137133,7 @@
136585
137133
  "image.png",
136586
137134
  "static/images/image.png",
136587
137135
  "/User/manny/projects/doc-detective/static/images/image.png",
137136
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
136588
137137
  "https://example.com/static/images/image.png",
136589
137138
  {
136590
137139
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -139019,10 +139568,11 @@
139019
139568
  },
139020
139569
  "overwrite": {
139021
139570
  "type": "string",
139022
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
139571
+ "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 — 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`.",
139023
139572
  "enum": [
139024
139573
  "true",
139025
- "false"
139574
+ "false",
139575
+ "aboveVariation"
139026
139576
  ]
139027
139577
  },
139028
139578
  "name": {
@@ -139080,6 +139630,88 @@
139080
139630
  }
139081
139631
  }
139082
139632
  ]
139633
+ },
139634
+ "verify": {
139635
+ "title": "Recording verify guards",
139636
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
139637
+ "type": "object",
139638
+ "additionalProperties": false,
139639
+ "properties": {
139640
+ "minDuration": {
139641
+ "type": "number",
139642
+ "description": "Fail if the video is shorter than this many seconds.",
139643
+ "minimum": 0
139644
+ },
139645
+ "maxDuration": {
139646
+ "type": "number",
139647
+ "description": "Fail if the video is longer than this many seconds.",
139648
+ "minimum": 0
139649
+ },
139650
+ "resolution": {
139651
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
139652
+ "anyOf": [
139653
+ {
139654
+ "type": "boolean",
139655
+ "title": "Verify resolution (boolean)"
139656
+ },
139657
+ {
139658
+ "type": "object",
139659
+ "title": "Verify resolution (detailed)",
139660
+ "additionalProperties": false,
139661
+ "required": [
139662
+ "width",
139663
+ "height"
139664
+ ],
139665
+ "properties": {
139666
+ "width": {
139667
+ "type": "integer",
139668
+ "minimum": 1
139669
+ },
139670
+ "height": {
139671
+ "type": "integer",
139672
+ "minimum": 1
139673
+ }
139674
+ }
139675
+ }
139676
+ ]
139677
+ },
139678
+ "notBlack": {
139679
+ "type": "boolean",
139680
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
139681
+ }
139682
+ }
139683
+ },
139684
+ "checkpoints": {
139685
+ "title": "Recording checkpoints",
139686
+ "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.",
139687
+ "anyOf": [
139688
+ {
139689
+ "type": "boolean",
139690
+ "title": "Recording checkpoints (boolean)",
139691
+ "description": "If `true`, enables checkpoints with default settings."
139692
+ },
139693
+ {
139694
+ "type": "object",
139695
+ "title": "Recording checkpoints (detailed)",
139696
+ "additionalProperties": false,
139697
+ "properties": {
139698
+ "maxVariation": {
139699
+ "type": "number",
139700
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
139701
+ "default": 0.05,
139702
+ "minimum": 0,
139703
+ "maximum": 1
139704
+ },
139705
+ "directory": {
139706
+ "type": "string",
139707
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
139708
+ "transform": [
139709
+ "trim"
139710
+ ]
139711
+ }
139712
+ }
139713
+ }
139714
+ ]
139083
139715
  }
139084
139716
  },
139085
139717
  "title": "Record (detailed)"
@@ -139341,10 +139973,11 @@
139341
139973
  },
139342
139974
  "overwrite": {
139343
139975
  "type": "string",
139344
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
139976
+ "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 — 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`.",
139345
139977
  "enum": [
139346
139978
  "true",
139347
- "false"
139979
+ "false",
139980
+ "aboveVariation"
139348
139981
  ]
139349
139982
  },
139350
139983
  "name": {
@@ -139402,6 +140035,88 @@
139402
140035
  }
139403
140036
  }
139404
140037
  ]
140038
+ },
140039
+ "verify": {
140040
+ "title": "Recording verify guards",
140041
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
140042
+ "type": "object",
140043
+ "additionalProperties": false,
140044
+ "properties": {
140045
+ "minDuration": {
140046
+ "type": "number",
140047
+ "description": "Fail if the video is shorter than this many seconds.",
140048
+ "minimum": 0
140049
+ },
140050
+ "maxDuration": {
140051
+ "type": "number",
140052
+ "description": "Fail if the video is longer than this many seconds.",
140053
+ "minimum": 0
140054
+ },
140055
+ "resolution": {
140056
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
140057
+ "anyOf": [
140058
+ {
140059
+ "type": "boolean",
140060
+ "title": "Verify resolution (boolean)"
140061
+ },
140062
+ {
140063
+ "type": "object",
140064
+ "title": "Verify resolution (detailed)",
140065
+ "additionalProperties": false,
140066
+ "required": [
140067
+ "width",
140068
+ "height"
140069
+ ],
140070
+ "properties": {
140071
+ "width": {
140072
+ "type": "integer",
140073
+ "minimum": 1
140074
+ },
140075
+ "height": {
140076
+ "type": "integer",
140077
+ "minimum": 1
140078
+ }
140079
+ }
140080
+ }
140081
+ ]
140082
+ },
140083
+ "notBlack": {
140084
+ "type": "boolean",
140085
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
140086
+ }
140087
+ }
140088
+ },
140089
+ "checkpoints": {
140090
+ "title": "Recording checkpoints",
140091
+ "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.",
140092
+ "anyOf": [
140093
+ {
140094
+ "type": "boolean",
140095
+ "title": "Recording checkpoints (boolean)",
140096
+ "description": "If `true`, enables checkpoints with default settings."
140097
+ },
140098
+ {
140099
+ "type": "object",
140100
+ "title": "Recording checkpoints (detailed)",
140101
+ "additionalProperties": false,
140102
+ "properties": {
140103
+ "maxVariation": {
140104
+ "type": "number",
140105
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
140106
+ "default": 0.05,
140107
+ "minimum": 0,
140108
+ "maximum": 1
140109
+ },
140110
+ "directory": {
140111
+ "type": "string",
140112
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
140113
+ "transform": [
140114
+ "trim"
140115
+ ]
140116
+ }
140117
+ }
140118
+ }
140119
+ ]
139405
140120
  }
139406
140121
  },
139407
140122
  "title": "Record (detailed)"
@@ -139468,6 +140183,17 @@
139468
140183
  "path": "results.mp4",
139469
140184
  "engine": "ffmpeg"
139470
140185
  },
140186
+ {
140187
+ "path": "results.mp4",
140188
+ "checkpoints": true
140189
+ },
140190
+ {
140191
+ "path": "results.mp4",
140192
+ "checkpoints": {
140193
+ "maxVariation": 0.02,
140194
+ "directory": "baselines/results"
140195
+ }
140196
+ },
139471
140197
  {
139472
140198
  "path": "results.mp4",
139473
140199
  "engine": {
@@ -180632,9 +181358,10 @@
180632
181358
  "anyOf": [
180633
181359
  {
180634
181360
  "title": "Screenshot (simple)",
181361
+ "$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 — 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.",
180635
181362
  "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.",
180636
181363
  "type": "string",
180637
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181364
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
180638
181365
  "transform": [
180639
181366
  "trim"
180640
181367
  ]
@@ -180868,9 +181595,10 @@
180868
181595
  },
180869
181596
  "path": {
180870
181597
  "title": "Screenshot (simple)",
181598
+ "$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 — 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.",
180871
181599
  "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.",
180872
181600
  "type": "string",
180873
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181601
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
180874
181602
  "transform": [
180875
181603
  "trim"
180876
181604
  ]
@@ -181128,9 +181856,10 @@
181128
181856
  "schemas": {
181129
181857
  "path": {
181130
181858
  "title": "Screenshot (simple)",
181859
+ "$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 — 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.",
181131
181860
  "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.",
181132
181861
  "type": "string",
181133
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181862
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181134
181863
  "transform": [
181135
181864
  "trim"
181136
181865
  ]
@@ -181364,9 +182093,10 @@
181364
182093
  },
181365
182094
  "path": {
181366
182095
  "title": "Screenshot (simple)",
182096
+ "$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 — 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.",
181367
182097
  "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.",
181368
182098
  "type": "string",
181369
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
182099
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181370
182100
  "transform": [
181371
182101
  "trim"
181372
182102
  ]
@@ -181840,9 +182570,10 @@
181840
182570
  },
181841
182571
  "path": {
181842
182572
  "title": "Screenshot (simple)",
182573
+ "$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 — 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.",
181843
182574
  "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.",
181844
182575
  "type": "string",
181845
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
182576
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
181846
182577
  "transform": [
181847
182578
  "trim"
181848
182579
  ]
@@ -182224,6 +182955,7 @@
182224
182955
  "image.png",
182225
182956
  "static/images/image.png",
182226
182957
  "/User/manny/projects/doc-detective/static/images/image.png",
182958
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
182227
182959
  "https://example.com/static/images/image.png",
182228
182960
  {
182229
182961
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -184658,10 +185390,11 @@
184658
185390
  },
184659
185391
  "overwrite": {
184660
185392
  "type": "string",
184661
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
185393
+ "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 — 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`.",
184662
185394
  "enum": [
184663
185395
  "true",
184664
- "false"
185396
+ "false",
185397
+ "aboveVariation"
184665
185398
  ]
184666
185399
  },
184667
185400
  "name": {
@@ -184719,6 +185452,88 @@
184719
185452
  }
184720
185453
  }
184721
185454
  ]
185455
+ },
185456
+ "verify": {
185457
+ "title": "Recording verify guards",
185458
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
185459
+ "type": "object",
185460
+ "additionalProperties": false,
185461
+ "properties": {
185462
+ "minDuration": {
185463
+ "type": "number",
185464
+ "description": "Fail if the video is shorter than this many seconds.",
185465
+ "minimum": 0
185466
+ },
185467
+ "maxDuration": {
185468
+ "type": "number",
185469
+ "description": "Fail if the video is longer than this many seconds.",
185470
+ "minimum": 0
185471
+ },
185472
+ "resolution": {
185473
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
185474
+ "anyOf": [
185475
+ {
185476
+ "type": "boolean",
185477
+ "title": "Verify resolution (boolean)"
185478
+ },
185479
+ {
185480
+ "type": "object",
185481
+ "title": "Verify resolution (detailed)",
185482
+ "additionalProperties": false,
185483
+ "required": [
185484
+ "width",
185485
+ "height"
185486
+ ],
185487
+ "properties": {
185488
+ "width": {
185489
+ "type": "integer",
185490
+ "minimum": 1
185491
+ },
185492
+ "height": {
185493
+ "type": "integer",
185494
+ "minimum": 1
185495
+ }
185496
+ }
185497
+ }
185498
+ ]
185499
+ },
185500
+ "notBlack": {
185501
+ "type": "boolean",
185502
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
185503
+ }
185504
+ }
185505
+ },
185506
+ "checkpoints": {
185507
+ "title": "Recording checkpoints",
185508
+ "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.",
185509
+ "anyOf": [
185510
+ {
185511
+ "type": "boolean",
185512
+ "title": "Recording checkpoints (boolean)",
185513
+ "description": "If `true`, enables checkpoints with default settings."
185514
+ },
185515
+ {
185516
+ "type": "object",
185517
+ "title": "Recording checkpoints (detailed)",
185518
+ "additionalProperties": false,
185519
+ "properties": {
185520
+ "maxVariation": {
185521
+ "type": "number",
185522
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
185523
+ "default": 0.05,
185524
+ "minimum": 0,
185525
+ "maximum": 1
185526
+ },
185527
+ "directory": {
185528
+ "type": "string",
185529
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
185530
+ "transform": [
185531
+ "trim"
185532
+ ]
185533
+ }
185534
+ }
185535
+ }
185536
+ ]
184722
185537
  }
184723
185538
  },
184724
185539
  "title": "Record (detailed)"
@@ -184980,10 +185795,11 @@
184980
185795
  },
184981
185796
  "overwrite": {
184982
185797
  "type": "string",
184983
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
185798
+ "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 — 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`.",
184984
185799
  "enum": [
184985
185800
  "true",
184986
- "false"
185801
+ "false",
185802
+ "aboveVariation"
184987
185803
  ]
184988
185804
  },
184989
185805
  "name": {
@@ -185041,6 +185857,88 @@
185041
185857
  }
185042
185858
  }
185043
185859
  ]
185860
+ },
185861
+ "verify": {
185862
+ "title": "Recording verify guards",
185863
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
185864
+ "type": "object",
185865
+ "additionalProperties": false,
185866
+ "properties": {
185867
+ "minDuration": {
185868
+ "type": "number",
185869
+ "description": "Fail if the video is shorter than this many seconds.",
185870
+ "minimum": 0
185871
+ },
185872
+ "maxDuration": {
185873
+ "type": "number",
185874
+ "description": "Fail if the video is longer than this many seconds.",
185875
+ "minimum": 0
185876
+ },
185877
+ "resolution": {
185878
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
185879
+ "anyOf": [
185880
+ {
185881
+ "type": "boolean",
185882
+ "title": "Verify resolution (boolean)"
185883
+ },
185884
+ {
185885
+ "type": "object",
185886
+ "title": "Verify resolution (detailed)",
185887
+ "additionalProperties": false,
185888
+ "required": [
185889
+ "width",
185890
+ "height"
185891
+ ],
185892
+ "properties": {
185893
+ "width": {
185894
+ "type": "integer",
185895
+ "minimum": 1
185896
+ },
185897
+ "height": {
185898
+ "type": "integer",
185899
+ "minimum": 1
185900
+ }
185901
+ }
185902
+ }
185903
+ ]
185904
+ },
185905
+ "notBlack": {
185906
+ "type": "boolean",
185907
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
185908
+ }
185909
+ }
185910
+ },
185911
+ "checkpoints": {
185912
+ "title": "Recording checkpoints",
185913
+ "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.",
185914
+ "anyOf": [
185915
+ {
185916
+ "type": "boolean",
185917
+ "title": "Recording checkpoints (boolean)",
185918
+ "description": "If `true`, enables checkpoints with default settings."
185919
+ },
185920
+ {
185921
+ "type": "object",
185922
+ "title": "Recording checkpoints (detailed)",
185923
+ "additionalProperties": false,
185924
+ "properties": {
185925
+ "maxVariation": {
185926
+ "type": "number",
185927
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
185928
+ "default": 0.05,
185929
+ "minimum": 0,
185930
+ "maximum": 1
185931
+ },
185932
+ "directory": {
185933
+ "type": "string",
185934
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
185935
+ "transform": [
185936
+ "trim"
185937
+ ]
185938
+ }
185939
+ }
185940
+ }
185941
+ ]
185044
185942
  }
185045
185943
  },
185046
185944
  "title": "Record (detailed)"
@@ -185107,6 +186005,17 @@
185107
186005
  "path": "results.mp4",
185108
186006
  "engine": "ffmpeg"
185109
186007
  },
186008
+ {
186009
+ "path": "results.mp4",
186010
+ "checkpoints": true
186011
+ },
186012
+ {
186013
+ "path": "results.mp4",
186014
+ "checkpoints": {
186015
+ "maxVariation": 0.02,
186016
+ "directory": "baselines/results"
186017
+ }
186018
+ },
185110
186019
  {
185111
186020
  "path": "results.mp4",
185112
186021
  "engine": {
@@ -228010,9 +228919,10 @@
228010
228919
  "anyOf": [
228011
228920
  {
228012
228921
  "title": "Screenshot (simple)",
228922
+ "$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 — 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.",
228013
228923
  "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.",
228014
228924
  "type": "string",
228015
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228925
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228016
228926
  "transform": [
228017
228927
  "trim"
228018
228928
  ]
@@ -228246,9 +229156,10 @@
228246
229156
  },
228247
229157
  "path": {
228248
229158
  "title": "Screenshot (simple)",
229159
+ "$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 — 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.",
228249
229160
  "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.",
228250
229161
  "type": "string",
228251
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229162
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228252
229163
  "transform": [
228253
229164
  "trim"
228254
229165
  ]
@@ -228506,9 +229417,10 @@
228506
229417
  "schemas": {
228507
229418
  "path": {
228508
229419
  "title": "Screenshot (simple)",
229420
+ "$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 — 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.",
228509
229421
  "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.",
228510
229422
  "type": "string",
228511
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229423
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228512
229424
  "transform": [
228513
229425
  "trim"
228514
229426
  ]
@@ -228742,9 +229654,10 @@
228742
229654
  },
228743
229655
  "path": {
228744
229656
  "title": "Screenshot (simple)",
229657
+ "$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 — 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.",
228745
229658
  "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.",
228746
229659
  "type": "string",
228747
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229660
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
228748
229661
  "transform": [
228749
229662
  "trim"
228750
229663
  ]
@@ -229218,9 +230131,10 @@
229218
230131
  },
229219
230132
  "path": {
229220
230133
  "title": "Screenshot (simple)",
230134
+ "$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 — 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.",
229221
230135
  "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.",
229222
230136
  "type": "string",
229223
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
230137
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
229224
230138
  "transform": [
229225
230139
  "trim"
229226
230140
  ]
@@ -229602,6 +230516,7 @@
229602
230516
  "image.png",
229603
230517
  "static/images/image.png",
229604
230518
  "/User/manny/projects/doc-detective/static/images/image.png",
230519
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
229605
230520
  "https://example.com/static/images/image.png",
229606
230521
  {
229607
230522
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -232036,10 +232951,11 @@
232036
232951
  },
232037
232952
  "overwrite": {
232038
232953
  "type": "string",
232039
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
232954
+ "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 — 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`.",
232040
232955
  "enum": [
232041
232956
  "true",
232042
- "false"
232957
+ "false",
232958
+ "aboveVariation"
232043
232959
  ]
232044
232960
  },
232045
232961
  "name": {
@@ -232097,6 +233013,88 @@
232097
233013
  }
232098
233014
  }
232099
233015
  ]
233016
+ },
233017
+ "verify": {
233018
+ "title": "Recording verify guards",
233019
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
233020
+ "type": "object",
233021
+ "additionalProperties": false,
233022
+ "properties": {
233023
+ "minDuration": {
233024
+ "type": "number",
233025
+ "description": "Fail if the video is shorter than this many seconds.",
233026
+ "minimum": 0
233027
+ },
233028
+ "maxDuration": {
233029
+ "type": "number",
233030
+ "description": "Fail if the video is longer than this many seconds.",
233031
+ "minimum": 0
233032
+ },
233033
+ "resolution": {
233034
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
233035
+ "anyOf": [
233036
+ {
233037
+ "type": "boolean",
233038
+ "title": "Verify resolution (boolean)"
233039
+ },
233040
+ {
233041
+ "type": "object",
233042
+ "title": "Verify resolution (detailed)",
233043
+ "additionalProperties": false,
233044
+ "required": [
233045
+ "width",
233046
+ "height"
233047
+ ],
233048
+ "properties": {
233049
+ "width": {
233050
+ "type": "integer",
233051
+ "minimum": 1
233052
+ },
233053
+ "height": {
233054
+ "type": "integer",
233055
+ "minimum": 1
233056
+ }
233057
+ }
233058
+ }
233059
+ ]
233060
+ },
233061
+ "notBlack": {
233062
+ "type": "boolean",
233063
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
233064
+ }
233065
+ }
233066
+ },
233067
+ "checkpoints": {
233068
+ "title": "Recording checkpoints",
233069
+ "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.",
233070
+ "anyOf": [
233071
+ {
233072
+ "type": "boolean",
233073
+ "title": "Recording checkpoints (boolean)",
233074
+ "description": "If `true`, enables checkpoints with default settings."
233075
+ },
233076
+ {
233077
+ "type": "object",
233078
+ "title": "Recording checkpoints (detailed)",
233079
+ "additionalProperties": false,
233080
+ "properties": {
233081
+ "maxVariation": {
233082
+ "type": "number",
233083
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
233084
+ "default": 0.05,
233085
+ "minimum": 0,
233086
+ "maximum": 1
233087
+ },
233088
+ "directory": {
233089
+ "type": "string",
233090
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
233091
+ "transform": [
233092
+ "trim"
233093
+ ]
233094
+ }
233095
+ }
233096
+ }
233097
+ ]
232100
233098
  }
232101
233099
  },
232102
233100
  "title": "Record (detailed)"
@@ -232358,10 +233356,11 @@
232358
233356
  },
232359
233357
  "overwrite": {
232360
233358
  "type": "string",
232361
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
233359
+ "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 — 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`.",
232362
233360
  "enum": [
232363
233361
  "true",
232364
- "false"
233362
+ "false",
233363
+ "aboveVariation"
232365
233364
  ]
232366
233365
  },
232367
233366
  "name": {
@@ -232419,6 +233418,88 @@
232419
233418
  }
232420
233419
  }
232421
233420
  ]
233421
+ },
233422
+ "verify": {
233423
+ "title": "Recording verify guards",
233424
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
233425
+ "type": "object",
233426
+ "additionalProperties": false,
233427
+ "properties": {
233428
+ "minDuration": {
233429
+ "type": "number",
233430
+ "description": "Fail if the video is shorter than this many seconds.",
233431
+ "minimum": 0
233432
+ },
233433
+ "maxDuration": {
233434
+ "type": "number",
233435
+ "description": "Fail if the video is longer than this many seconds.",
233436
+ "minimum": 0
233437
+ },
233438
+ "resolution": {
233439
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
233440
+ "anyOf": [
233441
+ {
233442
+ "type": "boolean",
233443
+ "title": "Verify resolution (boolean)"
233444
+ },
233445
+ {
233446
+ "type": "object",
233447
+ "title": "Verify resolution (detailed)",
233448
+ "additionalProperties": false,
233449
+ "required": [
233450
+ "width",
233451
+ "height"
233452
+ ],
233453
+ "properties": {
233454
+ "width": {
233455
+ "type": "integer",
233456
+ "minimum": 1
233457
+ },
233458
+ "height": {
233459
+ "type": "integer",
233460
+ "minimum": 1
233461
+ }
233462
+ }
233463
+ }
233464
+ ]
233465
+ },
233466
+ "notBlack": {
233467
+ "type": "boolean",
233468
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
233469
+ }
233470
+ }
233471
+ },
233472
+ "checkpoints": {
233473
+ "title": "Recording checkpoints",
233474
+ "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.",
233475
+ "anyOf": [
233476
+ {
233477
+ "type": "boolean",
233478
+ "title": "Recording checkpoints (boolean)",
233479
+ "description": "If `true`, enables checkpoints with default settings."
233480
+ },
233481
+ {
233482
+ "type": "object",
233483
+ "title": "Recording checkpoints (detailed)",
233484
+ "additionalProperties": false,
233485
+ "properties": {
233486
+ "maxVariation": {
233487
+ "type": "number",
233488
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
233489
+ "default": 0.05,
233490
+ "minimum": 0,
233491
+ "maximum": 1
233492
+ },
233493
+ "directory": {
233494
+ "type": "string",
233495
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
233496
+ "transform": [
233497
+ "trim"
233498
+ ]
233499
+ }
233500
+ }
233501
+ }
233502
+ ]
232422
233503
  }
232423
233504
  },
232424
233505
  "title": "Record (detailed)"
@@ -232485,6 +233566,17 @@
232485
233566
  "path": "results.mp4",
232486
233567
  "engine": "ffmpeg"
232487
233568
  },
233569
+ {
233570
+ "path": "results.mp4",
233571
+ "checkpoints": true
233572
+ },
233573
+ {
233574
+ "path": "results.mp4",
233575
+ "checkpoints": {
233576
+ "maxVariation": 0.02,
233577
+ "directory": "baselines/results"
233578
+ }
233579
+ },
232488
233580
  {
232489
233581
  "path": "results.mp4",
232490
233582
  "engine": {
@@ -272911,9 +274003,10 @@
272911
274003
  "anyOf": [
272912
274004
  {
272913
274005
  "title": "Screenshot (simple)",
274006
+ "$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 — 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.",
272914
274007
  "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.",
272915
274008
  "type": "string",
272916
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274009
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
272917
274010
  "transform": [
272918
274011
  "trim"
272919
274012
  ]
@@ -273147,9 +274240,10 @@
273147
274240
  },
273148
274241
  "path": {
273149
274242
  "title": "Screenshot (simple)",
274243
+ "$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 — 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.",
273150
274244
  "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.",
273151
274245
  "type": "string",
273152
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274246
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273153
274247
  "transform": [
273154
274248
  "trim"
273155
274249
  ]
@@ -273407,9 +274501,10 @@
273407
274501
  "schemas": {
273408
274502
  "path": {
273409
274503
  "title": "Screenshot (simple)",
274504
+ "$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 — 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.",
273410
274505
  "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.",
273411
274506
  "type": "string",
273412
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274507
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273413
274508
  "transform": [
273414
274509
  "trim"
273415
274510
  ]
@@ -273643,9 +274738,10 @@
273643
274738
  },
273644
274739
  "path": {
273645
274740
  "title": "Screenshot (simple)",
274741
+ "$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 — 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.",
273646
274742
  "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.",
273647
274743
  "type": "string",
273648
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274744
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
273649
274745
  "transform": [
273650
274746
  "trim"
273651
274747
  ]
@@ -274119,9 +275215,10 @@
274119
275215
  },
274120
275216
  "path": {
274121
275217
  "title": "Screenshot (simple)",
275218
+ "$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 — 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.",
274122
275219
  "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.",
274123
275220
  "type": "string",
274124
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
275221
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
274125
275222
  "transform": [
274126
275223
  "trim"
274127
275224
  ]
@@ -274503,6 +275600,7 @@
274503
275600
  "image.png",
274504
275601
  "static/images/image.png",
274505
275602
  "/User/manny/projects/doc-detective/static/images/image.png",
275603
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
274506
275604
  "https://example.com/static/images/image.png",
274507
275605
  {
274508
275606
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -276937,10 +278035,11 @@
276937
278035
  },
276938
278036
  "overwrite": {
276939
278037
  "type": "string",
276940
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
278038
+ "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 — 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`.",
276941
278039
  "enum": [
276942
278040
  "true",
276943
- "false"
278041
+ "false",
278042
+ "aboveVariation"
276944
278043
  ]
276945
278044
  },
276946
278045
  "name": {
@@ -276998,6 +278097,88 @@
276998
278097
  }
276999
278098
  }
277000
278099
  ]
278100
+ },
278101
+ "verify": {
278102
+ "title": "Recording verify guards",
278103
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
278104
+ "type": "object",
278105
+ "additionalProperties": false,
278106
+ "properties": {
278107
+ "minDuration": {
278108
+ "type": "number",
278109
+ "description": "Fail if the video is shorter than this many seconds.",
278110
+ "minimum": 0
278111
+ },
278112
+ "maxDuration": {
278113
+ "type": "number",
278114
+ "description": "Fail if the video is longer than this many seconds.",
278115
+ "minimum": 0
278116
+ },
278117
+ "resolution": {
278118
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
278119
+ "anyOf": [
278120
+ {
278121
+ "type": "boolean",
278122
+ "title": "Verify resolution (boolean)"
278123
+ },
278124
+ {
278125
+ "type": "object",
278126
+ "title": "Verify resolution (detailed)",
278127
+ "additionalProperties": false,
278128
+ "required": [
278129
+ "width",
278130
+ "height"
278131
+ ],
278132
+ "properties": {
278133
+ "width": {
278134
+ "type": "integer",
278135
+ "minimum": 1
278136
+ },
278137
+ "height": {
278138
+ "type": "integer",
278139
+ "minimum": 1
278140
+ }
278141
+ }
278142
+ }
278143
+ ]
278144
+ },
278145
+ "notBlack": {
278146
+ "type": "boolean",
278147
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
278148
+ }
278149
+ }
278150
+ },
278151
+ "checkpoints": {
278152
+ "title": "Recording checkpoints",
278153
+ "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.",
278154
+ "anyOf": [
278155
+ {
278156
+ "type": "boolean",
278157
+ "title": "Recording checkpoints (boolean)",
278158
+ "description": "If `true`, enables checkpoints with default settings."
278159
+ },
278160
+ {
278161
+ "type": "object",
278162
+ "title": "Recording checkpoints (detailed)",
278163
+ "additionalProperties": false,
278164
+ "properties": {
278165
+ "maxVariation": {
278166
+ "type": "number",
278167
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
278168
+ "default": 0.05,
278169
+ "minimum": 0,
278170
+ "maximum": 1
278171
+ },
278172
+ "directory": {
278173
+ "type": "string",
278174
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
278175
+ "transform": [
278176
+ "trim"
278177
+ ]
278178
+ }
278179
+ }
278180
+ }
278181
+ ]
277001
278182
  }
277002
278183
  },
277003
278184
  "title": "Record (detailed)"
@@ -277259,10 +278440,11 @@
277259
278440
  },
277260
278441
  "overwrite": {
277261
278442
  "type": "string",
277262
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
278443
+ "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 — 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`.",
277263
278444
  "enum": [
277264
278445
  "true",
277265
- "false"
278446
+ "false",
278447
+ "aboveVariation"
277266
278448
  ]
277267
278449
  },
277268
278450
  "name": {
@@ -277320,6 +278502,88 @@
277320
278502
  }
277321
278503
  }
277322
278504
  ]
278505
+ },
278506
+ "verify": {
278507
+ "title": "Recording verify guards",
278508
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
278509
+ "type": "object",
278510
+ "additionalProperties": false,
278511
+ "properties": {
278512
+ "minDuration": {
278513
+ "type": "number",
278514
+ "description": "Fail if the video is shorter than this many seconds.",
278515
+ "minimum": 0
278516
+ },
278517
+ "maxDuration": {
278518
+ "type": "number",
278519
+ "description": "Fail if the video is longer than this many seconds.",
278520
+ "minimum": 0
278521
+ },
278522
+ "resolution": {
278523
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
278524
+ "anyOf": [
278525
+ {
278526
+ "type": "boolean",
278527
+ "title": "Verify resolution (boolean)"
278528
+ },
278529
+ {
278530
+ "type": "object",
278531
+ "title": "Verify resolution (detailed)",
278532
+ "additionalProperties": false,
278533
+ "required": [
278534
+ "width",
278535
+ "height"
278536
+ ],
278537
+ "properties": {
278538
+ "width": {
278539
+ "type": "integer",
278540
+ "minimum": 1
278541
+ },
278542
+ "height": {
278543
+ "type": "integer",
278544
+ "minimum": 1
278545
+ }
278546
+ }
278547
+ }
278548
+ ]
278549
+ },
278550
+ "notBlack": {
278551
+ "type": "boolean",
278552
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
278553
+ }
278554
+ }
278555
+ },
278556
+ "checkpoints": {
278557
+ "title": "Recording checkpoints",
278558
+ "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.",
278559
+ "anyOf": [
278560
+ {
278561
+ "type": "boolean",
278562
+ "title": "Recording checkpoints (boolean)",
278563
+ "description": "If `true`, enables checkpoints with default settings."
278564
+ },
278565
+ {
278566
+ "type": "object",
278567
+ "title": "Recording checkpoints (detailed)",
278568
+ "additionalProperties": false,
278569
+ "properties": {
278570
+ "maxVariation": {
278571
+ "type": "number",
278572
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
278573
+ "default": 0.05,
278574
+ "minimum": 0,
278575
+ "maximum": 1
278576
+ },
278577
+ "directory": {
278578
+ "type": "string",
278579
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
278580
+ "transform": [
278581
+ "trim"
278582
+ ]
278583
+ }
278584
+ }
278585
+ }
278586
+ ]
277323
278587
  }
277324
278588
  },
277325
278589
  "title": "Record (detailed)"
@@ -277386,6 +278650,17 @@
277386
278650
  "path": "results.mp4",
277387
278651
  "engine": "ffmpeg"
277388
278652
  },
278653
+ {
278654
+ "path": "results.mp4",
278655
+ "checkpoints": true
278656
+ },
278657
+ {
278658
+ "path": "results.mp4",
278659
+ "checkpoints": {
278660
+ "maxVariation": 0.02,
278661
+ "directory": "baselines/results"
278662
+ }
278663
+ },
277389
278664
  {
277390
278665
  "path": "results.mp4",
277391
278666
  "engine": {
@@ -320773,9 +322048,10 @@
320773
322048
  "anyOf": [
320774
322049
  {
320775
322050
  "title": "Screenshot (simple)",
322051
+ "$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 — 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.",
320776
322052
  "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.",
320777
322053
  "type": "string",
320778
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322054
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
320779
322055
  "transform": [
320780
322056
  "trim"
320781
322057
  ]
@@ -321009,9 +322285,10 @@
321009
322285
  },
321010
322286
  "path": {
321011
322287
  "title": "Screenshot (simple)",
322288
+ "$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 — 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.",
321012
322289
  "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.",
321013
322290
  "type": "string",
321014
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322291
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321015
322292
  "transform": [
321016
322293
  "trim"
321017
322294
  ]
@@ -321269,9 +322546,10 @@
321269
322546
  "schemas": {
321270
322547
  "path": {
321271
322548
  "title": "Screenshot (simple)",
322549
+ "$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 — 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.",
321272
322550
  "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.",
321273
322551
  "type": "string",
321274
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322552
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321275
322553
  "transform": [
321276
322554
  "trim"
321277
322555
  ]
@@ -321505,9 +322783,10 @@
321505
322783
  },
321506
322784
  "path": {
321507
322785
  "title": "Screenshot (simple)",
322786
+ "$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 — 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.",
321508
322787
  "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.",
321509
322788
  "type": "string",
321510
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
322789
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321511
322790
  "transform": [
321512
322791
  "trim"
321513
322792
  ]
@@ -321981,9 +323260,10 @@
321981
323260
  },
321982
323261
  "path": {
321983
323262
  "title": "Screenshot (simple)",
323263
+ "$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 — 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.",
321984
323264
  "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.",
321985
323265
  "type": "string",
321986
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
323266
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
321987
323267
  "transform": [
321988
323268
  "trim"
321989
323269
  ]
@@ -322365,6 +323645,7 @@
322365
323645
  "image.png",
322366
323646
  "static/images/image.png",
322367
323647
  "/User/manny/projects/doc-detective/static/images/image.png",
323648
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
322368
323649
  "https://example.com/static/images/image.png",
322369
323650
  {
322370
323651
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -324799,10 +326080,11 @@
324799
326080
  },
324800
326081
  "overwrite": {
324801
326082
  "type": "string",
324802
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
326083
+ "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 — 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`.",
324803
326084
  "enum": [
324804
326085
  "true",
324805
- "false"
326086
+ "false",
326087
+ "aboveVariation"
324806
326088
  ]
324807
326089
  },
324808
326090
  "name": {
@@ -324860,6 +326142,88 @@
324860
326142
  }
324861
326143
  }
324862
326144
  ]
326145
+ },
326146
+ "verify": {
326147
+ "title": "Recording verify guards",
326148
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
326149
+ "type": "object",
326150
+ "additionalProperties": false,
326151
+ "properties": {
326152
+ "minDuration": {
326153
+ "type": "number",
326154
+ "description": "Fail if the video is shorter than this many seconds.",
326155
+ "minimum": 0
326156
+ },
326157
+ "maxDuration": {
326158
+ "type": "number",
326159
+ "description": "Fail if the video is longer than this many seconds.",
326160
+ "minimum": 0
326161
+ },
326162
+ "resolution": {
326163
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
326164
+ "anyOf": [
326165
+ {
326166
+ "type": "boolean",
326167
+ "title": "Verify resolution (boolean)"
326168
+ },
326169
+ {
326170
+ "type": "object",
326171
+ "title": "Verify resolution (detailed)",
326172
+ "additionalProperties": false,
326173
+ "required": [
326174
+ "width",
326175
+ "height"
326176
+ ],
326177
+ "properties": {
326178
+ "width": {
326179
+ "type": "integer",
326180
+ "minimum": 1
326181
+ },
326182
+ "height": {
326183
+ "type": "integer",
326184
+ "minimum": 1
326185
+ }
326186
+ }
326187
+ }
326188
+ ]
326189
+ },
326190
+ "notBlack": {
326191
+ "type": "boolean",
326192
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
326193
+ }
326194
+ }
326195
+ },
326196
+ "checkpoints": {
326197
+ "title": "Recording checkpoints",
326198
+ "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.",
326199
+ "anyOf": [
326200
+ {
326201
+ "type": "boolean",
326202
+ "title": "Recording checkpoints (boolean)",
326203
+ "description": "If `true`, enables checkpoints with default settings."
326204
+ },
326205
+ {
326206
+ "type": "object",
326207
+ "title": "Recording checkpoints (detailed)",
326208
+ "additionalProperties": false,
326209
+ "properties": {
326210
+ "maxVariation": {
326211
+ "type": "number",
326212
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
326213
+ "default": 0.05,
326214
+ "minimum": 0,
326215
+ "maximum": 1
326216
+ },
326217
+ "directory": {
326218
+ "type": "string",
326219
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
326220
+ "transform": [
326221
+ "trim"
326222
+ ]
326223
+ }
326224
+ }
326225
+ }
326226
+ ]
324863
326227
  }
324864
326228
  },
324865
326229
  "title": "Record (detailed)"
@@ -325121,10 +326485,11 @@
325121
326485
  },
325122
326486
  "overwrite": {
325123
326487
  "type": "string",
325124
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
326488
+ "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 — 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`.",
325125
326489
  "enum": [
325126
326490
  "true",
325127
- "false"
326491
+ "false",
326492
+ "aboveVariation"
325128
326493
  ]
325129
326494
  },
325130
326495
  "name": {
@@ -325182,6 +326547,88 @@
325182
326547
  }
325183
326548
  }
325184
326549
  ]
326550
+ },
326551
+ "verify": {
326552
+ "title": "Recording verify guards",
326553
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
326554
+ "type": "object",
326555
+ "additionalProperties": false,
326556
+ "properties": {
326557
+ "minDuration": {
326558
+ "type": "number",
326559
+ "description": "Fail if the video is shorter than this many seconds.",
326560
+ "minimum": 0
326561
+ },
326562
+ "maxDuration": {
326563
+ "type": "number",
326564
+ "description": "Fail if the video is longer than this many seconds.",
326565
+ "minimum": 0
326566
+ },
326567
+ "resolution": {
326568
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
326569
+ "anyOf": [
326570
+ {
326571
+ "type": "boolean",
326572
+ "title": "Verify resolution (boolean)"
326573
+ },
326574
+ {
326575
+ "type": "object",
326576
+ "title": "Verify resolution (detailed)",
326577
+ "additionalProperties": false,
326578
+ "required": [
326579
+ "width",
326580
+ "height"
326581
+ ],
326582
+ "properties": {
326583
+ "width": {
326584
+ "type": "integer",
326585
+ "minimum": 1
326586
+ },
326587
+ "height": {
326588
+ "type": "integer",
326589
+ "minimum": 1
326590
+ }
326591
+ }
326592
+ }
326593
+ ]
326594
+ },
326595
+ "notBlack": {
326596
+ "type": "boolean",
326597
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
326598
+ }
326599
+ }
326600
+ },
326601
+ "checkpoints": {
326602
+ "title": "Recording checkpoints",
326603
+ "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.",
326604
+ "anyOf": [
326605
+ {
326606
+ "type": "boolean",
326607
+ "title": "Recording checkpoints (boolean)",
326608
+ "description": "If `true`, enables checkpoints with default settings."
326609
+ },
326610
+ {
326611
+ "type": "object",
326612
+ "title": "Recording checkpoints (detailed)",
326613
+ "additionalProperties": false,
326614
+ "properties": {
326615
+ "maxVariation": {
326616
+ "type": "number",
326617
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
326618
+ "default": 0.05,
326619
+ "minimum": 0,
326620
+ "maximum": 1
326621
+ },
326622
+ "directory": {
326623
+ "type": "string",
326624
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
326625
+ "transform": [
326626
+ "trim"
326627
+ ]
326628
+ }
326629
+ }
326630
+ }
326631
+ ]
325185
326632
  }
325186
326633
  },
325187
326634
  "title": "Record (detailed)"
@@ -325248,6 +326695,17 @@
325248
326695
  "path": "results.mp4",
325249
326696
  "engine": "ffmpeg"
325250
326697
  },
326698
+ {
326699
+ "path": "results.mp4",
326700
+ "checkpoints": true
326701
+ },
326702
+ {
326703
+ "path": "results.mp4",
326704
+ "checkpoints": {
326705
+ "maxVariation": 0.02,
326706
+ "directory": "baselines/results"
326707
+ }
326708
+ },
325251
326709
  {
325252
326710
  "path": "results.mp4",
325253
326711
  "engine": {
@@ -365674,9 +367132,10 @@
365674
367132
  "anyOf": [
365675
367133
  {
365676
367134
  "title": "Screenshot (simple)",
367135
+ "$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 — 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.",
365677
367136
  "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.",
365678
367137
  "type": "string",
365679
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367138
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
365680
367139
  "transform": [
365681
367140
  "trim"
365682
367141
  ]
@@ -365910,9 +367369,10 @@
365910
367369
  },
365911
367370
  "path": {
365912
367371
  "title": "Screenshot (simple)",
367372
+ "$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 — 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.",
365913
367373
  "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.",
365914
367374
  "type": "string",
365915
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367375
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
365916
367376
  "transform": [
365917
367377
  "trim"
365918
367378
  ]
@@ -366170,9 +367630,10 @@
366170
367630
  "schemas": {
366171
367631
  "path": {
366172
367632
  "title": "Screenshot (simple)",
367633
+ "$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 — 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.",
366173
367634
  "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.",
366174
367635
  "type": "string",
366175
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367636
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366176
367637
  "transform": [
366177
367638
  "trim"
366178
367639
  ]
@@ -366406,9 +367867,10 @@
366406
367867
  },
366407
367868
  "path": {
366408
367869
  "title": "Screenshot (simple)",
367870
+ "$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 — 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.",
366409
367871
  "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.",
366410
367872
  "type": "string",
366411
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
367873
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366412
367874
  "transform": [
366413
367875
  "trim"
366414
367876
  ]
@@ -366882,9 +368344,10 @@
366882
368344
  },
366883
368345
  "path": {
366884
368346
  "title": "Screenshot (simple)",
368347
+ "$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 — 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.",
366885
368348
  "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.",
366886
368349
  "type": "string",
366887
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
368350
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
366888
368351
  "transform": [
366889
368352
  "trim"
366890
368353
  ]
@@ -367266,6 +368729,7 @@
367266
368729
  "image.png",
367267
368730
  "static/images/image.png",
367268
368731
  "/User/manny/projects/doc-detective/static/images/image.png",
368732
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
367269
368733
  "https://example.com/static/images/image.png",
367270
368734
  {
367271
368735
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -369700,10 +371164,11 @@
369700
371164
  },
369701
371165
  "overwrite": {
369702
371166
  "type": "string",
369703
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
371167
+ "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 — 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`.",
369704
371168
  "enum": [
369705
371169
  "true",
369706
- "false"
371170
+ "false",
371171
+ "aboveVariation"
369707
371172
  ]
369708
371173
  },
369709
371174
  "name": {
@@ -369761,6 +371226,88 @@
369761
371226
  }
369762
371227
  }
369763
371228
  ]
371229
+ },
371230
+ "verify": {
371231
+ "title": "Recording verify guards",
371232
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
371233
+ "type": "object",
371234
+ "additionalProperties": false,
371235
+ "properties": {
371236
+ "minDuration": {
371237
+ "type": "number",
371238
+ "description": "Fail if the video is shorter than this many seconds.",
371239
+ "minimum": 0
371240
+ },
371241
+ "maxDuration": {
371242
+ "type": "number",
371243
+ "description": "Fail if the video is longer than this many seconds.",
371244
+ "minimum": 0
371245
+ },
371246
+ "resolution": {
371247
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
371248
+ "anyOf": [
371249
+ {
371250
+ "type": "boolean",
371251
+ "title": "Verify resolution (boolean)"
371252
+ },
371253
+ {
371254
+ "type": "object",
371255
+ "title": "Verify resolution (detailed)",
371256
+ "additionalProperties": false,
371257
+ "required": [
371258
+ "width",
371259
+ "height"
371260
+ ],
371261
+ "properties": {
371262
+ "width": {
371263
+ "type": "integer",
371264
+ "minimum": 1
371265
+ },
371266
+ "height": {
371267
+ "type": "integer",
371268
+ "minimum": 1
371269
+ }
371270
+ }
371271
+ }
371272
+ ]
371273
+ },
371274
+ "notBlack": {
371275
+ "type": "boolean",
371276
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
371277
+ }
371278
+ }
371279
+ },
371280
+ "checkpoints": {
371281
+ "title": "Recording checkpoints",
371282
+ "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.",
371283
+ "anyOf": [
371284
+ {
371285
+ "type": "boolean",
371286
+ "title": "Recording checkpoints (boolean)",
371287
+ "description": "If `true`, enables checkpoints with default settings."
371288
+ },
371289
+ {
371290
+ "type": "object",
371291
+ "title": "Recording checkpoints (detailed)",
371292
+ "additionalProperties": false,
371293
+ "properties": {
371294
+ "maxVariation": {
371295
+ "type": "number",
371296
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
371297
+ "default": 0.05,
371298
+ "minimum": 0,
371299
+ "maximum": 1
371300
+ },
371301
+ "directory": {
371302
+ "type": "string",
371303
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
371304
+ "transform": [
371305
+ "trim"
371306
+ ]
371307
+ }
371308
+ }
371309
+ }
371310
+ ]
369764
371311
  }
369765
371312
  },
369766
371313
  "title": "Record (detailed)"
@@ -370022,10 +371569,11 @@
370022
371569
  },
370023
371570
  "overwrite": {
370024
371571
  "type": "string",
370025
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
371572
+ "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 — 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`.",
370026
371573
  "enum": [
370027
371574
  "true",
370028
- "false"
371575
+ "false",
371576
+ "aboveVariation"
370029
371577
  ]
370030
371578
  },
370031
371579
  "name": {
@@ -370083,6 +371631,88 @@
370083
371631
  }
370084
371632
  }
370085
371633
  ]
371634
+ },
371635
+ "verify": {
371636
+ "title": "Recording verify guards",
371637
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
371638
+ "type": "object",
371639
+ "additionalProperties": false,
371640
+ "properties": {
371641
+ "minDuration": {
371642
+ "type": "number",
371643
+ "description": "Fail if the video is shorter than this many seconds.",
371644
+ "minimum": 0
371645
+ },
371646
+ "maxDuration": {
371647
+ "type": "number",
371648
+ "description": "Fail if the video is longer than this many seconds.",
371649
+ "minimum": 0
371650
+ },
371651
+ "resolution": {
371652
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
371653
+ "anyOf": [
371654
+ {
371655
+ "type": "boolean",
371656
+ "title": "Verify resolution (boolean)"
371657
+ },
371658
+ {
371659
+ "type": "object",
371660
+ "title": "Verify resolution (detailed)",
371661
+ "additionalProperties": false,
371662
+ "required": [
371663
+ "width",
371664
+ "height"
371665
+ ],
371666
+ "properties": {
371667
+ "width": {
371668
+ "type": "integer",
371669
+ "minimum": 1
371670
+ },
371671
+ "height": {
371672
+ "type": "integer",
371673
+ "minimum": 1
371674
+ }
371675
+ }
371676
+ }
371677
+ ]
371678
+ },
371679
+ "notBlack": {
371680
+ "type": "boolean",
371681
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
371682
+ }
371683
+ }
371684
+ },
371685
+ "checkpoints": {
371686
+ "title": "Recording checkpoints",
371687
+ "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.",
371688
+ "anyOf": [
371689
+ {
371690
+ "type": "boolean",
371691
+ "title": "Recording checkpoints (boolean)",
371692
+ "description": "If `true`, enables checkpoints with default settings."
371693
+ },
371694
+ {
371695
+ "type": "object",
371696
+ "title": "Recording checkpoints (detailed)",
371697
+ "additionalProperties": false,
371698
+ "properties": {
371699
+ "maxVariation": {
371700
+ "type": "number",
371701
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
371702
+ "default": 0.05,
371703
+ "minimum": 0,
371704
+ "maximum": 1
371705
+ },
371706
+ "directory": {
371707
+ "type": "string",
371708
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
371709
+ "transform": [
371710
+ "trim"
371711
+ ]
371712
+ }
371713
+ }
371714
+ }
371715
+ ]
370086
371716
  }
370087
371717
  },
370088
371718
  "title": "Record (detailed)"
@@ -370149,6 +371779,17 @@
370149
371779
  "path": "results.mp4",
370150
371780
  "engine": "ffmpeg"
370151
371781
  },
371782
+ {
371783
+ "path": "results.mp4",
371784
+ "checkpoints": true
371785
+ },
371786
+ {
371787
+ "path": "results.mp4",
371788
+ "checkpoints": {
371789
+ "maxVariation": 0.02,
371790
+ "directory": "baselines/results"
371791
+ }
371792
+ },
370152
371793
  {
370153
371794
  "path": "results.mp4",
370154
371795
  "engine": {
@@ -385685,9 +387326,10 @@
385685
387326
  "anyOf": [
385686
387327
  {
385687
387328
  "title": "Screenshot (simple)",
387329
+ "$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 — 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.",
385688
387330
  "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.",
385689
387331
  "type": "string",
385690
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
387332
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
385691
387333
  "transform": [
385692
387334
  "trim"
385693
387335
  ]
@@ -385921,9 +387563,10 @@
385921
387563
  },
385922
387564
  "path": {
385923
387565
  "title": "Screenshot (simple)",
387566
+ "$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 — 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.",
385924
387567
  "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.",
385925
387568
  "type": "string",
385926
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
387569
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
385927
387570
  "transform": [
385928
387571
  "trim"
385929
387572
  ]
@@ -386181,9 +387824,10 @@
386181
387824
  "schemas": {
386182
387825
  "path": {
386183
387826
  "title": "Screenshot (simple)",
387827
+ "$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 — 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.",
386184
387828
  "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.",
386185
387829
  "type": "string",
386186
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
387830
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386187
387831
  "transform": [
386188
387832
  "trim"
386189
387833
  ]
@@ -386417,9 +388061,10 @@
386417
388061
  },
386418
388062
  "path": {
386419
388063
  "title": "Screenshot (simple)",
388064
+ "$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 — 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.",
386420
388065
  "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.",
386421
388066
  "type": "string",
386422
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
388067
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386423
388068
  "transform": [
386424
388069
  "trim"
386425
388070
  ]
@@ -386893,9 +388538,10 @@
386893
388538
  },
386894
388539
  "path": {
386895
388540
  "title": "Screenshot (simple)",
388541
+ "$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 — 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.",
386896
388542
  "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.",
386897
388543
  "type": "string",
386898
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
388544
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
386899
388545
  "transform": [
386900
388546
  "trim"
386901
388547
  ]
@@ -387277,6 +388923,7 @@
387277
388923
  "image.png",
387278
388924
  "static/images/image.png",
387279
388925
  "/User/manny/projects/doc-detective/static/images/image.png",
388926
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
387280
388927
  "https://example.com/static/images/image.png",
387281
388928
  {
387282
388929
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -416860,9 +418507,10 @@
416860
418507
  "anyOf": [
416861
418508
  {
416862
418509
  "title": "Screenshot (simple)",
418510
+ "$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 — 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.",
416863
418511
  "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.",
416864
418512
  "type": "string",
416865
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418513
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
416866
418514
  "transform": [
416867
418515
  "trim"
416868
418516
  ]
@@ -417096,9 +418744,10 @@
417096
418744
  },
417097
418745
  "path": {
417098
418746
  "title": "Screenshot (simple)",
418747
+ "$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 — 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.",
417099
418748
  "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.",
417100
418749
  "type": "string",
417101
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418750
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417102
418751
  "transform": [
417103
418752
  "trim"
417104
418753
  ]
@@ -417356,9 +419005,10 @@
417356
419005
  "schemas": {
417357
419006
  "path": {
417358
419007
  "title": "Screenshot (simple)",
419008
+ "$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 — 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.",
417359
419009
  "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.",
417360
419010
  "type": "string",
417361
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
419011
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417362
419012
  "transform": [
417363
419013
  "trim"
417364
419014
  ]
@@ -417592,9 +419242,10 @@
417592
419242
  },
417593
419243
  "path": {
417594
419244
  "title": "Screenshot (simple)",
419245
+ "$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 — 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.",
417595
419246
  "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.",
417596
419247
  "type": "string",
417597
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
419248
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
417598
419249
  "transform": [
417599
419250
  "trim"
417600
419251
  ]
@@ -418068,9 +419719,10 @@
418068
419719
  },
418069
419720
  "path": {
418070
419721
  "title": "Screenshot (simple)",
419722
+ "$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 — 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.",
418071
419723
  "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.",
418072
419724
  "type": "string",
418073
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
419725
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
418074
419726
  "transform": [
418075
419727
  "trim"
418076
419728
  ]
@@ -418452,6 +420104,7 @@
418452
420104
  "image.png",
418453
420105
  "static/images/image.png",
418454
420106
  "/User/manny/projects/doc-detective/static/images/image.png",
420107
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
418455
420108
  "https://example.com/static/images/image.png",
418456
420109
  {
418457
420110
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -420886,10 +422539,11 @@
420886
422539
  },
420887
422540
  "overwrite": {
420888
422541
  "type": "string",
420889
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
422542
+ "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 — 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`.",
420890
422543
  "enum": [
420891
422544
  "true",
420892
- "false"
422545
+ "false",
422546
+ "aboveVariation"
420893
422547
  ]
420894
422548
  },
420895
422549
  "name": {
@@ -420947,6 +422601,88 @@
420947
422601
  }
420948
422602
  }
420949
422603
  ]
422604
+ },
422605
+ "verify": {
422606
+ "title": "Recording verify guards",
422607
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
422608
+ "type": "object",
422609
+ "additionalProperties": false,
422610
+ "properties": {
422611
+ "minDuration": {
422612
+ "type": "number",
422613
+ "description": "Fail if the video is shorter than this many seconds.",
422614
+ "minimum": 0
422615
+ },
422616
+ "maxDuration": {
422617
+ "type": "number",
422618
+ "description": "Fail if the video is longer than this many seconds.",
422619
+ "minimum": 0
422620
+ },
422621
+ "resolution": {
422622
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
422623
+ "anyOf": [
422624
+ {
422625
+ "type": "boolean",
422626
+ "title": "Verify resolution (boolean)"
422627
+ },
422628
+ {
422629
+ "type": "object",
422630
+ "title": "Verify resolution (detailed)",
422631
+ "additionalProperties": false,
422632
+ "required": [
422633
+ "width",
422634
+ "height"
422635
+ ],
422636
+ "properties": {
422637
+ "width": {
422638
+ "type": "integer",
422639
+ "minimum": 1
422640
+ },
422641
+ "height": {
422642
+ "type": "integer",
422643
+ "minimum": 1
422644
+ }
422645
+ }
422646
+ }
422647
+ ]
422648
+ },
422649
+ "notBlack": {
422650
+ "type": "boolean",
422651
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
422652
+ }
422653
+ }
422654
+ },
422655
+ "checkpoints": {
422656
+ "title": "Recording checkpoints",
422657
+ "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.",
422658
+ "anyOf": [
422659
+ {
422660
+ "type": "boolean",
422661
+ "title": "Recording checkpoints (boolean)",
422662
+ "description": "If `true`, enables checkpoints with default settings."
422663
+ },
422664
+ {
422665
+ "type": "object",
422666
+ "title": "Recording checkpoints (detailed)",
422667
+ "additionalProperties": false,
422668
+ "properties": {
422669
+ "maxVariation": {
422670
+ "type": "number",
422671
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
422672
+ "default": 0.05,
422673
+ "minimum": 0,
422674
+ "maximum": 1
422675
+ },
422676
+ "directory": {
422677
+ "type": "string",
422678
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
422679
+ "transform": [
422680
+ "trim"
422681
+ ]
422682
+ }
422683
+ }
422684
+ }
422685
+ ]
420950
422686
  }
420951
422687
  },
420952
422688
  "title": "Record (detailed)"
@@ -421208,10 +422944,11 @@
421208
422944
  },
421209
422945
  "overwrite": {
421210
422946
  "type": "string",
421211
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
422947
+ "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 — 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`.",
421212
422948
  "enum": [
421213
422949
  "true",
421214
- "false"
422950
+ "false",
422951
+ "aboveVariation"
421215
422952
  ]
421216
422953
  },
421217
422954
  "name": {
@@ -421269,6 +423006,88 @@
421269
423006
  }
421270
423007
  }
421271
423008
  ]
423009
+ },
423010
+ "verify": {
423011
+ "title": "Recording verify guards",
423012
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
423013
+ "type": "object",
423014
+ "additionalProperties": false,
423015
+ "properties": {
423016
+ "minDuration": {
423017
+ "type": "number",
423018
+ "description": "Fail if the video is shorter than this many seconds.",
423019
+ "minimum": 0
423020
+ },
423021
+ "maxDuration": {
423022
+ "type": "number",
423023
+ "description": "Fail if the video is longer than this many seconds.",
423024
+ "minimum": 0
423025
+ },
423026
+ "resolution": {
423027
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
423028
+ "anyOf": [
423029
+ {
423030
+ "type": "boolean",
423031
+ "title": "Verify resolution (boolean)"
423032
+ },
423033
+ {
423034
+ "type": "object",
423035
+ "title": "Verify resolution (detailed)",
423036
+ "additionalProperties": false,
423037
+ "required": [
423038
+ "width",
423039
+ "height"
423040
+ ],
423041
+ "properties": {
423042
+ "width": {
423043
+ "type": "integer",
423044
+ "minimum": 1
423045
+ },
423046
+ "height": {
423047
+ "type": "integer",
423048
+ "minimum": 1
423049
+ }
423050
+ }
423051
+ }
423052
+ ]
423053
+ },
423054
+ "notBlack": {
423055
+ "type": "boolean",
423056
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
423057
+ }
423058
+ }
423059
+ },
423060
+ "checkpoints": {
423061
+ "title": "Recording checkpoints",
423062
+ "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.",
423063
+ "anyOf": [
423064
+ {
423065
+ "type": "boolean",
423066
+ "title": "Recording checkpoints (boolean)",
423067
+ "description": "If `true`, enables checkpoints with default settings."
423068
+ },
423069
+ {
423070
+ "type": "object",
423071
+ "title": "Recording checkpoints (detailed)",
423072
+ "additionalProperties": false,
423073
+ "properties": {
423074
+ "maxVariation": {
423075
+ "type": "number",
423076
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
423077
+ "default": 0.05,
423078
+ "minimum": 0,
423079
+ "maximum": 1
423080
+ },
423081
+ "directory": {
423082
+ "type": "string",
423083
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
423084
+ "transform": [
423085
+ "trim"
423086
+ ]
423087
+ }
423088
+ }
423089
+ }
423090
+ ]
421272
423091
  }
421273
423092
  },
421274
423093
  "title": "Record (detailed)"
@@ -421335,6 +423154,17 @@
421335
423154
  "path": "results.mp4",
421336
423155
  "engine": "ffmpeg"
421337
423156
  },
423157
+ {
423158
+ "path": "results.mp4",
423159
+ "checkpoints": true
423160
+ },
423161
+ {
423162
+ "path": "results.mp4",
423163
+ "checkpoints": {
423164
+ "maxVariation": 0.02,
423165
+ "directory": "baselines/results"
423166
+ }
423167
+ },
421338
423168
  {
421339
423169
  "path": "results.mp4",
421340
423170
  "engine": {
@@ -461761,9 +463591,10 @@
461761
463591
  "anyOf": [
461762
463592
  {
461763
463593
  "title": "Screenshot (simple)",
463594
+ "$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 — 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.",
461764
463595
  "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.",
461765
463596
  "type": "string",
461766
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463597
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
461767
463598
  "transform": [
461768
463599
  "trim"
461769
463600
  ]
@@ -461997,9 +463828,10 @@
461997
463828
  },
461998
463829
  "path": {
461999
463830
  "title": "Screenshot (simple)",
463831
+ "$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 — 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.",
462000
463832
  "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.",
462001
463833
  "type": "string",
462002
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
463834
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462003
463835
  "transform": [
462004
463836
  "trim"
462005
463837
  ]
@@ -462257,9 +464089,10 @@
462257
464089
  "schemas": {
462258
464090
  "path": {
462259
464091
  "title": "Screenshot (simple)",
464092
+ "$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 — 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.",
462260
464093
  "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.",
462261
464094
  "type": "string",
462262
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
464095
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462263
464096
  "transform": [
462264
464097
  "trim"
462265
464098
  ]
@@ -462493,9 +464326,10 @@
462493
464326
  },
462494
464327
  "path": {
462495
464328
  "title": "Screenshot (simple)",
464329
+ "$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 — 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.",
462496
464330
  "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.",
462497
464331
  "type": "string",
462498
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
464332
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462499
464333
  "transform": [
462500
464334
  "trim"
462501
464335
  ]
@@ -462969,9 +464803,10 @@
462969
464803
  },
462970
464804
  "path": {
462971
464805
  "title": "Screenshot (simple)",
464806
+ "$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 — 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.",
462972
464807
  "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.",
462973
464808
  "type": "string",
462974
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
464809
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
462975
464810
  "transform": [
462976
464811
  "trim"
462977
464812
  ]
@@ -463353,6 +465188,7 @@
463353
465188
  "image.png",
463354
465189
  "static/images/image.png",
463355
465190
  "/User/manny/projects/doc-detective/static/images/image.png",
465191
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
463356
465192
  "https://example.com/static/images/image.png",
463357
465193
  {
463358
465194
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -465787,10 +467623,11 @@
465787
467623
  },
465788
467624
  "overwrite": {
465789
467625
  "type": "string",
465790
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
467626
+ "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 — 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`.",
465791
467627
  "enum": [
465792
467628
  "true",
465793
- "false"
467629
+ "false",
467630
+ "aboveVariation"
465794
467631
  ]
465795
467632
  },
465796
467633
  "name": {
@@ -465848,6 +467685,88 @@
465848
467685
  }
465849
467686
  }
465850
467687
  ]
467688
+ },
467689
+ "verify": {
467690
+ "title": "Recording verify guards",
467691
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
467692
+ "type": "object",
467693
+ "additionalProperties": false,
467694
+ "properties": {
467695
+ "minDuration": {
467696
+ "type": "number",
467697
+ "description": "Fail if the video is shorter than this many seconds.",
467698
+ "minimum": 0
467699
+ },
467700
+ "maxDuration": {
467701
+ "type": "number",
467702
+ "description": "Fail if the video is longer than this many seconds.",
467703
+ "minimum": 0
467704
+ },
467705
+ "resolution": {
467706
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
467707
+ "anyOf": [
467708
+ {
467709
+ "type": "boolean",
467710
+ "title": "Verify resolution (boolean)"
467711
+ },
467712
+ {
467713
+ "type": "object",
467714
+ "title": "Verify resolution (detailed)",
467715
+ "additionalProperties": false,
467716
+ "required": [
467717
+ "width",
467718
+ "height"
467719
+ ],
467720
+ "properties": {
467721
+ "width": {
467722
+ "type": "integer",
467723
+ "minimum": 1
467724
+ },
467725
+ "height": {
467726
+ "type": "integer",
467727
+ "minimum": 1
467728
+ }
467729
+ }
467730
+ }
467731
+ ]
467732
+ },
467733
+ "notBlack": {
467734
+ "type": "boolean",
467735
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
467736
+ }
467737
+ }
467738
+ },
467739
+ "checkpoints": {
467740
+ "title": "Recording checkpoints",
467741
+ "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.",
467742
+ "anyOf": [
467743
+ {
467744
+ "type": "boolean",
467745
+ "title": "Recording checkpoints (boolean)",
467746
+ "description": "If `true`, enables checkpoints with default settings."
467747
+ },
467748
+ {
467749
+ "type": "object",
467750
+ "title": "Recording checkpoints (detailed)",
467751
+ "additionalProperties": false,
467752
+ "properties": {
467753
+ "maxVariation": {
467754
+ "type": "number",
467755
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
467756
+ "default": 0.05,
467757
+ "minimum": 0,
467758
+ "maximum": 1
467759
+ },
467760
+ "directory": {
467761
+ "type": "string",
467762
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
467763
+ "transform": [
467764
+ "trim"
467765
+ ]
467766
+ }
467767
+ }
467768
+ }
467769
+ ]
465851
467770
  }
465852
467771
  },
465853
467772
  "title": "Record (detailed)"
@@ -466109,10 +468028,11 @@
466109
468028
  },
466110
468029
  "overwrite": {
466111
468030
  "type": "string",
466112
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
468031
+ "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 — 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`.",
466113
468032
  "enum": [
466114
468033
  "true",
466115
- "false"
468034
+ "false",
468035
+ "aboveVariation"
466116
468036
  ]
466117
468037
  },
466118
468038
  "name": {
@@ -466170,6 +468090,88 @@
466170
468090
  }
466171
468091
  }
466172
468092
  ]
468093
+ },
468094
+ "verify": {
468095
+ "title": "Recording verify guards",
468096
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
468097
+ "type": "object",
468098
+ "additionalProperties": false,
468099
+ "properties": {
468100
+ "minDuration": {
468101
+ "type": "number",
468102
+ "description": "Fail if the video is shorter than this many seconds.",
468103
+ "minimum": 0
468104
+ },
468105
+ "maxDuration": {
468106
+ "type": "number",
468107
+ "description": "Fail if the video is longer than this many seconds.",
468108
+ "minimum": 0
468109
+ },
468110
+ "resolution": {
468111
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
468112
+ "anyOf": [
468113
+ {
468114
+ "type": "boolean",
468115
+ "title": "Verify resolution (boolean)"
468116
+ },
468117
+ {
468118
+ "type": "object",
468119
+ "title": "Verify resolution (detailed)",
468120
+ "additionalProperties": false,
468121
+ "required": [
468122
+ "width",
468123
+ "height"
468124
+ ],
468125
+ "properties": {
468126
+ "width": {
468127
+ "type": "integer",
468128
+ "minimum": 1
468129
+ },
468130
+ "height": {
468131
+ "type": "integer",
468132
+ "minimum": 1
468133
+ }
468134
+ }
468135
+ }
468136
+ ]
468137
+ },
468138
+ "notBlack": {
468139
+ "type": "boolean",
468140
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
468141
+ }
468142
+ }
468143
+ },
468144
+ "checkpoints": {
468145
+ "title": "Recording checkpoints",
468146
+ "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.",
468147
+ "anyOf": [
468148
+ {
468149
+ "type": "boolean",
468150
+ "title": "Recording checkpoints (boolean)",
468151
+ "description": "If `true`, enables checkpoints with default settings."
468152
+ },
468153
+ {
468154
+ "type": "object",
468155
+ "title": "Recording checkpoints (detailed)",
468156
+ "additionalProperties": false,
468157
+ "properties": {
468158
+ "maxVariation": {
468159
+ "type": "number",
468160
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
468161
+ "default": 0.05,
468162
+ "minimum": 0,
468163
+ "maximum": 1
468164
+ },
468165
+ "directory": {
468166
+ "type": "string",
468167
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
468168
+ "transform": [
468169
+ "trim"
468170
+ ]
468171
+ }
468172
+ }
468173
+ }
468174
+ ]
466173
468175
  }
466174
468176
  },
466175
468177
  "title": "Record (detailed)"
@@ -466236,6 +468238,17 @@
466236
468238
  "path": "results.mp4",
466237
468239
  "engine": "ffmpeg"
466238
468240
  },
468241
+ {
468242
+ "path": "results.mp4",
468243
+ "checkpoints": true
468244
+ },
468245
+ {
468246
+ "path": "results.mp4",
468247
+ "checkpoints": {
468248
+ "maxVariation": 0.02,
468249
+ "directory": "baselines/results"
468250
+ }
468251
+ },
466239
468252
  {
466240
468253
  "path": "results.mp4",
466241
468254
  "engine": {
@@ -508354,9 +510367,10 @@
508354
510367
  "anyOf": [
508355
510368
  {
508356
510369
  "title": "Screenshot (simple)",
510370
+ "$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 — 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.",
508357
510371
  "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.",
508358
510372
  "type": "string",
508359
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
510373
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508360
510374
  "transform": [
508361
510375
  "trim"
508362
510376
  ]
@@ -508590,9 +510604,10 @@
508590
510604
  },
508591
510605
  "path": {
508592
510606
  "title": "Screenshot (simple)",
510607
+ "$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 — 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.",
508593
510608
  "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.",
508594
510609
  "type": "string",
508595
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
510610
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508596
510611
  "transform": [
508597
510612
  "trim"
508598
510613
  ]
@@ -508850,9 +510865,10 @@
508850
510865
  "schemas": {
508851
510866
  "path": {
508852
510867
  "title": "Screenshot (simple)",
510868
+ "$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 — 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.",
508853
510869
  "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.",
508854
510870
  "type": "string",
508855
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
510871
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
508856
510872
  "transform": [
508857
510873
  "trim"
508858
510874
  ]
@@ -509086,9 +511102,10 @@
509086
511102
  },
509087
511103
  "path": {
509088
511104
  "title": "Screenshot (simple)",
511105
+ "$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 — 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.",
509089
511106
  "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.",
509090
511107
  "type": "string",
509091
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
511108
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509092
511109
  "transform": [
509093
511110
  "trim"
509094
511111
  ]
@@ -509562,9 +511579,10 @@
509562
511579
  },
509563
511580
  "path": {
509564
511581
  "title": "Screenshot (simple)",
511582
+ "$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 — 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.",
509565
511583
  "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.",
509566
511584
  "type": "string",
509567
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
511585
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
509568
511586
  "transform": [
509569
511587
  "trim"
509570
511588
  ]
@@ -509946,6 +511964,7 @@
509946
511964
  "image.png",
509947
511965
  "static/images/image.png",
509948
511966
  "/User/manny/projects/doc-detective/static/images/image.png",
511967
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
509949
511968
  "https://example.com/static/images/image.png",
509950
511969
  {
509951
511970
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -512380,10 +514399,11 @@
512380
514399
  },
512381
514400
  "overwrite": {
512382
514401
  "type": "string",
512383
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
514402
+ "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 — 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`.",
512384
514403
  "enum": [
512385
514404
  "true",
512386
- "false"
514405
+ "false",
514406
+ "aboveVariation"
512387
514407
  ]
512388
514408
  },
512389
514409
  "name": {
@@ -512441,6 +514461,88 @@
512441
514461
  }
512442
514462
  }
512443
514463
  ]
514464
+ },
514465
+ "verify": {
514466
+ "title": "Recording verify guards",
514467
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
514468
+ "type": "object",
514469
+ "additionalProperties": false,
514470
+ "properties": {
514471
+ "minDuration": {
514472
+ "type": "number",
514473
+ "description": "Fail if the video is shorter than this many seconds.",
514474
+ "minimum": 0
514475
+ },
514476
+ "maxDuration": {
514477
+ "type": "number",
514478
+ "description": "Fail if the video is longer than this many seconds.",
514479
+ "minimum": 0
514480
+ },
514481
+ "resolution": {
514482
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
514483
+ "anyOf": [
514484
+ {
514485
+ "type": "boolean",
514486
+ "title": "Verify resolution (boolean)"
514487
+ },
514488
+ {
514489
+ "type": "object",
514490
+ "title": "Verify resolution (detailed)",
514491
+ "additionalProperties": false,
514492
+ "required": [
514493
+ "width",
514494
+ "height"
514495
+ ],
514496
+ "properties": {
514497
+ "width": {
514498
+ "type": "integer",
514499
+ "minimum": 1
514500
+ },
514501
+ "height": {
514502
+ "type": "integer",
514503
+ "minimum": 1
514504
+ }
514505
+ }
514506
+ }
514507
+ ]
514508
+ },
514509
+ "notBlack": {
514510
+ "type": "boolean",
514511
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
514512
+ }
514513
+ }
514514
+ },
514515
+ "checkpoints": {
514516
+ "title": "Recording checkpoints",
514517
+ "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.",
514518
+ "anyOf": [
514519
+ {
514520
+ "type": "boolean",
514521
+ "title": "Recording checkpoints (boolean)",
514522
+ "description": "If `true`, enables checkpoints with default settings."
514523
+ },
514524
+ {
514525
+ "type": "object",
514526
+ "title": "Recording checkpoints (detailed)",
514527
+ "additionalProperties": false,
514528
+ "properties": {
514529
+ "maxVariation": {
514530
+ "type": "number",
514531
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
514532
+ "default": 0.05,
514533
+ "minimum": 0,
514534
+ "maximum": 1
514535
+ },
514536
+ "directory": {
514537
+ "type": "string",
514538
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
514539
+ "transform": [
514540
+ "trim"
514541
+ ]
514542
+ }
514543
+ }
514544
+ }
514545
+ ]
512444
514546
  }
512445
514547
  },
512446
514548
  "title": "Record (detailed)"
@@ -512702,10 +514804,11 @@
512702
514804
  },
512703
514805
  "overwrite": {
512704
514806
  "type": "string",
512705
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
514807
+ "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 — 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`.",
512706
514808
  "enum": [
512707
514809
  "true",
512708
- "false"
514810
+ "false",
514811
+ "aboveVariation"
512709
514812
  ]
512710
514813
  },
512711
514814
  "name": {
@@ -512763,6 +514866,88 @@
512763
514866
  }
512764
514867
  }
512765
514868
  ]
514869
+ },
514870
+ "verify": {
514871
+ "title": "Recording verify guards",
514872
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
514873
+ "type": "object",
514874
+ "additionalProperties": false,
514875
+ "properties": {
514876
+ "minDuration": {
514877
+ "type": "number",
514878
+ "description": "Fail if the video is shorter than this many seconds.",
514879
+ "minimum": 0
514880
+ },
514881
+ "maxDuration": {
514882
+ "type": "number",
514883
+ "description": "Fail if the video is longer than this many seconds.",
514884
+ "minimum": 0
514885
+ },
514886
+ "resolution": {
514887
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
514888
+ "anyOf": [
514889
+ {
514890
+ "type": "boolean",
514891
+ "title": "Verify resolution (boolean)"
514892
+ },
514893
+ {
514894
+ "type": "object",
514895
+ "title": "Verify resolution (detailed)",
514896
+ "additionalProperties": false,
514897
+ "required": [
514898
+ "width",
514899
+ "height"
514900
+ ],
514901
+ "properties": {
514902
+ "width": {
514903
+ "type": "integer",
514904
+ "minimum": 1
514905
+ },
514906
+ "height": {
514907
+ "type": "integer",
514908
+ "minimum": 1
514909
+ }
514910
+ }
514911
+ }
514912
+ ]
514913
+ },
514914
+ "notBlack": {
514915
+ "type": "boolean",
514916
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
514917
+ }
514918
+ }
514919
+ },
514920
+ "checkpoints": {
514921
+ "title": "Recording checkpoints",
514922
+ "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.",
514923
+ "anyOf": [
514924
+ {
514925
+ "type": "boolean",
514926
+ "title": "Recording checkpoints (boolean)",
514927
+ "description": "If `true`, enables checkpoints with default settings."
514928
+ },
514929
+ {
514930
+ "type": "object",
514931
+ "title": "Recording checkpoints (detailed)",
514932
+ "additionalProperties": false,
514933
+ "properties": {
514934
+ "maxVariation": {
514935
+ "type": "number",
514936
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
514937
+ "default": 0.05,
514938
+ "minimum": 0,
514939
+ "maximum": 1
514940
+ },
514941
+ "directory": {
514942
+ "type": "string",
514943
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
514944
+ "transform": [
514945
+ "trim"
514946
+ ]
514947
+ }
514948
+ }
514949
+ }
514950
+ ]
512766
514951
  }
512767
514952
  },
512768
514953
  "title": "Record (detailed)"
@@ -512829,6 +515014,17 @@
512829
515014
  "path": "results.mp4",
512830
515015
  "engine": "ffmpeg"
512831
515016
  },
515017
+ {
515018
+ "path": "results.mp4",
515019
+ "checkpoints": true
515020
+ },
515021
+ {
515022
+ "path": "results.mp4",
515023
+ "checkpoints": {
515024
+ "maxVariation": 0.02,
515025
+ "directory": "baselines/results"
515026
+ }
515027
+ },
512832
515028
  {
512833
515029
  "path": "results.mp4",
512834
515030
  "engine": {
@@ -556600,9 +558796,10 @@
556600
558796
  "anyOf": [
556601
558797
  {
556602
558798
  "title": "Screenshot (simple)",
558799
+ "$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 — 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.",
556603
558800
  "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.",
556604
558801
  "type": "string",
556605
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
558802
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
556606
558803
  "transform": [
556607
558804
  "trim"
556608
558805
  ]
@@ -556836,9 +559033,10 @@
556836
559033
  },
556837
559034
  "path": {
556838
559035
  "title": "Screenshot (simple)",
559036
+ "$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 — 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.",
556839
559037
  "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.",
556840
559038
  "type": "string",
556841
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
559039
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
556842
559040
  "transform": [
556843
559041
  "trim"
556844
559042
  ]
@@ -557096,9 +559294,10 @@
557096
559294
  "schemas": {
557097
559295
  "path": {
557098
559296
  "title": "Screenshot (simple)",
559297
+ "$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 — 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.",
557099
559298
  "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.",
557100
559299
  "type": "string",
557101
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
559300
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557102
559301
  "transform": [
557103
559302
  "trim"
557104
559303
  ]
@@ -557332,9 +559531,10 @@
557332
559531
  },
557333
559532
  "path": {
557334
559533
  "title": "Screenshot (simple)",
559534
+ "$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 — 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.",
557335
559535
  "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.",
557336
559536
  "type": "string",
557337
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
559537
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557338
559538
  "transform": [
557339
559539
  "trim"
557340
559540
  ]
@@ -557808,9 +560008,10 @@
557808
560008
  },
557809
560009
  "path": {
557810
560010
  "title": "Screenshot (simple)",
560011
+ "$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 — 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.",
557811
560012
  "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.",
557812
560013
  "type": "string",
557813
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
560014
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
557814
560015
  "transform": [
557815
560016
  "trim"
557816
560017
  ]
@@ -558192,6 +560393,7 @@
558192
560393
  "image.png",
558193
560394
  "static/images/image.png",
558194
560395
  "/User/manny/projects/doc-detective/static/images/image.png",
560396
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
558195
560397
  "https://example.com/static/images/image.png",
558196
560398
  {
558197
560399
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -560626,10 +562828,11 @@
560626
562828
  },
560627
562829
  "overwrite": {
560628
562830
  "type": "string",
560629
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
562831
+ "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 — 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`.",
560630
562832
  "enum": [
560631
562833
  "true",
560632
- "false"
562834
+ "false",
562835
+ "aboveVariation"
560633
562836
  ]
560634
562837
  },
560635
562838
  "name": {
@@ -560687,6 +562890,88 @@
560687
562890
  }
560688
562891
  }
560689
562892
  ]
562893
+ },
562894
+ "verify": {
562895
+ "title": "Recording verify guards",
562896
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
562897
+ "type": "object",
562898
+ "additionalProperties": false,
562899
+ "properties": {
562900
+ "minDuration": {
562901
+ "type": "number",
562902
+ "description": "Fail if the video is shorter than this many seconds.",
562903
+ "minimum": 0
562904
+ },
562905
+ "maxDuration": {
562906
+ "type": "number",
562907
+ "description": "Fail if the video is longer than this many seconds.",
562908
+ "minimum": 0
562909
+ },
562910
+ "resolution": {
562911
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
562912
+ "anyOf": [
562913
+ {
562914
+ "type": "boolean",
562915
+ "title": "Verify resolution (boolean)"
562916
+ },
562917
+ {
562918
+ "type": "object",
562919
+ "title": "Verify resolution (detailed)",
562920
+ "additionalProperties": false,
562921
+ "required": [
562922
+ "width",
562923
+ "height"
562924
+ ],
562925
+ "properties": {
562926
+ "width": {
562927
+ "type": "integer",
562928
+ "minimum": 1
562929
+ },
562930
+ "height": {
562931
+ "type": "integer",
562932
+ "minimum": 1
562933
+ }
562934
+ }
562935
+ }
562936
+ ]
562937
+ },
562938
+ "notBlack": {
562939
+ "type": "boolean",
562940
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
562941
+ }
562942
+ }
562943
+ },
562944
+ "checkpoints": {
562945
+ "title": "Recording checkpoints",
562946
+ "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.",
562947
+ "anyOf": [
562948
+ {
562949
+ "type": "boolean",
562950
+ "title": "Recording checkpoints (boolean)",
562951
+ "description": "If `true`, enables checkpoints with default settings."
562952
+ },
562953
+ {
562954
+ "type": "object",
562955
+ "title": "Recording checkpoints (detailed)",
562956
+ "additionalProperties": false,
562957
+ "properties": {
562958
+ "maxVariation": {
562959
+ "type": "number",
562960
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
562961
+ "default": 0.05,
562962
+ "minimum": 0,
562963
+ "maximum": 1
562964
+ },
562965
+ "directory": {
562966
+ "type": "string",
562967
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
562968
+ "transform": [
562969
+ "trim"
562970
+ ]
562971
+ }
562972
+ }
562973
+ }
562974
+ ]
560690
562975
  }
560691
562976
  },
560692
562977
  "title": "Record (detailed)"
@@ -560948,10 +563233,11 @@
560948
563233
  },
560949
563234
  "overwrite": {
560950
563235
  "type": "string",
560951
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
563236
+ "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 — 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`.",
560952
563237
  "enum": [
560953
563238
  "true",
560954
- "false"
563239
+ "false",
563240
+ "aboveVariation"
560955
563241
  ]
560956
563242
  },
560957
563243
  "name": {
@@ -561009,6 +563295,88 @@
561009
563295
  }
561010
563296
  }
561011
563297
  ]
563298
+ },
563299
+ "verify": {
563300
+ "title": "Recording verify guards",
563301
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
563302
+ "type": "object",
563303
+ "additionalProperties": false,
563304
+ "properties": {
563305
+ "minDuration": {
563306
+ "type": "number",
563307
+ "description": "Fail if the video is shorter than this many seconds.",
563308
+ "minimum": 0
563309
+ },
563310
+ "maxDuration": {
563311
+ "type": "number",
563312
+ "description": "Fail if the video is longer than this many seconds.",
563313
+ "minimum": 0
563314
+ },
563315
+ "resolution": {
563316
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
563317
+ "anyOf": [
563318
+ {
563319
+ "type": "boolean",
563320
+ "title": "Verify resolution (boolean)"
563321
+ },
563322
+ {
563323
+ "type": "object",
563324
+ "title": "Verify resolution (detailed)",
563325
+ "additionalProperties": false,
563326
+ "required": [
563327
+ "width",
563328
+ "height"
563329
+ ],
563330
+ "properties": {
563331
+ "width": {
563332
+ "type": "integer",
563333
+ "minimum": 1
563334
+ },
563335
+ "height": {
563336
+ "type": "integer",
563337
+ "minimum": 1
563338
+ }
563339
+ }
563340
+ }
563341
+ ]
563342
+ },
563343
+ "notBlack": {
563344
+ "type": "boolean",
563345
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
563346
+ }
563347
+ }
563348
+ },
563349
+ "checkpoints": {
563350
+ "title": "Recording checkpoints",
563351
+ "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.",
563352
+ "anyOf": [
563353
+ {
563354
+ "type": "boolean",
563355
+ "title": "Recording checkpoints (boolean)",
563356
+ "description": "If `true`, enables checkpoints with default settings."
563357
+ },
563358
+ {
563359
+ "type": "object",
563360
+ "title": "Recording checkpoints (detailed)",
563361
+ "additionalProperties": false,
563362
+ "properties": {
563363
+ "maxVariation": {
563364
+ "type": "number",
563365
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
563366
+ "default": 0.05,
563367
+ "minimum": 0,
563368
+ "maximum": 1
563369
+ },
563370
+ "directory": {
563371
+ "type": "string",
563372
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
563373
+ "transform": [
563374
+ "trim"
563375
+ ]
563376
+ }
563377
+ }
563378
+ }
563379
+ ]
561012
563380
  }
561013
563381
  },
561014
563382
  "title": "Record (detailed)"
@@ -561075,6 +563443,17 @@
561075
563443
  "path": "results.mp4",
561076
563444
  "engine": "ffmpeg"
561077
563445
  },
563446
+ {
563447
+ "path": "results.mp4",
563448
+ "checkpoints": true
563449
+ },
563450
+ {
563451
+ "path": "results.mp4",
563452
+ "checkpoints": {
563453
+ "maxVariation": 0.02,
563454
+ "directory": "baselines/results"
563455
+ }
563456
+ },
561078
563457
  {
561079
563458
  "path": "results.mp4",
561080
563459
  "engine": {
@@ -601501,9 +603880,10 @@
601501
603880
  "anyOf": [
601502
603881
  {
601503
603882
  "title": "Screenshot (simple)",
603883
+ "$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 — 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.",
601504
603884
  "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.",
601505
603885
  "type": "string",
601506
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
603886
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
601507
603887
  "transform": [
601508
603888
  "trim"
601509
603889
  ]
@@ -601737,9 +604117,10 @@
601737
604117
  },
601738
604118
  "path": {
601739
604119
  "title": "Screenshot (simple)",
604120
+ "$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 — 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.",
601740
604121
  "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.",
601741
604122
  "type": "string",
601742
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
604123
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
601743
604124
  "transform": [
601744
604125
  "trim"
601745
604126
  ]
@@ -601997,9 +604378,10 @@
601997
604378
  "schemas": {
601998
604379
  "path": {
601999
604380
  "title": "Screenshot (simple)",
604381
+ "$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 — 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.",
602000
604382
  "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.",
602001
604383
  "type": "string",
602002
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
604384
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602003
604385
  "transform": [
602004
604386
  "trim"
602005
604387
  ]
@@ -602233,9 +604615,10 @@
602233
604615
  },
602234
604616
  "path": {
602235
604617
  "title": "Screenshot (simple)",
604618
+ "$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 — 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.",
602236
604619
  "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.",
602237
604620
  "type": "string",
602238
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
604621
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602239
604622
  "transform": [
602240
604623
  "trim"
602241
604624
  ]
@@ -602709,9 +605092,10 @@
602709
605092
  },
602710
605093
  "path": {
602711
605094
  "title": "Screenshot (simple)",
605095
+ "$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 — 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.",
602712
605096
  "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.",
602713
605097
  "type": "string",
602714
- "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|[A-Za-z]:[\\/\\\\].*\\.(?:png|PNG)|[\\/\\\\]?[A-Za-z0-9_.\\/\\\\-]*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
605098
+ "pattern": "^(?:https?:\\/\\/.+\\.(?:png|PNG)(?:\\?.*)?|.*\\.(?:png|PNG)|\\$[A-Za-z0-9_]+)$",
602715
605099
  "transform": [
602716
605100
  "trim"
602717
605101
  ]
@@ -603093,6 +605477,7 @@
603093
605477
  "image.png",
603094
605478
  "static/images/image.png",
603095
605479
  "/User/manny/projects/doc-detective/static/images/image.png",
605480
+ "/Users/jane doe/projects/my docs (v2)/static/images/image.png",
603096
605481
  "https://example.com/static/images/image.png",
603097
605482
  {
603098
605483
  "path": "https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/request-changes-button-0c851bb3.png",
@@ -605527,10 +607912,11 @@
605527
607912
  },
605528
607913
  "overwrite": {
605529
607914
  "type": "string",
605530
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
607915
+ "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 — 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`.",
605531
607916
  "enum": [
605532
607917
  "true",
605533
- "false"
607918
+ "false",
607919
+ "aboveVariation"
605534
607920
  ]
605535
607921
  },
605536
607922
  "name": {
@@ -605588,6 +607974,88 @@
605588
607974
  }
605589
607975
  }
605590
607976
  ]
607977
+ },
607978
+ "verify": {
607979
+ "title": "Recording verify guards",
607980
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
607981
+ "type": "object",
607982
+ "additionalProperties": false,
607983
+ "properties": {
607984
+ "minDuration": {
607985
+ "type": "number",
607986
+ "description": "Fail if the video is shorter than this many seconds.",
607987
+ "minimum": 0
607988
+ },
607989
+ "maxDuration": {
607990
+ "type": "number",
607991
+ "description": "Fail if the video is longer than this many seconds.",
607992
+ "minimum": 0
607993
+ },
607994
+ "resolution": {
607995
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
607996
+ "anyOf": [
607997
+ {
607998
+ "type": "boolean",
607999
+ "title": "Verify resolution (boolean)"
608000
+ },
608001
+ {
608002
+ "type": "object",
608003
+ "title": "Verify resolution (detailed)",
608004
+ "additionalProperties": false,
608005
+ "required": [
608006
+ "width",
608007
+ "height"
608008
+ ],
608009
+ "properties": {
608010
+ "width": {
608011
+ "type": "integer",
608012
+ "minimum": 1
608013
+ },
608014
+ "height": {
608015
+ "type": "integer",
608016
+ "minimum": 1
608017
+ }
608018
+ }
608019
+ }
608020
+ ]
608021
+ },
608022
+ "notBlack": {
608023
+ "type": "boolean",
608024
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
608025
+ }
608026
+ }
608027
+ },
608028
+ "checkpoints": {
608029
+ "title": "Recording checkpoints",
608030
+ "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.",
608031
+ "anyOf": [
608032
+ {
608033
+ "type": "boolean",
608034
+ "title": "Recording checkpoints (boolean)",
608035
+ "description": "If `true`, enables checkpoints with default settings."
608036
+ },
608037
+ {
608038
+ "type": "object",
608039
+ "title": "Recording checkpoints (detailed)",
608040
+ "additionalProperties": false,
608041
+ "properties": {
608042
+ "maxVariation": {
608043
+ "type": "number",
608044
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
608045
+ "default": 0.05,
608046
+ "minimum": 0,
608047
+ "maximum": 1
608048
+ },
608049
+ "directory": {
608050
+ "type": "string",
608051
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
608052
+ "transform": [
608053
+ "trim"
608054
+ ]
608055
+ }
608056
+ }
608057
+ }
608058
+ ]
605591
608059
  }
605592
608060
  },
605593
608061
  "title": "Record (detailed)"
@@ -605849,10 +608317,11 @@
605849
608317
  },
605850
608318
  "overwrite": {
605851
608319
  "type": "string",
605852
- "description": "If `true`, overwrites the existing recording at `path` if it exists.",
608320
+ "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 — 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`.",
605853
608321
  "enum": [
605854
608322
  "true",
605855
- "false"
608323
+ "false",
608324
+ "aboveVariation"
605856
608325
  ]
605857
608326
  },
605858
608327
  "name": {
@@ -605910,6 +608379,88 @@
605910
608379
  }
605911
608380
  }
605912
608381
  ]
608382
+ },
608383
+ "verify": {
608384
+ "title": "Recording verify guards",
608385
+ "description": "Structural assertions on the produced video file, evaluated when the recording stops. Unlike checkpoint drift (a WARNING), a violated guard FAILs the stopRecord step — these are properties you explicitly demand of the artifact.",
608386
+ "type": "object",
608387
+ "additionalProperties": false,
608388
+ "properties": {
608389
+ "minDuration": {
608390
+ "type": "number",
608391
+ "description": "Fail if the video is shorter than this many seconds.",
608392
+ "minimum": 0
608393
+ },
608394
+ "maxDuration": {
608395
+ "type": "number",
608396
+ "description": "Fail if the video is longer than this many seconds.",
608397
+ "minimum": 0
608398
+ },
608399
+ "resolution": {
608400
+ "description": "Verify the video's dimensions (±2 pixels — encoders round to even dimensions). `true` compares against the capture plan's resolved expectation (the crop rectangle when a window/viewport crop applied, else the capture frame size; skipped when no expectation exists for the engine). An object compares literal dimensions.",
608401
+ "anyOf": [
608402
+ {
608403
+ "type": "boolean",
608404
+ "title": "Verify resolution (boolean)"
608405
+ },
608406
+ {
608407
+ "type": "object",
608408
+ "title": "Verify resolution (detailed)",
608409
+ "additionalProperties": false,
608410
+ "required": [
608411
+ "width",
608412
+ "height"
608413
+ ],
608414
+ "properties": {
608415
+ "width": {
608416
+ "type": "integer",
608417
+ "minimum": 1
608418
+ },
608419
+ "height": {
608420
+ "type": "integer",
608421
+ "minimum": 1
608422
+ }
608423
+ }
608424
+ }
608425
+ ]
608426
+ },
608427
+ "notBlack": {
608428
+ "type": "boolean",
608429
+ "description": "Fail if the video is essentially all black (black frames cover 95% or more of its duration) — the classic symptom of a broken screen capture."
608430
+ }
608431
+ }
608432
+ },
608433
+ "checkpoints": {
608434
+ "title": "Recording checkpoints",
608435
+ "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.",
608436
+ "anyOf": [
608437
+ {
608438
+ "type": "boolean",
608439
+ "title": "Recording checkpoints (boolean)",
608440
+ "description": "If `true`, enables checkpoints with default settings."
608441
+ },
608442
+ {
608443
+ "type": "object",
608444
+ "title": "Recording checkpoints (detailed)",
608445
+ "additionalProperties": false,
608446
+ "properties": {
608447
+ "maxVariation": {
608448
+ "type": "number",
608449
+ "description": "Maximum fractional pixel difference tolerated between a checkpoint and its baseline before the drift surfaces as a WARNING.",
608450
+ "default": 0.05,
608451
+ "minimum": 0,
608452
+ "maximum": 1
608453
+ },
608454
+ "directory": {
608455
+ "type": "string",
608456
+ "description": "Directory for the checkpoint baselines. If unset, defaults to a `.checkpoints` directory beside the recording (for example, `demo.mp4.checkpoints/`).",
608457
+ "transform": [
608458
+ "trim"
608459
+ ]
608460
+ }
608461
+ }
608462
+ }
608463
+ ]
605913
608464
  }
605914
608465
  },
605915
608466
  "title": "Record (detailed)"
@@ -605976,6 +608527,17 @@
605976
608527
  "path": "results.mp4",
605977
608528
  "engine": "ffmpeg"
605978
608529
  },
608530
+ {
608531
+ "path": "results.mp4",
608532
+ "checkpoints": true
608533
+ },
608534
+ {
608535
+ "path": "results.mp4",
608536
+ "checkpoints": {
608537
+ "maxVariation": 0.02,
608538
+ "directory": "baselines/results"
608539
+ }
608540
+ },
605979
608541
  {
605980
608542
  "path": "results.mp4",
605981
608543
  "engine": {