ravensight-playtest 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ the reports and renders them.
7
7
  Simulation Runner adds seeded headless batches for mechanics, economy and
8
8
  progression checks through your own game adapter. It runs locally without
9
9
  model calls and saves bounded results to the same Playtest workspace. See
10
- [Simulation Runner](docs/SIMULATION.md) for the held candidate and setup.
10
+ [Simulation Runner](docs/SIMULATION.md) for setup.
11
11
 
12
12
  You need a Ravensight account and Node 22 or newer. You do not need an
13
13
  Anthropic API key: model calls go through Ravensight under the same token you
@@ -374,7 +374,10 @@ splitting the job in two is what gets everything played.
374
374
  | `--max-actions <n>` | Lower the actions one persona may take. It can only lower the persona's own patience (and the server's per run ceiling), never raise it. |
375
375
  | `--long` | A long persona run: twice the actions, model budget and wall time of a standard run, at twice the price. The price is read from the estimate and confirmed before any charge. |
376
376
  | `--concurrency <n>` | Personas at once. Default 2; `check` recommends a number for this machine. |
377
- | `--upload-video` | Upload `session.webm` as well as the reports. |
377
+ | `--controller <name>` | One playthrough using `standard` or an available System One controller. The server must advertise and quote that exact choice. |
378
+ | `--compare-with jev` | Separate agent/skill and Jev playthroughs for each chosen persona. Shows their combined quote before purchase. Comparisons run one playthrough at a time. |
379
+ | `--record-video` | Capture the selected web or graphical Godot game locally. Does not upload the video. |
380
+ | `--upload-video` | Record and explicitly upload `session.webm` or `session.mp4` with the reports. |
378
381
  | `--upload-transcript` | Upload `transcript.jsonl` as well as the reports. |
379
382
  | `--dry-upload` | List every file and byte that would be sent, and send nothing. |
380
383
  | `--key-modifier <meta\|control>` | The modifier the browser driver uses for editing shortcuts such as select all. Defaults to this machine's own (Command on macOS, Control elsewhere); set it when the browser's platform is not this machine's. `RAVENSIGHT_KEY_MODIFIER` does the same. |
@@ -780,7 +783,7 @@ to it. The short version:
780
783
 
781
784
  Uploaded by default: `report.md`, `report.json`, `usage.json`, `changeset.json`,
782
785
  the aggregate and capability reports, and `screenshots/`. Uploaded only when you ask:
783
- `transcript.jsonl` (`--upload-transcript`) and `session.webm`
786
+ `transcript.jsonl` (`--upload-transcript`) and `session.webm` or `session.mp4`
784
787
  (`--upload-video`). **Source code never, at any path.** The upload allowlist has
785
788
  no entry for it, the CLI refuses it locally, and the server refuses anything not
786
789
  on its own copy of the list. `src/upload/allowlist.js` is that copy, and a test
@@ -881,6 +884,59 @@ before registering a paid job. No provider key belongs in the CLI. Selection cos
881
884
  share the existing persona-run budget and do not create another wallet charge.
882
885
  Resume preserves the controller recorded in the job journal.
883
886
 
