agent-scenario-loop 0.1.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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/app/profile-session.ts +812 -0
  4. package/core/config-template.json +41 -0
  5. package/dist/core/agent-summary.d.ts +15 -0
  6. package/dist/core/agent-summary.js +177 -0
  7. package/dist/core/artifact-contract.d.ts +151 -0
  8. package/dist/core/artifact-contract.js +897 -0
  9. package/dist/core/artifact-layout.d.ts +56 -0
  10. package/dist/core/artifact-layout.js +61 -0
  11. package/dist/core/artifact-writer.d.ts +44 -0
  12. package/dist/core/artifact-writer.js +55 -0
  13. package/dist/core/comparison.d.ts +133 -0
  14. package/dist/core/comparison.js +294 -0
  15. package/dist/core/evidence-interpreter.d.ts +28 -0
  16. package/dist/core/evidence-interpreter.js +69 -0
  17. package/dist/core/execution-plan.d.ts +44 -0
  18. package/dist/core/execution-plan.js +95 -0
  19. package/dist/core/planner.d.ts +132 -0
  20. package/dist/core/planner.js +812 -0
  21. package/dist/core/ports.d.ts +198 -0
  22. package/dist/core/ports.js +146 -0
  23. package/dist/core/run-index.d.ts +62 -0
  24. package/dist/core/run-index.js +143 -0
  25. package/dist/core/schema-validator.d.ts +86 -0
  26. package/dist/core/schema-validator.js +407 -0
  27. package/dist/index.d.ts +11 -0
  28. package/dist/index.js +27 -0
  29. package/dist/runner/agent-device-driver.d.ts +126 -0
  30. package/dist/runner/agent-device-driver.js +168 -0
  31. package/dist/runner/agent-device.d.ts +295 -0
  32. package/dist/runner/agent-device.js +1271 -0
  33. package/dist/runner/android-adb-driver.d.ts +175 -0
  34. package/dist/runner/android-adb-driver.js +399 -0
  35. package/dist/runner/android-adb.d.ts +254 -0
  36. package/dist/runner/android-adb.js +1618 -0
  37. package/dist/runner/argent-driver.d.ts +183 -0
  38. package/dist/runner/argent-driver.js +297 -0
  39. package/dist/runner/argent.d.ts +349 -0
  40. package/dist/runner/argent.js +1211 -0
  41. package/dist/runner/check-plan.d.ts +45 -0
  42. package/dist/runner/check-plan.js +210 -0
  43. package/dist/runner/cli.d.ts +20 -0
  44. package/dist/runner/cli.js +23 -0
  45. package/dist/runner/compare-latest.d.ts +99 -0
  46. package/dist/runner/compare-latest.js +233 -0
  47. package/dist/runner/compare.d.ts +58 -0
  48. package/dist/runner/compare.js +157 -0
  49. package/dist/runner/demo-loop.d.ts +45 -0
  50. package/dist/runner/demo-loop.js +170 -0
  51. package/dist/runner/example-android-live.d.ts +137 -0
  52. package/dist/runner/example-android-live.js +454 -0
  53. package/dist/runner/example-ios-live.d.ts +137 -0
  54. package/dist/runner/example-ios-live.js +471 -0
  55. package/dist/runner/host-doctor.d.ts +131 -0
  56. package/dist/runner/host-doctor.js +628 -0
  57. package/dist/runner/init-project.d.ts +88 -0
  58. package/dist/runner/init-project.js +263 -0
  59. package/dist/runner/ios-simctl-driver.d.ts +69 -0
  60. package/dist/runner/ios-simctl-driver.js +97 -0
  61. package/dist/runner/ios-simctl.d.ts +254 -0
  62. package/dist/runner/ios-simctl.js +1415 -0
  63. package/dist/runner/live-android.d.ts +137 -0
  64. package/dist/runner/live-android.js +539 -0
  65. package/dist/runner/live-comparison.d.ts +67 -0
  66. package/dist/runner/live-comparison.js +147 -0
  67. package/dist/runner/live-ios.d.ts +137 -0
  68. package/dist/runner/live-ios.js +460 -0
  69. package/dist/runner/live-proof-summary.d.ts +263 -0
  70. package/dist/runner/live-proof-summary.js +465 -0
  71. package/dist/runner/live-proof.d.ts +467 -0
  72. package/dist/runner/live-proof.js +920 -0
  73. package/dist/runner/local-env.d.ts +64 -0
  74. package/dist/runner/local-env.js +155 -0
  75. package/dist/runner/profile-android.d.ts +82 -0
  76. package/dist/runner/profile-android.js +671 -0
  77. package/dist/runner/profile-ios.d.ts +108 -0
  78. package/dist/runner/profile-ios.js +532 -0
  79. package/dist/runner/profile-mobile.d.ts +254 -0
  80. package/dist/runner/profile-mobile.js +1307 -0
  81. package/dist/runner/validate-project.d.ts +273 -0
  82. package/dist/runner/validate-project.js +1501 -0
  83. package/docs/adapters.md +145 -0
  84. package/docs/api.md +94 -0
  85. package/docs/authoring.md +196 -0
  86. package/docs/concepts.md +136 -0
  87. package/docs/consumer-rehearsal.md +115 -0
  88. package/docs/contracts.md +267 -0
  89. package/docs/live-proofs.md +270 -0
  90. package/docs/principles.md +46 -0
  91. package/examples/event-logs/app-startup-baseline.log +4 -0
  92. package/examples/event-logs/app-startup-current.log +4 -0
  93. package/examples/minimal-app/README.md +70 -0
  94. package/examples/mobile-app/README.md +302 -0
  95. package/examples/mobile-app/app.json +22 -0
  96. package/examples/mobile-app/asl/package-scripts.json +32 -0
  97. package/examples/mobile-app/asl.config.json +37 -0
  98. package/examples/mobile-app/event-logs/android-app-startup.log +4 -0
  99. package/examples/mobile-app/event-logs/android-open-close-cycle.log +12 -0
  100. package/examples/mobile-app/event-logs/android-scroll-settle.log +12 -0
  101. package/examples/mobile-app/event-logs/app-startup.log +4 -0
  102. package/examples/mobile-app/event-logs/open-close-cycle.log +12 -0
  103. package/examples/mobile-app/event-logs/scroll-settle.log +12 -0
  104. package/examples/mobile-app/index.ts +20 -0
  105. package/examples/mobile-app/metro.config.js +20 -0
  106. package/examples/mobile-app/package.json +62 -0
  107. package/examples/mobile-app/patches/expo-modules-jsi@56.0.10.patch +19 -0
  108. package/examples/mobile-app/plugins/with-ios-build-compat.js +271 -0
  109. package/examples/mobile-app/pnpm-lock.yaml +4440 -0
  110. package/examples/mobile-app/runner-manifests/evidence-provider.json +79 -0
  111. package/examples/mobile-app/runner-manifests/primary-runner.json +19 -0
  112. package/examples/mobile-app/scenarios/android/app-startup-video.json +73 -0
  113. package/examples/mobile-app/scenarios/android/app-startup.json +44 -0
  114. package/examples/mobile-app/scenarios/android/open-close-cycle.json +54 -0
  115. package/examples/mobile-app/scenarios/android/scroll-settle.json +49 -0
  116. package/examples/mobile-app/scenarios/ios/app-startup.json +44 -0
  117. package/examples/mobile-app/scenarios/ios/open-close-cycle.json +54 -0
  118. package/examples/mobile-app/scenarios/ios/scroll-settle.json +49 -0
  119. package/examples/mobile-app/scenarios/mobile/app-startup.json +91 -0
  120. package/examples/mobile-app/scenarios/mobile/open-close-cycle.json +160 -0
  121. package/examples/mobile-app/scenarios/mobile/scroll-settle.json +148 -0
  122. package/examples/mobile-app/scripts/asl-capture-accessibility-provider.mjs +112 -0
  123. package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +127 -0
  124. package/examples/mobile-app/src/devtools/profile-session.ts +7 -0
  125. package/examples/mobile-app/src/example-screen.tsx +322 -0
  126. package/examples/mobile-app/tsconfig.json +16 -0
  127. package/examples/mobile-app/tsconfig.typecheck.json +13 -0
  128. package/examples/runners/README.md +44 -0
  129. package/examples/runners/adb-android.json +25 -0
  130. package/examples/runners/agent-device-android.json +27 -0
  131. package/examples/runners/agent-device-ios.json +27 -0
  132. package/examples/runners/argent-android.json +32 -0
  133. package/examples/runners/argent-ios.json +32 -0
  134. package/examples/runners/argent-react-profiler-provider.json +15 -0
  135. package/examples/runners/axe-accessibility-provider.json +24 -0
  136. package/examples/runners/manual-log-ingest.json +9 -0
  137. package/examples/runners/rozenite-profiler-provider.json +9 -0
  138. package/examples/runners/script-accessibility-provider.json +24 -0
  139. package/examples/runners/script-memory-provider.json +24 -0
  140. package/examples/runners/script-network-provider.json +24 -0
  141. package/examples/runners/script-profiler-provider.json +30 -0
  142. package/examples/runners/xcodebuildmcp-ios.json +29 -0
  143. package/examples/scenarios/ios/app-startup.json +28 -0
  144. package/examples/scenarios/ios/open-close-cycle.json +35 -0
  145. package/examples/scenarios/mobile/app-startup.json +72 -0
  146. package/examples/scenarios/mobile/media-open-close.json +141 -0
  147. package/examples/scenarios/mobile/open-close-cycle.json +135 -0
  148. package/examples/scenarios/mobile/scroll-settle.json +106 -0
  149. package/package.json +240 -0
  150. package/schemas/budget-verdict.schema.json +115 -0
  151. package/schemas/causal-run.schema.json +279 -0
  152. package/schemas/comparison.schema.json +196 -0
  153. package/schemas/health.schema.json +108 -0
  154. package/schemas/live-proof-set.schema.json +195 -0
  155. package/schemas/live-proof.schema.json +413 -0
  156. package/schemas/manifest.schema.json +204 -0
  157. package/schemas/metrics.schema.json +137 -0
  158. package/schemas/project-validation.schema.json +343 -0
  159. package/schemas/runner-capabilities.schema.json +217 -0
  160. package/schemas/scenario.schema.json +400 -0
  161. package/schemas/verdict.schema.json +88 -0
  162. package/templates/evidence-provider.json +83 -0
  163. package/templates/gitignore-snippet +9 -0
  164. package/templates/integration-readme.md +125 -0
  165. package/templates/mobile-scenario.json +133 -0
  166. package/templates/package-scripts.json +32 -0
  167. package/templates/primary-runner.json +19 -0
  168. package/templates/project.config.json +37 -0
  169. package/templates/scripts/asl-capture-accessibility-provider.mjs +112 -0
  170. package/templates/scripts/asl-capture-profiler-provider.mjs +127 -0
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "runnerId": "script-network-provider",
4
+ "kind": "evidenceProvider",
5
+ "platforms": ["ios", "android"],
6
+ "capabilities": ["network"],
7
+ "artifactOutputs": ["network", "signals"],
8
+ "lifecycle": ["prepare", "startWindow", "capture", "stopWindow", "finalize"],
9
+ "providerCommands": [
10
+ {
11
+ "id": "capture-network",
12
+ "phase": "capture",
13
+ "command": "capture-network",
14
+ "args": ["--platform", "{platform}", "--scenario", "{scenarioId}", "--out", "{providerDir}/network.har"],
15
+ "outputs": [
16
+ {
17
+ "channel": "signal",
18
+ "kind": "network",
19
+ "path": "{providerDir}/network.har"
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "runnerId": "script-profiler-provider",
4
+ "kind": "evidenceProvider",
5
+ "platforms": ["ios", "android"],
6
+ "capabilities": ["profiler"],
7
+ "driverActions": ["collectPerfSignals"],
8
+ "artifactOutputs": ["profiler", "signals"],
9
+ "lifecycle": ["prepare", "startWindow", "capture", "stopWindow", "finalize"],
10
+ "providerCommands": [
11
+ {
12
+ "id": "capture-profiler",
13
+ "phase": "capture",
14
+ "command": "capture-profiler",
15
+ "args": ["--platform", "{platform}", "--run-id", "{runId}", "--out", "{providerDir}/profiler.json"],
16
+ "outputs": [
17
+ {
18
+ "channel": "provider",
19
+ "kind": "profiler",
20
+ "path": "{providerDir}/profiler.json"
21
+ },
22
+ {
23
+ "channel": "signal",
24
+ "kind": "js",
25
+ "path": "{providerDir}/profiler.json"
26
+ }
27
+ ]
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "runnerId": "xcodebuildmcp-ios",
4
+ "kind": "primary",
5
+ "platforms": ["ios"],
6
+ "capabilities": [
7
+ "launch",
8
+ "sessionControl",
9
+ "command",
10
+ "logCapture",
11
+ "artifactWrite",
12
+ "screenshot",
13
+ "video",
14
+ "uiTree",
15
+ "accessibility"
16
+ ],
17
+ "driverActions": ["tap", "scroll", "assertVisible", "inspectTree", "screenshot", "record", "readLogs"],
18
+ "artifactOutputs": ["logs", "signals", "screenshot", "video", "uiTree", "accessibility"],
19
+ "lifecycle": [
20
+ "prepare",
21
+ "launch",
22
+ "startSession",
23
+ "executeStep",
24
+ "waitForTruthEvent",
25
+ "captureEvidence",
26
+ "stopSession",
27
+ "finalize"
28
+ ]
29
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "app-startup",
3
+ "flowId": "startup",
4
+ "description": "Cold-launch the app directly into an active profile session and measure time to the first usable screen.",
5
+ "type": "app-startup",
6
+ "platform": "ios",
7
+ "interactionDriver": "xcodebuildmcp",
8
+ "trigger": {
9
+ "kind": "cold-launch",
10
+ "label": "Launch the app from a terminated state."
11
+ },
12
+ "defaultIterations": 1,
13
+ "timelinePhases": {
14
+ "app_first_usable_screen": "visual"
15
+ },
16
+ "budgets": {
17
+ "metric": "startup first usable screen",
18
+ "pass": {
19
+ "failures": 0,
20
+ "timeouts": 0,
21
+ "cycleP50Ms": 8000,
22
+ "cycleP95Ms": 8000
23
+ }
24
+ },
25
+ "metricEvents": {
26
+ "opened": "app_first_usable_screen"
27
+ }
28
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "open-close-cycle",
3
+ "flowId": "open-close-cycle",
4
+ "description": "Repeatedly open and dismiss a target surface to capture deterministic repeated-cycle evidence.",
5
+ "platform": "ios",
6
+ "interactionDriver": "xcodebuildmcp",
7
+ "trigger": {
8
+ "kind": "tap",
9
+ "label": "Open a target surface from a stable home state."
10
+ },
11
+ "defaultIterations": 5,
12
+ "timelinePhases": {
13
+ "surface_open_requested": "intent",
14
+ "surface_opened": "navigation",
15
+ "surface_close_requested": "intent",
16
+ "surface_dismissed": "completion"
17
+ },
18
+ "metricEvents": {
19
+ "openRequested": "surface_open_requested",
20
+ "opened": "surface_opened",
21
+ "closeRequested": "surface_close_requested",
22
+ "dismissed": "surface_dismissed"
23
+ },
24
+ "budgets": {
25
+ "metric": "surface open-close cycle",
26
+ "pass": {
27
+ "failures": 0,
28
+ "timeouts": 0,
29
+ "cycleP50Ms": 1200,
30
+ "cycleP95Ms": 1800,
31
+ "openP50Ms": 700,
32
+ "closeP50Ms": 700
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "id": "app-startup",
4
+ "flowId": "startup",
5
+ "description": "Launch the app and wait for the first usable screen milestone.",
6
+ "journey": {
7
+ "name": "App startup",
8
+ "intent": "Open the app from a cold state and prove the first usable screen is ready.",
9
+ "actor": "returning user",
10
+ "startState": "app not running",
11
+ "endState": "first usable screen visible"
12
+ },
13
+ "platforms": ["ios", "android"],
14
+ "requiredCapabilities": ["launch", "sessionControl", "logCapture", "artifactWrite"],
15
+ "optionalCapabilities": ["screenshot", "video"],
16
+ "truthEvents": {
17
+ "firstUsable": {
18
+ "event": "app_first_usable_screen",
19
+ "required": true,
20
+ "timeoutMs": 8000,
21
+ "phase": "visual"
22
+ }
23
+ },
24
+ "milestones": [
25
+ {
26
+ "id": "firstUsable",
27
+ "event": "app_first_usable_screen",
28
+ "description": "The first app-owned screen is visible and usable.",
29
+ "required": true,
30
+ "timeoutMs": 8000,
31
+ "phase": "visual"
32
+ }
33
+ ],
34
+ "expectedEvents": ["app_first_usable_screen"],
35
+ "cycles": {
36
+ "iterations": 1,
37
+ "warmupIterations": 0,
38
+ "stopOnFailure": true
39
+ },
40
+ "budgets": [
41
+ {
42
+ "name": "startup first usable p95",
43
+ "source": "milestone",
44
+ "metric": "p95",
45
+ "unit": "ms",
46
+ "limit": 8000,
47
+ "toMilestone": "firstUsable"
48
+ }
49
+ ],
50
+ "steps": [
51
+ {
52
+ "id": "launch",
53
+ "kind": "launch"
54
+ },
55
+ {
56
+ "id": "wait-for-first-usable",
57
+ "kind": "waitForMilestone",
58
+ "milestone": "firstUsable",
59
+ "timeoutMs": 8000
60
+ },
61
+ {
62
+ "id": "capture-final-screen",
63
+ "kind": "captureEvidence",
64
+ "artifact": "screenshot",
65
+ "required": false
66
+ }
67
+ ],
68
+ "artifacts": {
69
+ "required": ["logs", "signals"],
70
+ "optional": ["screenshot", "video"]
71
+ }
72
+ }
@@ -0,0 +1,141 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "id": "media-open-close",
4
+ "flowId": "media-open-close",
5
+ "description": "Open media, wait for visual readiness, then dismiss cleanly.",
6
+ "journey": {
7
+ "name": "Open and dismiss media",
8
+ "intent": "Open a media surface, prove visual readiness, then dismiss it cleanly.",
9
+ "actor": "app user",
10
+ "startState": "media entry point visible",
11
+ "endState": "media surface dismissed"
12
+ },
13
+ "platforms": ["ios", "android"],
14
+ "requiredCapabilities": ["launch", "sessionControl", "command", "logCapture", "artifactWrite"],
15
+ "optionalCapabilities": ["screenshot", "video", "memory", "network"],
16
+ "truthEvents": {
17
+ "openRequested": {
18
+ "event": "media_open_requested",
19
+ "required": true,
20
+ "phase": "intent"
21
+ },
22
+ "mediaReady": {
23
+ "event": "media_visual_ready",
24
+ "required": true,
25
+ "timeoutMs": 5000,
26
+ "phase": "visual"
27
+ },
28
+ "closeRequested": {
29
+ "event": "media_close_requested",
30
+ "required": true,
31
+ "phase": "intent"
32
+ },
33
+ "dismissed": {
34
+ "event": "media_dismissed",
35
+ "required": true,
36
+ "timeoutMs": 3000,
37
+ "phase": "completion"
38
+ }
39
+ },
40
+ "milestones": [
41
+ {
42
+ "id": "openRequested",
43
+ "event": "media_open_requested",
44
+ "description": "The app received the command to open media.",
45
+ "required": true,
46
+ "phase": "intent"
47
+ },
48
+ {
49
+ "id": "mediaReady",
50
+ "event": "media_visual_ready",
51
+ "description": "The media surface is visually ready.",
52
+ "required": true,
53
+ "timeoutMs": 5000,
54
+ "phase": "visual"
55
+ },
56
+ {
57
+ "id": "closeRequested",
58
+ "event": "media_close_requested",
59
+ "description": "The app received the command to dismiss media.",
60
+ "required": true,
61
+ "phase": "intent"
62
+ },
63
+ {
64
+ "id": "dismissed",
65
+ "event": "media_dismissed",
66
+ "description": "The media surface was dismissed cleanly.",
67
+ "required": true,
68
+ "timeoutMs": 3000,
69
+ "phase": "completion"
70
+ }
71
+ ],
72
+ "expectedEvents": [
73
+ "media_open_requested",
74
+ "media_visual_ready",
75
+ "media_close_requested",
76
+ "media_dismissed"
77
+ ],
78
+ "cycles": {
79
+ "iterations": 3,
80
+ "warmupIterations": 0,
81
+ "stopOnFailure": true
82
+ },
83
+ "budgets": [
84
+ {
85
+ "name": "media ready p95",
86
+ "source": "milestone",
87
+ "metric": "p95",
88
+ "unit": "ms",
89
+ "limit": 5000,
90
+ "fromMilestone": "openRequested",
91
+ "toMilestone": "mediaReady"
92
+ },
93
+ {
94
+ "name": "dismiss p95",
95
+ "source": "milestone",
96
+ "metric": "p95",
97
+ "unit": "ms",
98
+ "limit": 3000,
99
+ "fromMilestone": "closeRequested",
100
+ "toMilestone": "dismissed"
101
+ }
102
+ ],
103
+ "steps": [
104
+ {
105
+ "id": "launch",
106
+ "kind": "launch"
107
+ },
108
+ {
109
+ "id": "open-media",
110
+ "kind": "command",
111
+ "command": "activate-target:example-media-open"
112
+ },
113
+ {
114
+ "id": "wait-media-ready",
115
+ "kind": "waitForMilestone",
116
+ "milestone": "mediaReady",
117
+ "timeoutMs": 5000
118
+ },
119
+ {
120
+ "id": "capture-media",
121
+ "kind": "captureEvidence",
122
+ "artifact": "screenshot",
123
+ "required": false
124
+ },
125
+ {
126
+ "id": "close-media",
127
+ "kind": "command",
128
+ "command": "activate-target:example-media-close"
129
+ },
130
+ {
131
+ "id": "wait-dismissed",
132
+ "kind": "waitForMilestone",
133
+ "milestone": "dismissed",
134
+ "timeoutMs": 3000
135
+ }
136
+ ],
137
+ "artifacts": {
138
+ "required": ["logs", "signals"],
139
+ "optional": ["screenshot", "video", "memory", "network"]
140
+ }
141
+ }
@@ -0,0 +1,135 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "id": "open-close-cycle",
4
+ "flowId": "open-close-cycle",
5
+ "description": "Open and dismiss a target surface repeatedly using app-owned commands and truth milestones.",
6
+ "journey": {
7
+ "name": "Open and close target surface",
8
+ "intent": "Open a stable target surface, prove it rendered, dismiss it, and prove return to the prior state.",
9
+ "actor": "app user",
10
+ "startState": "home surface stable",
11
+ "endState": "target surface dismissed"
12
+ },
13
+ "platforms": ["ios", "android"],
14
+ "requiredCapabilities": ["launch", "sessionControl", "command", "logCapture", "artifactWrite"],
15
+ "optionalCapabilities": ["screenshot", "video", "uiTree"],
16
+ "truthEvents": {
17
+ "openRequested": {
18
+ "event": "surface_open_requested",
19
+ "required": true,
20
+ "phase": "intent"
21
+ },
22
+ "opened": {
23
+ "event": "surface_opened",
24
+ "required": true,
25
+ "timeoutMs": 2500,
26
+ "phase": "visual"
27
+ },
28
+ "closeRequested": {
29
+ "event": "surface_close_requested",
30
+ "required": true,
31
+ "phase": "intent"
32
+ },
33
+ "dismissed": {
34
+ "event": "surface_dismissed",
35
+ "required": true,
36
+ "timeoutMs": 2500,
37
+ "phase": "completion"
38
+ }
39
+ },
40
+ "milestones": [
41
+ {
42
+ "id": "openRequested",
43
+ "event": "surface_open_requested",
44
+ "description": "The app received the command to open the target surface.",
45
+ "required": true,
46
+ "phase": "intent"
47
+ },
48
+ {
49
+ "id": "opened",
50
+ "event": "surface_opened",
51
+ "description": "The target surface is visually ready.",
52
+ "required": true,
53
+ "timeoutMs": 2500,
54
+ "phase": "visual"
55
+ },
56
+ {
57
+ "id": "closeRequested",
58
+ "event": "surface_close_requested",
59
+ "description": "The app received the command to dismiss the target surface.",
60
+ "required": true,
61
+ "phase": "intent"
62
+ },
63
+ {
64
+ "id": "dismissed",
65
+ "event": "surface_dismissed",
66
+ "description": "The target surface was dismissed cleanly.",
67
+ "required": true,
68
+ "timeoutMs": 2500,
69
+ "phase": "completion"
70
+ }
71
+ ],
72
+ "expectedEvents": [
73
+ "surface_open_requested",
74
+ "surface_opened",
75
+ "surface_close_requested",
76
+ "surface_dismissed"
77
+ ],
78
+ "cycles": {
79
+ "iterations": 3,
80
+ "warmupIterations": 0,
81
+ "stopOnFailure": true
82
+ },
83
+ "budgets": [
84
+ {
85
+ "name": "open p95",
86
+ "source": "milestone",
87
+ "metric": "p95",
88
+ "unit": "ms",
89
+ "limit": 1200,
90
+ "fromMilestone": "openRequested",
91
+ "toMilestone": "opened"
92
+ },
93
+ {
94
+ "name": "close p95",
95
+ "source": "milestone",
96
+ "metric": "p95",
97
+ "unit": "ms",
98
+ "limit": 1200,
99
+ "fromMilestone": "closeRequested",
100
+ "toMilestone": "dismissed"
101
+ }
102
+ ],
103
+ "steps": [
104
+ {
105
+ "id": "launch",
106
+ "kind": "launch"
107
+ },
108
+ {
109
+ "id": "open-surface",
110
+ "kind": "command",
111
+ "command": "activate-target:example-surface-open"
112
+ },
113
+ {
114
+ "id": "wait-opened",
115
+ "kind": "waitForMilestone",
116
+ "milestone": "opened",
117
+ "timeoutMs": 2500
118
+ },
119
+ {
120
+ "id": "close-surface",
121
+ "kind": "command",
122
+ "command": "activate-target:example-surface-close"
123
+ },
124
+ {
125
+ "id": "wait-dismissed",
126
+ "kind": "waitForMilestone",
127
+ "milestone": "dismissed",
128
+ "timeoutMs": 2500
129
+ }
130
+ ],
131
+ "artifacts": {
132
+ "required": ["logs", "signals"],
133
+ "optional": ["screenshot", "video", "uiTree"]
134
+ }
135
+ }
@@ -0,0 +1,106 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "id": "scroll-settle",
4
+ "flowId": "scroll-settle",
5
+ "description": "Scroll a stable list and wait for app-owned visible/settled evidence.",
6
+ "journey": {
7
+ "name": "Scroll and settle list",
8
+ "intent": "Move a stable list and prove visible content reached a settled state.",
9
+ "actor": "app user",
10
+ "startState": "list visible and idle",
11
+ "endState": "list content settled after scroll"
12
+ },
13
+ "platforms": ["ios", "android"],
14
+ "requiredCapabilities": ["launch", "sessionControl", "command", "logCapture", "artifactWrite"],
15
+ "optionalCapabilities": ["screenshot", "video", "uiTree", "profiler"],
16
+ "truthEvents": {
17
+ "scrollStarted": {
18
+ "event": "list_scroll_started",
19
+ "required": true,
20
+ "phase": "intent"
21
+ },
22
+ "settled": {
23
+ "event": "list_scroll_settled",
24
+ "required": true,
25
+ "timeoutMs": 3000,
26
+ "phase": "visual"
27
+ }
28
+ },
29
+ "milestones": [
30
+ {
31
+ "id": "scrollStarted",
32
+ "event": "list_scroll_started",
33
+ "description": "The list scroll interaction started.",
34
+ "required": true,
35
+ "phase": "intent"
36
+ },
37
+ {
38
+ "id": "settled",
39
+ "event": "list_scroll_settled",
40
+ "description": "The app observed stable visible content after scrolling.",
41
+ "required": true,
42
+ "timeoutMs": 3000,
43
+ "phase": "visual"
44
+ }
45
+ ],
46
+ "expectedEvents": [
47
+ "list_scroll_started",
48
+ "list_scroll_settled"
49
+ ],
50
+ "cycles": {
51
+ "iterations": 3,
52
+ "warmupIterations": 0,
53
+ "stopOnFailure": true
54
+ },
55
+ "budgets": [
56
+ {
57
+ "name": "scroll settle p95",
58
+ "source": "milestone",
59
+ "metric": "p95",
60
+ "unit": "ms",
61
+ "limit": 1400,
62
+ "fromMilestone": "scrollStarted",
63
+ "toMilestone": "settled"
64
+ },
65
+ {
66
+ "name": "dropped frames",
67
+ "source": "artifact",
68
+ "metric": "droppedFrames",
69
+ "unit": "count",
70
+ "limit": 0,
71
+ "artifact": "profiler"
72
+ }
73
+ ],
74
+ "steps": [
75
+ {
76
+ "id": "launch",
77
+ "kind": "launch"
78
+ },
79
+ {
80
+ "id": "scroll-list",
81
+ "kind": "command",
82
+ "command": "scroll-by:600",
83
+ "adapterOptions": {
84
+ "gestureFallback": {
85
+ "direction": "down"
86
+ }
87
+ }
88
+ },
89
+ {
90
+ "id": "wait-settled",
91
+ "kind": "waitForMilestone",
92
+ "milestone": "settled",
93
+ "timeoutMs": 3000
94
+ },
95
+ {
96
+ "id": "capture-ui-tree",
97
+ "kind": "captureEvidence",
98
+ "artifact": "uiTree",
99
+ "required": false
100
+ }
101
+ ],
102
+ "artifacts": {
103
+ "required": ["logs", "signals"],
104
+ "optional": ["screenshot", "video", "uiTree", "profiler"]
105
+ }
106
+ }