verikun 0.25.1 → 0.26.0-rc.2

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.
@@ -397,7 +397,8 @@ vk ai onboarding.md --timeout 5m # tighten the run timeout (default 15m)
397
397
  (`--max-cost-usd`) or the wall-clock passes **15m** (`--timeout`), so a runaway
398
398
  compile/repair loop can't spend or hang without limit. The ceiling is **per test, not per
399
399
  suite** — `vk suite` gives each `*.md` its own fresh budget, so 20 tests at the default can
400
- reach $60; there is no suite-wide cap, so lower the per-test figure instead. A model is only
400
+ reach $60; lower the per-test figure, or set `vk suite --max-suite-cost-usd <n>` for an
401
+ aggregate cap (off by default; stops the suite at exit `1`). A model is only
401
402
  ever called to **compile** (once, on a cache miss) or to **repair** (≤3 per failing step);
402
403
  replay is always $0, and every non-`ai` command is $0 always. Full mechanism, the estimate
403
404
  formula and the cache multipliers: <https://ddikman.github.io/verikun/reference/cost/>.
@@ -437,6 +438,28 @@ vk suite tests/ --app com.example.app # reset app data between tests
437
438
  are **not** counted as failures. So `3` = fix the machine and rerun; `1` = a real
438
439
  regression to investigate.
439
440
 
441
+ ### Across several devices
442
+
443
+ Suite time is the **sum** of its tests on one device. Given a pool, the suite becomes a
444
+ work queue instead — every device takes the next test as it frees up:
445
+
446
+ ```sh
447
+ vk suite tests/ --app com.example.app --devices emulator-5554,emulator-5556
448
+ vk suite tests/ --app com.example.app --server "$VERIKUN_SERVER" # a pooled server
449
+ vk suite tests/ --app com.example.app --servers http://a:8391,http://b:8391
450
+ ```
451
+
452
+ - A `vk server --devices all` holds several devices behind one URL; a plain `--server`
453
+ suite reads its capacity and sizes itself. Nothing else about the command changes.
454
+ - **File order stops sequencing tests** — they must be independent. Ordering becomes
455
+ longest-first, learned from the previous run's `index.json`.
456
+ - Each row records **which device ran it**, and the manifest splits
457
+ `totals.wallClockMs` (how long the gate took) from `totals.durationMs` (device-seconds).
458
+ - `--concurrency N` caps how many run at once — more devices on one host can thrash it.
459
+ `--max-suite-cost-usd N` stops the suite once total model spend crosses it (exit `1`).
460
+ - A device that breaks retires; its tests move to the others. Exit `3` only when all are gone.
461
+ - `--ensure-device` is refused with `--devices` — start the pool with `vk devices start`.
462
+
440
463
  ## Drive a remote device (--server)
441
464
 
442
465
  If the device is attached to another machine running `vk server`, point
@@ -450,11 +473,23 @@ vk install ./app-debug.apk --server "$VERIKUN_SERVER" # server needs --allow-i
450
473
  vk suite tests/ --app com.example.app --server "$VERIKUN_SERVER"