887
+ ### Recorded playthroughs and comparisons
888
+
889
+ The `0.5.0` candidate adds local recording and explicit comparisons. It requires a
890
+ matching server release for controller plans; an older server is refused before
891
+ purchase. Rune is recognized as a future controller but is unavailable until
892
+ provider access, pricing and acceptance are complete. These commands do not
893
+ activate graphical AWS hosting.
894
+
895
+ To keep one playthrough's video on your computer:
896
+
897
+ ```sh
898
+ ravensight-playtest run --driver playwright_web --build-url https://your-game.example/ --record-video
899
+ ```
900
+
901
+ To request two reports, one from the agent/skill controller and one from Jev:
902
+
903
+ ```sh
904
+ ravensight-playtest run --driver playwright_web --build-url https://your-game.example/ --personas curious-kid --compare-with jev --record-video
905
+ ```
906
+
907
+ The server quotes every persona/controller run before the CLI asks for payment.
908
+ Each keeps its own existing model budget and settlement rules. Two standard runs
909
+ at the current $1 unit price total $2; the displayed server quote is authoritative.
910
+ No comparison model call or extra recording fee is added. Use `--upload-video`
911
+ instead of `--record-video` only when you want the videos uploaded for private
912
+ dashboard playback. Recording is silent and captures only the selected game,
913
+ not your desktop. Browser popups and navigation to another origin are blocked
914
+ while recording.
915
+
916
+ Web recording produces WebM. Graphical Godot recording needs an existing FFmpeg
917
+ installation and produces MP4; a headless Godot run or missing encoder is reported
918
+ as unsupported. Capture is bounded to 15 minutes, 200 MiB of video, 1280 by 720
919
+ pixels and 10 frames per second for the Godot encoder. Godot raw frames have a
920
+ separate 256 MiB bound. This is evidence capture, not a promise of smooth video
921
+ at the game's original frame rate. Cancellation, crashes and limits can leave a
922
+ playable partial video; local `recording.json` records that outcome. Neither that
923
+ file nor raw frames are uploaded as artifacts.
924
+
925
+ With `--upload-video`, completion also sends a bounded capture status, duration
926
+ and stop reason. This is the runner's account of capture, separate from verified
927
+ upload or report success. Without video-upload consent, no capture status is sent.
928
+ Older recordings and later byte-only uploads can show status as unreported.
929
+ Malformed local status is omitted so it cannot prevent an existing report from
930
+ being completed.
931
+
932
+ Each controller starts a separate game session and produces its own report.
933
+ Saved progress and random events may still differ; this is not a guaranteed
934
+ identical-state benchmark. The aggregate compares evidence-bearing findings and
935
+ marks missing, failed or partial playthroughs as inconclusive. Agreement between
936
+ reports is evidence to review, not confirmation that a bug exists. In the
937
+ dashboard, choose a playthrough and its Recording tab; timestamp links refer only
938
+ to that run's uploaded video. Headless Simulation Runner has no rendered video.
939
+
884
940
  For Jump Bunny, use `https://reality-software-entertainment.github.io/poc-jump-bunny/`
885
941
  and the public player label **Ravensight Playtester**. This is a local browser run,
886
942
  not a hosted browser service. Human playtesting is still needed for subjective
@@ -892,7 +948,7 @@ Browser and Godot runners share the paid playtest lifecycle with desktop, Unity/
892
948
 
893
949
  ## Simulation Runner
894
950
 
895
- Available in held local candidate `0.4.0`; not yet published. Simulation Runner requires macOS or Linux (including WSL); native Windows is refused before purchase.
951
+ Available since `0.4.0`. Simulation Runner requires macOS or Linux (including WSL); native Windows is refused before purchase.
896
952
 
897
953
  Run seeded mechanics tests through a local game adapter, compare explicit strategies and replay selected traces. A batch uses the existing standard Playtest purchase of 5 AI units from prepaid balance, confirmed against the server quote. Only the summary is uploaded; game execution and replay traces stay local. It does not automatically call a model or replace testing the player interface.
898
954
 
@@ -1,6 +1,6 @@
1
1
  # Simulation Runner
2
2
 
3
- Held local candidate: `0.4.0`. This package has not been published; use a reviewed local tarball with the matching held server implementation.
3
+ Available since `0.4.0`, with the matching released Ravensight server.
4
4
 
5
5
  Simulation Runner exercises game mechanics through a developer-owned local adapter. It can run seeded games across explicit strategies, summarize outcomes and metrics, and save selected replay traces. Use it for questions such as whether an economy supports several viable strategies or whether a progression rule traps a simulated player. It does not judge animation, interface usability, feel or subjective fun.
6
6
 
@@ -0,0 +1,310 @@
1
+ # The Godot driver
2
+
3
+ `godot_driver` lets a playtest persona observe and control a native Godot game
4
+ by launching the developer's own Godot on their own machine. It has two halves:
5
+
6
+ - `addons/ravensight_driver/` (GDScript, Godot 4.2+): a TCP server that answers
7
+ JSON-RPC requests from inside the running game.
8
+ - `src/run/drivers/godot.js` (Node): starts Godot, speaks that protocol, turns
9
+ the scene tree into an observation, and writes screenshots.
10
+
11
+ Spec 07 is the contract for both halves. This document is how the two sides fit
12
+ together and what the driver is and is not able to do.
13
+
14
+ ## The driver never reaches players
15
+
16
+ The addon refuses to start unless every one of these holds:
17
+
18
+ 1. The build is not an export template, **or** it was exported with the custom
19
+ feature tag `ravensight_driver`. A normal release export never activates it.
20
+ 2. **And** either `--ravensight-driver` is in the user args (after the `--`
21
+ separator) or `RAVENSIGHT_DRIVER=1` is set.
22
+ 3. It binds `127.0.0.1` only. There is no code path that binds any other
23
+ address, and the Node client refuses to dial a non loopback host.
24
+ 4. If `RAVENSIGHT_DRIVER_TOKEN` is set, the first request on a connection must
25
+ be `hello{token}`. A wrong token gets `-32002` and the connection closes.
26
+ 5. One client at a time. A second connection is answered with `-32001 busy` and
27
+ hung up on.
28
+
29
+ When any of that fails the autoload calls `queue_free()` on itself in
30
+ `_ready()`, so an inactive driver is not a node that idles in `_process`, it is
31
+ not in the tree at all.
32
+
33
+ On top of refusing to run, `export_plugin.gd` **strips the whole addon** from
34
+ any export whose preset was not built with the `ravensight_driver` feature tag,
35
+ so the usual release build does not contain the driver's code.
36
+
37
+ One file does not simply vanish. A developer who installs the addon in their own
38
+ project gets `RavensightDriver` written into `project.godot`, and `project.godot`
39
+ ships in the export. Deleting `driver.gd` outright would leave that autoload
40
+ pointing at a file that is not there, and the exported game would greet the
41
+ player with a load error before its first frame. So the strip leaves a four line
42
+ inert stub at `driver.gd`'s path: `extends Node` and a `_ready()` that calls
43
+ `queue_free()`. The autoload resolves, nothing listens, and no driver code
44
+ ships. The alternative, failing the export with a message, was rejected because
45
+ it punishes a developer for having installed the addon at all.
46
+
47
+ This is covered by a test, not an assertion of intent: `godot-real.test.js`
48
+ exports the same project twice with `--export-pack` (which needs no export
49
+ templates) and checks that the plain pack contains the stub and none of the
50
+ driver's source, while a pack built with the feature tag contains all of it.
51
+
52
+ ## How the addon gets into the project
53
+
54
+ The CLI never writes to the developer's project. `prepareProjectCopy` copies the
55
+ project to a temp directory (mode `0700`, `.git` and `node_modules` excluded,
56
+ refused above 2 GB), drops the addon into `addons/ravensight_driver/` in the
57
+ copy, and registers the autoload **twice** in that copy:
58
+
59
+ - in `override.cfg`, which is the mechanism spec 07 describes, and
60
+ - in the copy's own `project.godot`.
61
+
62
+ Spec 07's open question 1 asks whether an `override.cfg` autoload works for
63
+ every project layout. Rather than leave that unanswered at runtime, both are
64
+ written. They name the same script, so whichever Godot honors, the result is
65
+ identical. A project that already ships an `override.cfg` or already registers
66
+ other autoloads keeps them: only the one `RavensightDriver` key is added.
67
+
68
+ **Symlinks are the trap here.** `project.godot`, `override.cfg` and `addons/`
69
+ can each be a symlink pointing outside the project, which is what a developer
70
+ gets from a shared settings repo or a vendored addons checkout. Writing to a
71
+ symlink writes to its target, so a naive copy would put the injection into the
72
+ developer's real files, permanently, breaking the one promise spec 07 makes
73
+ about the project directory. Three things prevent it:
74
+
75
+ - the copy runs with `dereference: true`, so a symlink becomes a real file or
76
+ directory in the copy rather than a pointer back out of it;
77
+ - every path this module writes to is materialized first, component by
78
+ component, replacing any surviving link with a real copy of its content;
79
+ - `measureProject` follows symlinked directories when it sizes the project,
80
+ because sizing that ignored them reported a few hundred bytes for a tree the
81
+ copy then dereferenced into gigabytes, and the 2 GB cap did nothing.
82
+
83
+ Dereferencing brings its own failure mode, so the copy filter drops two shapes
84
+ outright. A link resolving to the project directory or any ancestor of it
85
+ (`selfref -> .`, `up -> ..`) is dropped: dereferenced, the copy walks the project
86
+ into itself, finds the link again, and repeats until `PATH_MAX`, which one such
87
+ link turned into 33 nested copies. And a directory already dereferenced once is
88
+ not dereferenced again, because `measureProject` counts a shared target once and
89
+ the two have to agree or the cap stops meaning anything.
90
+
91
+ Both rules compare **realpaths**, not `path.resolve()` output. On macOS
92
+ `os.tmpdir()` is `/var/folders/...`, a symlink to `/private/var/folders/...`, so
93
+ a project living there never matches its own symlink target unless both sides are
94
+ fully resolved, and the rule quietly does nothing. That is a test that caught it,
95
+ not a hypothetical.
96
+
97
+ A failed `prepareProjectCopy` deletes the temp root it created, so a project that
98
+ cannot be prepared does not leave a half written copy on disk. A `tempRoot` the
99
+ caller passed in is left alone: it is theirs.
100
+
101
+ A dangling symlink is dropped from the copy instead of failing the launch:
102
+ `fs.cp` throws on one it cannot dereference, and one broken link is not a reason
103
+ a project cannot be playtested.
104
+
105
+ ## Launch
106
+
107
+ ```
108
+ <godot> --path <temp copy> --resolution 1280x720 -- --ravensight-driver --ravensight-driver-port=<port>
109
+ ```
110
+
111
+ The binary is found the way spec 17 describes: `--godot-path`, then `$GODOT`,
112
+ then `PATH` (`godot`, `godot4`), then the platform's usual install locations.
113
+ When the project declares an engine version in `config/features`, a binary with
114
+ that major.minor wins over a newer one. Anything below 4.2 is refused.
115
+
116
+ The environment matters as much as the arguments:
117
+
118
+ | Variable | Read by | Why |
119
+ |---|---|---|
120
+ | `RAVENSIGHT_DRIVER=1` | the addon | activation |
121
+ | `RAVENSIGHT_DRIVER_PORT` | the addon | loopback port, default 47800 |
122
+ | `RAVENSIGHT_DRIVER_TOKEN` | the addon | per run handshake secret |
123
+ | `RAVENSIGHT_PLAYTEST_TOKEN` | the game's own `Ravensight.gd` | playtest session auth |
124
+ | `RAVENSIGHT_PLAYTEST_RUN_ID` | the game's own `Ravensight.gd` | `pt_run` tag, throwaway device id |
125
+ | `RAVENSIGHT_PLAYTEST_JOB_ID` | the game's own `Ravensight.gd` | `pt_job` tag |
126
+ | `RAVENSIGHT_PLAYTEST_PERSONA` | the game's own `Ravensight.gd` | `persona` tag |
127
+
128
+ Only those, plus a fixed allowlist of variables a process needs to start at all
129
+ (`PATH`, `HOME`, the `XDG_*`, `DISPLAY`, `LD_LIBRARY_PATH` and
130
+ `LIBGL_ALWAYS_SOFTWARE` set that the Xvfb path needs, the Windows equivalents),
131
+ reach Godot. The environment is **not** inherited wholesale: the game is the
132
+ customer's own code running on the developer's machine, and it has no business
133
+ seeing `ANTHROPIC_API_KEY`, `RAVENSIGHT_TOKEN`, cloud credentials or CI secrets.
134
+ `ENV_ALLOWLIST` is the list, and `extraEnv` is the deliberate escape hatch for a
135
+ caller that needs one more variable through.
136
+
137
+ The last four are the whole reason a driven run shows up correctly in the
138
+ dashboard: the SDK reads them in `_ready()` and every event the game already
139
+ tracks gets `synthetic: true`, `persona`, `pt_job`, `pt_run` and `pt_tier`
140
+ without the developer writing anything. The driver sets them and otherwise
141
+ stays out of the SDK's way. When the SDK autoload is present the driver also
142
+ connects to its `event_tracked` signal and mirrors each tracked event to the
143
+ runner as a `ravensight_event` notification.
144
+
145
+ The runner then dials `127.0.0.1`, retrying every 250 ms for up to 60 s, sends
146
+ `hello`, and calls `ping`. Godot's stdout and stderr go to `<run dir>/godot.log`
147
+ and are scanned for `SCRIPT ERROR` / `ERROR:` lines, which `consoleErrors()`
148
+ returns with the `at:` source line attached. That is spec 07's stdout fallback:
149
+ the engine's `Logger` hook is not available on every 4.x version the CLI
150
+ supports, so it is not used.
151
+
152
+ ## The driver interface
153
+
154
+ `src/run/drivers/driver.js` does not exist on this branch, so `godot.js` writes
155
+ the five method shape out in its own header instead of importing it:
156
+
157
+ ```js
158
+ launch({ recordVideo, viewport }) // -> {}
159
+ observe({ vision }) // -> Observation
160
+ act(action) // -> { ok, error?, code?, data?, changed }
161
+ screenshot(name) // -> { path } | { path: null, skipped }
162
+ stop({ reason }) // -> { videoPath, recording, frameCount, chapters, logPath, exitCode }
163
+ ```
164
+
165
+ plus the two optional extras spec 03 lists, `consoleErrors()` and
166
+ `chapter(title)`.
167
+
168
+ `act` maps the spec 03 action kinds onto driver methods:
169
+
170
+ | Action | Driver calls |
171
+ |---|---|
172
+ | `key` | `key` press, `wait_frames{1}`, `key` release |
173
+ | `type` | one press and release per character, each carrying its unicode, then Enter when `submit` |
174
+ | `click` with a string target | `click{node_path}` (the center of the Control's global rect) |
175
+ | `click` with coordinates | `click{x,y}` |
176
+ | `press_action` | `press_action{action, duration_ms}` |
177
+ | `wait` | `wait_frames`, milliseconds converted at 60 fps |
178
+ | `set_time_scale` | `set_time_scale` |
179
+ | `quit` | `quit` |
180
+
181
+ `changed` compares the hash of the scene tree outline before and after the
182
+ action. Two details make that answer honest:
183
+
184
+ - input reaches the game a frame after it is parsed, and a handler that updates
185
+ a label runs a frame after that, so `act` waits `settleFrames` (default 2)
186
+ before taking the comparison snapshot;
187
+ - `observe()` always takes its own `get_state`. An earlier version reused the
188
+ snapshot `act()` had just taken, to save a round trip, and that handed the
189
+ persona a screen one action out of date, which is the opposite of what
190
+ `observe()` is for. Two `get_state` calls a turn is the price of the
191
+ observation being current.
192
+
193
+ A driver level error is returned, not thrown: an undefined input action comes
194
+ back as `{ ok: false, code: -32012, data: { available_actions } }` so the
195
+ persona can pick a real one instead of the run ending.
196
+
197
+ ## Observations cite files
198
+
199
+ Every node in a `get_state` snapshot carries its `path`, and also its `scene`
200
+ (`scene_file_path`) and `script` (the script's `resource_path`) when it has
201
+ them. `buildObservation` collects all of those and puts them at the top of the
202
+ observation text:
203
+
204
+ ```
205
+ scene: res://main.tscn
206
+ scene node: /root/Main
207
+ scripts: res://ravensight_stub.gd, res://main.gd
208
+
209
+ Window root
210
+ Node2D Main @(0,0) [groups=ravensight_state] {res://main.tscn} <res://main.gd>
211
+ CanvasLayer HUD
212
+ Label TrustLabel "Trust: 4" @(24,16)
213
+ Button BeginButton "Begin" @(600,400) [focus]
214
+
215
+ game state:
216
+ /root/Main: {"phase":"HEALING","trust":4}
217
+ ```
218
+
219
+ That is what lets a finding say which scene and which script to open. The
220
+ driver's own autoload is filtered out of the snapshot, so its script path never
221
+ appears in that list.
222
+
223
+ Subtrees with nothing a player could see or press are dropped, the outline is
224
+ capped at 12k characters, and `needsVision()` asks for a screenshot when the
225
+ remaining outline is under 200 characters, which is the canvas drawn or 3D case.
226
+
227
+ Spec 07's `extensions/navigation.gd` (`nav_sample`, `nav_path`, `teleport`,
228
+ `raycast`) is **not** part of this addon: those methods exist for spec 06 level
229
+ probing, not for a persona run, and they can be added later through
230
+ `register_method()` without touching the protocol. `list_actions` is the one
231
+ extension method implemented here, because a persona that gets `-32012` needs to
232
+ know what it may press instead.
233
+
234
+ The author hook is unchanged from the spec: a node in the `ravensight_state`
235
+ group with a `_ravensight_state() -> Dictionary` method. Vectors become arrays,
236
+ recursion stops at 8 levels, each result is capped at 64 KB, and a hook that
237
+ returns something other than a Dictionary gets `-32020` naming the node path.
238
+ A broken hook is recorded as a console error against the game, not treated as a
239
+ driver failure.
240
+
241
+ ## Headless is for smoke tests, not for playtests
242
+
243
+ Verified against Godot on macOS: in `--headless` mode the engine routes no
244
+ synthesized mouse or GUI input at all. A `click` on a Button whose rect is right
245
+ there does nothing, and `screenshot` returns `-32010` because there is no
246
+ viewport. The same clicks work windowed.
247
+
248
+ So `--headless` is only useful for a game driven entirely by InputMap actions
249
+ that manages its own focus. `launch()` pushes a console entry saying so when it
250
+ detects a headless run, and the local CLI default stays windowed.
251
+
252
+ `--record-video` opts into local capture of this game's viewport; it does not
253
+ consent to upload. `--upload-video` still enables both recording and optional
254
+ video upload. Audio capture is off. Godot samples up to ten frames per second,
255
+ records their actual timestamps, and uses optional local FFmpeg to create
256
+ `session.mp4`. Set `RAVENSIGHT_FFMPEG` to an existing executable when it is not
257
+ on PATH. No package or encoder is downloaded automatically.
258
+
259
+ Recording is capped at 15 minutes, 1280 by 720 pixels, 256 MiB of temporary
260
+ frames and the existing 200 MiB final video limit. Temporary frames are removed
261
+ after encoding. A missing encoder or headless viewport is reported as
262
+ `unsupported` in local `recording.json`; frames are never labeled as video.
263
+ Cancellation, a game crash, or a recording limit can produce a playable partial
264
+ video, explicitly marked with its stop reason. Encoding failure produces no
265
+ uploadable video. Normal stop and concurrent cancellation share one bounded
266
+ finalization. A hard kill of the CLI or machine cannot guarantee finalization.
267
+
268
+ `recording.json` stays local and includes timestamps, duration, size, status,
269
+ reason and `audio: false`. When optional FFprobe is available, duration is read
270
+ from the actual silent media stream; otherwise Godot uses frame timestamps and
271
+ Playwright uses capture clock time, labeled by `durationSource`. This metadata
272
+ is not part of the upload allowlist. Video upload remains separately opted in,
273
+ with the existing per-run limits and server retention unchanged.
274
+
275
+ ## Verifying a change
276
+
277
+ The GDScript is parse checked with a real Godot binary, which is the only thing
278
+ that catches an undeclared identifier:
279
+
280
+ ```bash
281
+ /Users/.../game_tracker/scripts/check-gdscript.sh \
282
+ /Applications/Godot.app/Contents/MacOS/Godot \
283
+ addons/ravensight_driver/driver.gd \
284
+ addons/ravensight_driver/plugin.gd \
285
+ addons/ravensight_driver/export_plugin.gd
286
+ ```
287
+
288
+ It works on this standalone package: `--check-only --script` needs no
289
+ `project.godot`, and `res://` resolves against the working directory, so
290
+ `plugin.gd`'s `preload` of `export_plugin.gd` is checked too. Run it from the
291
+ package root.
292
+
293
+ Most Node tests (`npm test`) run against `testing/fake-godot.js`, a fake driver
294
+ socket that speaks the same wire protocol and enforces the same single client,
295
+ hello before anything, and keycode resolution rules. No Godot binary is involved
296
+ there, so those tests prove the Node side's shapes and nothing about the
297
+ GDScript.
298
+
299
+ `godot-real.test.js` covers what only a real engine can: the activation matrix
300
+ (nothing listens without the flag, a wrong token is refused, a second client is
301
+ refused, an unknown method and a bad param get the right codes), the author hook,
302
+ typing a space, and the export strip. It drives the fixture project at
303
+ `test/fixtures/godot-project/` and **skips itself** when no Godot 4.2+ binary is
304
+ found, the same way the Ravensight repo's ClickHouse integration tests skip
305
+ without a server. A skip there is expected, not a failure to fix, but it does
306
+ mean a change to `driver.gd` is unverified until it has run on a machine with
307
+ Godot installed.
308
+
309
+ The fixture is never written to: every case copies it to a temp directory first,
310
+ so `--import` and `--export-pack` leave no `.godot/` or `out/` behind.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ravensight-playtest",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Local AI playtests and seeded game simulations with reports in Ravensight.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://ravensight.io/docs/#playtest",
@@ -29,6 +29,7 @@
29
29
  "addons",
30
30
  "docs/ADAPTERS.md",
31
31
  "docs/NATIVE_RUNNER.md",
32
+ "docs/godot-driver.md",
32
33
  "docs/SIMULATION.md",
33
34
  "examples/simulation",
34
35
  "README.md",
package/src/api/client.js CHANGED
@@ -194,9 +194,9 @@ export function createClient(options = {}) {
194
194
  * persona run's price; anything else leaves `run_length` out, which the
195
195
  * server reads as a standard run.
196
196
  */
197
- estimate: (gameId, { modules = [], personas = [], runLength = null, simulation } = {}) =>
197
+ estimate: (gameId, { modules = [], personas = [], runLength = null, simulation, controllers, driver } = {}) =>
198
198
  request('POST', `${gameBase(gameId)}/estimate`, {
199
- body: { modules, personas, ...(runLength === 'long' ? { run_length: 'long' } : {}), ...(simulation ? { simulation } : {}) }
199
+ body: { modules, personas, ...(controllers ? { controllers, driver } : {}), ...(runLength === 'long' ? { run_length: 'long' } : {}), ...(simulation ? { simulation } : {}) }
200
200
  }).then(answer => answer.body),
201
201
 
202
202
  /**
package/src/cli.js CHANGED
@@ -54,7 +54,9 @@ export const RUNNER_HELP = `
54
54
  Run options (parsed by the runner, see src/run/args.js):
55
55
  --personas <list> comma separated persona slugs, default one (curious-kid); "personas" lists them
56
56
  --driver <name> playwright_web, godot_driver, desktop_driver, unity_driver, unreal_driver, android_driver, ios_driver or cli_stdio
57
- --controller <name> standard or jev action selection (default standard)
57
+ --controller <name> one controller: standard, jev or rune (if enabled)
58
+ --compare-with <names> separate standard + jev and/or rune playthroughs; quotes every run
59
+ --record-video record this game locally; --upload-video also uploads it
58
60
  --build-url <url> the web build to play
59
61
  --godot-project <dir> the Godot project directory
60
62
  --driver-config <file> local WebDriver or JSON-RPC bridge configuration