451
474
  ```
452
475
 
453
- A wrong URL/key fails fast with exit 3; `409` means another run holds the
454
- device; `503` means the server has no device attached — boot one (below). To
455
- expose a device from THIS machine: `vk server --allow-install`
476
+ A wrong URL/key fails fast with exit 3; `409` means every device is already
477
+ leased by another run; `503` means the server has no device attached — boot one
478
+ (below). To expose a device from THIS machine: `vk server --allow-install`
456
479
  (add `--bind <addr>` to leave loopback; auth key auto-generates if unset).
457
480
 
481
+ **`vk install` on Android replaces a build signed by a different key.** Android refuses
482
+ to update a package across signing keys, so verikun removes the installed build and
483
+ installs again, warning on stderr that **that build's app data is gone**. A same-key
484
+ install still keeps its data. Do not treat the warning as a failure — it is how the
485
+ install succeeded. iOS has no equivalent recovery; the install just fails there.
486
+
487
+ `vk server --devices all` (or `all-android` / `all-ios` / a serial list) serves a **pool**
488
+ from one address. Each run leases one device for its whole life, so a run's steps and
489
+ repairs always land on the same phone. `vk install --server` then installs on every device;
490
+ `vk devices start|restart|stop --server` is refused (`403`) — a pool has no single device
491
+ to act on.
492
+
458
493
  **If you see `[verikun] server moved device: A → B` on stderr**, the server left a
459
494
  device that failed and is now on another one. What that means depends on the line:
460
495
 
@@ -466,7 +501,9 @@ device that failed and is now on another one. What that means depends on the lin
466
501
  flow again from the top if you want it on B.
467
502
 
468
503
  The server rules the bad device out until it is power-cycled;
469
- `vk devices --server <url>` shows why in its `NOTE` column.
504
+ `vk devices --server <url>` shows why in its `NOTE` column. On a pool the replacement
505
+ joins the pool, so capacity holds — and the last device is never shed, so its own error
506
+ keeps reaching you rather than a bare "no device attached".
470
507
 
471
508
  ## The device is missing or wedged
472
509
 
package/CHANGELOG.md CHANGED
@@ -6,6 +6,62 @@ All notable changes to this project are documented here. The format is based on
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Changed
10
+ - **`vk install`** (Android) removes a differently-signed build of the same package and retries,
11
+ instead of failing. Its app data is lost; stderr says so. ([#96])
12
+ - **`VERIKUN_CLAIM_TTL_MIN`** now also paces a parallel suite's claim heartbeat — a quarter of
13
+ the window, capped at 60s — so a short TTL no longer races it.
14
+
15
+ [#96]: https://github.com/ddikman/verikun/issues/96
16
+
17
+ ## [0.26.0] - 2026-08-24
18
+
19
+ ### Added
20
+ - **`vk suite --devices a,b` / `--servers u1,u2`**: run tests across a device pool, next free
21
+ device takes the next test. One merged report. ([#39])
22
+ - **`vk suite --server`**: sizes itself automatically from a pooled server's capacity — one URL,
23
+ one secret, unchanged CI line. ([#39])
24
+ - **`vk server --devices all | all-android | all-ios | a,b`**: serve several devices from one
25
+ address, one lease per run. ([#39])
26
+ - **`vk suite --devices all | all-android | all-ios`**: same spelling as `vk server --devices`. ([#39])
27
+ - **`vk suite --concurrency n`**: cap how many devices run at once, below the pool's size. ([#39])
28
+ - **`vk suite --max-suite-cost-usd n`**: stop the suite once total model spend crosses it
29
+ (exit `1`). Off by default. ([#39])
30
+ - **`vk ai --reset-app <id>`**: clear (iOS: force-stop) the app before the first step, on the run's
31
+ own device.
32
+ - **`VERIKUN_LANE`**: moves the active run to `./.verikun/run-<lane>/` so concurrent tests in one
33
+ working directory don't delete each other's state.
34
+ - **`POST /v1/lease`** and `capacity` / `devices` on `/v1/health`: which device a run token holds,
35
+ and how many the server has.
36
+
37
+ ### Changed
38
+ - **Suite manifest**: adds `totals.wallClockMs`, `concurrency` and a per-test `device`.
39
+ `totals.durationMs` is unchanged but is now labelled device time. `schemaVersion` stays `1`.
40
+ - **`vk install --server`** installs on **every** device of a pooled server, not one.
41
+ - **Release workflow**: a prerelease tag takes its GitHub release notes from the version it is a candidate for (`v1.0.0-rc.1` → `## [1.0.0]`).
42
+ - **`vk suite --devices`**: file order no longer sequences tests, and longest-first ordering is
43
+ taken from the previous run's manifest. A serial suite is unchanged. ([#39])
44
+ - **`vk server --devices`**: `/v1/devices/{start,restart,stop}` answer `403` on a pool — there is
45
+ no single device to act on. `--ensure-device` is likewise refused with `--devices`.
46
+ - **`vk suite --devices` with `--server`** is a usage error (exit `2`): it would have tested local
47
+ devices and reported green.
48
+ - **`vk suite --servers u1,u2`**: opens one lane per device each server has, not one per URL.
49
+ - **`vk server`**: a lease is broken only when another run needs that device, so a paused run
50
+ keeps its own phone.
51
+ - **`--json` errors** carry `errorKind`, the error's class, so a caller need not match on message
52
+ text.
53
+ - **`vk server --devices all`** and failover prefer virtual devices, so neither enlists an attached
54
+ phone unasked.
55
+ - **`vk server` failover on a pool**: a failed device is quarantined and a healthy one takes its
56
+ place, so capacity holds. ([#39], [#99])
57
+
58
+ ### Fixed
59
+ - **`--server` runs no longer fail with `fetch failed`** when a step follows a pause longer
60
+ than 5s, such as a cold compile.
61
+ - **`--server` screenshots and failure evidence** are no longer corrupted in the archived report.
62
+
63
+ [#39]: https://github.com/ddikman/verikun/issues/39
64
+
9
65
  ## [0.25.1] - 2026-08-21
10
66
 
11
67
  ### Changed
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  - **Puppeteer for native mobile** — a thin wrapper over native Android and iOS automation runners with zero runtime dependencies.
9
9
  - **Natural-language tests** — `vk ai <file>`: runs plain-English tests, compiled once and replayed model-free (~$0), calling a model only to self-heal a drifted step. [What that costs](https://ddikman.github.io/verikun/reference/cost/), and how the `--max-cost-usd` ceiling bounds it.
10
10
  - **Self-improving** — the agent runner will provide prescriptive improvements to existing scripts to help stabilise flakiness for future runs.
11
- - **CI-ready** — `vk suite` runs a folder of tests as one gated pass/fail run; `vk server` exposes a real device over an authenticated tunnel so a disposable CI runner (no phone attached) can still drive it, and moves to another attached device if that one goes bad.
11
+ - **CI-ready** — `vk suite` runs a folder of tests as one gated pass/fail run, across one device or a whole pool of them; `vk server` exposes real devices over an authenticated tunnel so a disposable CI runner (no phone attached) can still drive them, and swaps out any that goes bad.
12
12
 
13
13
  ```
14
14
  $ vk ui
@@ -170,6 +170,13 @@ function createRemoteBackend(opts, health) {
170
170
  };
171
171
  return {
172
172
  exec: (command, positionals, flags) => execRaw({ command, positionals, flags }, true),
173
+ async lease() {
174
+ // Feature-detect on a FIELD, never on the version: `capacity` and /v1/lease landed
175
+ // together, and a client cannot otherwise tell "old server" from "new server".
176
+ if (health.capacity === undefined)
177
+ return null;
178
+ return t.postJson('/v1/lease', {}, HEALTH_TIMEOUT_MS);
179
+ },
173
180
  async getElements() {
174
181
  const res = await t.postJson('/v1/elements', {}, ELEMENTS_TIMEOUT_MS);
175
182
  return res.elements;