Skip to content

Release 0.8.0 - #84

Merged
addisdev merged 72 commits into
mainfrom
develop
Oct 15, 2026
Merged

addisdev merged 72 commits into
mainfrom
develop

Conversation

@addisdev

@addisdev addisdev commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Promotes develop to main for v0.8.0. After this merges, tag v0.8.0 on the merge commit to run release.yml.

See CHANGELOG.md.

addisdev and others added 30 commits October 2, 2026 12:05
The shelf's seven silhouettes become 22 illustrations: iPhone, iPhone SE,
Galaxy, Android phone, iPad, Android tablet, Roku box and stick, Apple TV,
Fire TV Stick, Chromecast, smart TV, MacBook, PC laptop, desktop, server,
Apple Watch, Wear OS watch, headset, board, browser, and a fallback.

The drawing is chosen from the model string first. The fleet's older agents
report a Roku as android and an Apple TV or Apple Watch as ios, so platform
alone drew all three as phones. A self-declared kind still picks the family;
the model picks the member.

Status is unchanged: the screen is green with a pulse online, amber stale,
dark offline, breathing while a job runs, with a dashed outline for a
simulator. Colours are theme tokens, so light and dark both work.

scripts/check-device-art.ts runs in npm test with the live fleet's model
strings as fixtures, and fails if any drawing becomes unreachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dashboard: draw each device as the object it is
Shapes for the explore workload (EXPANSION-PLAN's 'monkey test with a
memory'), and a focused/scrollable flag on tree nodes for TV driving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A job that sets params.androidtv { package } also runs the adb targets that
declare leanback (AndroidActuator.isTv). Each TV gets a generated tvloop.toml
naming it as an androidtv device, since a bare --device is taken to be a Roku
address. Only doctor and replay run there: tvloop's install builds a Roku zip,
and the spike and hardware suite are the Roku adapter's. TVs need no Roku
password, so a host without one still runs its TVs; a job that is all Rokus
behaves exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ay, filing

A vision model works through mission cards on a device; the harness snaps
taps onto real elements, refuses dangerous controls by what the tree says
they are, checks every step (crash/ANR logs, blank, frozen, dead controls,
accessibility, a separate visual judge) and replays each candidate on a
clean install before posting it to /findings. Speaks Holo4's published
mobile_* tool dialect; tv_press for D-pad screens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RokuActuator loads tvloop's own Roku adapter from the host's checkout
(FLEET_TVLOOP_DIR, then ~/tvloop) and connects with the developer password as
an argument, so the password never enters the executor's environment the way
openDevice's in-process daemon would need. Keys only; taps and swipes are
refused by name. observe() re-encodes the JPEG screenshot as PNG, maps the
agent's tree to A11yNodes in screenshot pixels and writes a focus line from
tvloop's focus chain; crashes() counts tvloop's one-event crashes from the
debug console since the previous call.

replay-tvloop.ts turns a trajectory's key presses and waits into a tvloop
flow (JSON), ending in assert noErrors, and runs one through tvloop replay
against a generated one-device config. A crash finding reproduces when that
assertion fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…evice

56 checks: coordinate mapping, escaping, snapping and the leash, screen
identity, fingerprints, the Maestro file, and one mission against a fake
app and a scripted fake model (dead control, refused delete, crash
replayed twice). The picture hash now sees horizontal changes and ignores
the system bars; 'nothing changed' also compares the tree's state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nner (B4), bake-off (B5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One XCUITest bundle, built for iOS (FleetDriverUITests) and tvOS
(FleetDriverUITestsTV) from runner-ios/FleetDriver/. With FLEET_DRIVER=1 it
starts an NWListener HTTP/1.1 server inside the test runner and serves
/health, /launch, /activate, /terminate, /screenshot, /tree (snapshot-based,
debugDescription on request or as fallback), /foreground, /tap, /long_press,
/swipe, /type (with clear), /hide_keyboard, /press and /quit until told to stop
or idle for FLEET_DRIVER_IDLE_S. Commands run one at a time on the main thread
from the test's own loop, never re-entrantly; XCUITest's recorded issues become
the command's error instead of ending the session. Loopback-only on a
simulator, optional token on every request.

Neither target depends on an app, so a run never replaces the FleetRunner agent
installed on a real device, and the FleetDriver/FleetDriverTV schemes build the
bundle alone. The FleetRunner scheme's test action is unchanged.

Also the a11y dump helper executor.ts asked for: FleetSmokeUITests prints the
app's debugDescription between FLEET-A11Y-DUMP-BEGIN/END when
FLEET_A11Y_DUMP=1, and the executor's error now says what a missing dump means.
Regenerating the project also picks up MARKETING_VERSION 0.7.2, which the
committed pbxproj had left at 0.5.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collector/src/workloads/explore/actuators/apple.ts builds the driver once per
host into ~/.fleet/explore/derived/<platform>-<sim|device> (stamped with a hash
of its sources and the Xcode version), starts it with xcodebuild
test-without-building, polls /health, and implements the Actuator contract:
observe (screenshot, snapshot tree scaled to pixels, foreground, tvOS focus
line, keyboard), act (every Action kind; pixels to points; scroll as a centred
swipe on touch and D-pad presses on a TV), reset (reinstall from a file, or
empty the data and group containers and cfprefsd's copy on a simulator),
crashes (parseSimCrashLog over the simulator log since the last call; an honest
problem on hardware) and close.

The physical-device route (CoreDevice tunnel address from devicectl, signing
with FLEET_APPLE_TEAM_ID) is isolated in one function and unverified: no
device was attached.

apple.test.ts exports runAppleActuatorChecks for the integrator to wire into
smoke.ts. scripts/explore-apple-check.ts drives a booted simulator end to end
and prints timings. isTappableType is exported from a11y-tree.ts so driver
nodes are judged tappable by the same rule as a parsed dump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…patterns

The debug build talks to the production backend, so the signed-out cards
block account creation and resets, and the signed-in tour is read-only and
skips itself until a test account's password is in the host's Keychain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /findings upserts on fingerprint and refuses findings whose replay
reproduced nothing (422; replay null only for a crash). Verdicts, per
app x check precision with the 30% switch-off rule, a morning digest sent
once a day through the alert webhook, and GitHub issues composed and
capped (5/app/24h) as dry runs unless FLEET_GITHUB_ISSUES=1 and a token
are both set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
List with app/check/status filters, severity, check chip, seen count and
builds, replay state; detail with screenshot, steps, evidence links,
Real / Duplicate / Not a bug / Agent's mistake (keys r d n a, ignored
after g), the duplicate picker, and the dry-run issue for audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
B3 and B4 of the explore plan. bug-garden/android is Sprout, a small
Compose houseplant journal (sign-in, list, detail, add/edit, search,
settings in three languages and four text sizes, profile, invite, about)
with twenty defects planted across it: four crashes, an ANR, a blank
screen, two dead controls, five visual defects, three a11y defects, two
broken goals, a wrong back target and a prompt-injection note. All
deterministic, all reachable by touch, all switched off by the string
extra garden_defects=false.

defects.json lists each one with the check that should catch it, its
screen, steps and a match pattern; README.md states the scoring rule.
collector/examples/missions/bug-garden has seven exploration missions
and twenty bench missions whose check blocks a harness can evaluate from
a uiautomator dump (tools/check-bench.mjs does exactly that), and
collector/examples/flows/bug-garden has the sign-in setup flow, a smoke
flow and a scripted answer to each bench mission.

Same toolchain as runner-android (Gradle 8.13, AGP 8.7.3, Kotlin 2.1.0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… parallel

Two fixes found driving Dozehound's Android TV build on the dozehound-tv AVD,
kept in their own commit because android.ts belongs to another part of the
explore work:

- focusLine read 'View' (focused ...) on every screen. A Compose focusable
  tile is an unlabelled View whose words are its Text children, so the new
  focusedText() falls back to the text of the nodes nested inside the
  focused one. The line now reads 'Meadow / Hills under a whole day...'.
- observe() took the screenshot and then the uiautomator dump, ~5.3 s a step
  on a 1080p TV emulator playing video; Dozehound's channel bar hides after
  6 s without a key, so the explorer never saw the bar it had just opened.
  Both are now taken at once (~3.7 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and gateway names

- findings carry the visual judge's class; precision and switch-off are per
  check and class, crashes are never switched off
- explore-run artifacts expire after seven days unless a finding links them
- job examples and three nightly/weekly schedules, seeded off
- LaunchAgents for an executor on ultra and its tunnel to the brain over the
  tailnet; the pilot/vision gateway names; a Holo4 fetch script

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ded off)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dumpsys window opens with a frozen copy of the window state at the last ANR,
mFocusedApp included, and parseForeground took the first match. On the
dozehound-tv AVD, after three ANRs on a saturated host, it reported Dozehound
in front while the Google TV launcher was. The snapshot is now cut out (it
carries its own DISPLAY CONTENTS header, so it runs to POLICY STATE).

Kept apart from the Roku work because android.ts is another part's file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- nodesFromTree keeps a focused node the scene marks invisible (the fake's
  details screen focuses a hidden PlayButton); dropping it left the tree
  saying nothing had focus while the chain said otherwise.
- Replay results carry errorsWatched: tvloop's noErrors cannot fail on
  Android TV (its daemon reads no logcat). The replay header records that,
  that tvloop's Android launch is monkey with one RANDOM event (it once left
  the launcher in front and the keys drove it), and that on a Roku a replay
  straight after a crash sees the console backlog and fails noErrors with
  no new crash.
- The tvloop workload's header says what a green Fire TV row does not mean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
examples/missions/fakeroku: ten checked navigation missions over the fake
Roku's row of five tiles and its details screen (check: screen and/or
focused_label). examples/missions/dozehound-tv: ten checked missions for the
Fire TV build (channel bar focus, Leaving Home, pause; checks are a focused
label or banner text from uiautomator) and three explorations (calming
playlist, every button, a guest who wants out).

scripts/explore-tv-check.ts drives a fake Roku (in-process from the tvloop
checkout, so a crash can be injected) or a real one, and an Android TV over
adb, through the explore actuators and the tvloop CLI, printing timings and
saving screenshots; --missions plays each checked mission's known answer
from a fresh start and evaluates its check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… flow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a device

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First real run (Dozehound Fire TV build, Qwen3.5-2B stand-in): trimming one
screenshot per step cut cached prompt tokens from 3.6k to 1.5k every step.
In batches of four, a 14-step mission kept 7,996 of 9,072 tokens cached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /health took the screen size from UIScreen in the runner, which has no
  launch screen and so runs in legacy mode: 320x480 on an iPhone 16, every tap
  landing at 0.82 of its aim. Now from SpringBoard's (PineBoard's) frame.
- observe asks for the foreground first and reads the tree of whatever is in
  front. A snapshot of a backgrounded Settings took 38 s to return nothing.
- /tree answers `keyboard` from XCUITest's keyboards query: on iOS 26 the
  keyboard is another process's view and is not in the app's snapshot.
- close stops xcodebuild after 10 s instead of 20; it was only writing a
  result bundle nobody reads.
- the check script avoids the Apple Account row, pulls Settings down for its
  search field, and can --reset / --file / --dump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t is verified

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
addisdev and others added 26 commits October 8, 2026 20:21
… installed on ultra

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
explore: run on ultra (night-queue command, harness gateway and keys, Holo4 fixes)
For PeerTest's advisor corpus (ADVISOR-PLAN T1): the same walk on a real
device and on an emulator, in the base, dark, Spanish and large-text
settings, so a screenshot's provenance is its label. t1-walk.yaml is the
shared walk; the four t1-*.yaml set the scene and name the shots by PREFIX.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
flows(bug-garden): one screenshot of each screen, four ways
RunnerService.start() called Context.startForegroundService, which
arrived in API 26; with minSdk 24 it threw NoSuchMethodError on 24-25,
the old phones the fleet keeps minSdk low for. Below 26 it now calls
startService, which may still run in the background there.

RunnerServiceStartTest runs start() under Robolectric at sdk 25 and 34;
the 25 case fails with the NoSuchMethodError against the old code.
lintDebug reported it as NewApi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(runner-android): start the runner service on API 24-25
lintDebug failed with two errors, both from one manifest line:
`uses-feature android.hardware.type.watch required="false"`.
InvalidWearFeatureAttribute: the watch feature cannot be not-required
(Play rejects it). WearStandaloneAppFlag: once the manifest declares the
watch feature it reads as a Wear app, which owes the
com.google.android.wearable.standalone flag.

The line is removed rather than fixed. The comment said it made a Wear
launcher list the app, but a launcher lists any MAIN/LAUNCHER activity;
the feature is only a store filter, and there it means "Wear-only".
Making it required would hide the one APK from every phone, TV and
headset. Adding the standalone flag would leave the first error. adb
installs (how every shape joins, per docs/platforms.md) do not change,
and the runner still detects a watch at run time with
hasSystemFeature(FEATURE_WATCH). aapt2 badging of the built APK still
shows both launchers, the banner and every not-required feature.

runner-android.yml now runs :app:lintDebug -Pfleet.skipNative after the
unit tests, so an error like this or #75's NewApi fails CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(runner-android): drop the Wear feature, lint in CI
- explore-night.ts runs several --spec in turn on one device and boots a
  named simulator with --sim name:runtime:devicetype (created if missing,
  shut down afterwards if it booted it).
- Apple actuator unpacks a zipped .app before simctl install.
- deploy/explore/nightly-builds.sh builds dozehound-firetv, greenfolio-ios
  and dozehound-tvos from origin/develop on ultra and uploads them as
  develop-<sha>, so the specs can ask for "latest".
- Specs for the Fire TV bench (Holo4 and the resident vision model), Fire TV
  explorers, GreenFolio iOS offline and Dozehound tvOS; night-queue
  manifests for all four explore jobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Building with -sdk compiled GreenFolio's embedded watch app against the
phone SDK (WatchKit missing); a destination builds each target for its own
platform. Secrets.xcconfig comes from the checked-in placeholder template,
so no build carries real keys. git fetch retries three times past the home
gateway's connection refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…per zip

Dozehound tvOS refuses to start on the template's placeholder key, and
GreenFolio's template names its production API. Explore builds now get
SUPABASE_URL http://127.0.0.1:9 and a dummy key: configured, unreachable,
no telemetry key. unpackApp re-extracts when the zip is newer than the
folder, so a rebuild at the same path does not install the old app.

Smoke on ultra (no model): FleetDriver up on iOS 26.5 and tvOS 26.5 sims,
GreenFolio offline sign-in and Dozehound stub channels on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Fire TV app refuses to start without a real https backend and has no
stub mode, so exploring it means reading production: the owner's call.
tvOS's stub catalog needs no backend, so phase 3 runs there: ten bench
cards written from the stub app's real end states (walked by hand on a
tvOS 26.5 sim), each run with Holo4 and with the resident vision model.
GreenFolio iOS keeps its own night; nightly builds skip Fire TV unless
named.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
explore: nightly builds from develop, Fire TV bench, Apple simulators
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
explore: TV and Apple nights run after the products' night jobs
…en auth

- /api/v1: devices, workloads, jobs (validate, summary, wait), results, flaky,
  artifacts, schedules, alerts, audit; one list envelope and one error shape
- One route table drives registration, openapi.json and the typed client
  (npm run gen:api); the suite fails on drift
- FLEET_AUTH=tokens: scoped bearer tokens, delegate actor, audit log, a
  read-only session cookie; the dashboard sends its token on reads and
  exchanges it for the cookie
- Strict job-spec validation; deprecation headers on the older routes
- Fix: a long-poll whose client hung up kept claiming jobs for nobody
- cold-start moves out of executor.ts into workloads/cold-start, with
  fake-ctx checks

Co-authored-by: Cursor <cursoragent@cursor.com>
… agent skill

- fleet devices|workloads|run|job|wait|cancel|retry|results|flaky|artifact|
  login|whoami|token|skill, with --json and the verdict as the exit code
  (0 passed, 1 failed, 2 usage, 3 running, 4 skipped, 5 refused)
- fleet mcp: one tool table for stdio and Streamable HTTP; annotations,
  output schemas, resources
- fleet mcp --http: OAuth 2.1 resource server (RFC 9728, JWKS, RS/PS/ES256),
  per-person rate limits, shell refused, jobs recorded as the person
- skills/fleet-runner/SKILL.md with generated tool and exit-code tables;
  fleet skill install; fleetr alias; FLEET_RUNNER_URL/TOKEN
- Tests against a real collector: CLI, MCP (with the SDK client), gateway
  (fake IdP), skill drift
- Docs: API v1, agents, auth and gateway setup; SECURITY, CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>
…md smoke

- desktop.yml: cargo check with the fleet sidecar staged (macOS)
- runner-roku.yml: brighterscript parses and validates the channel, which
  otherwise only fails on a television
- fleet.yml: build and run the Docker image and drive it with the CLI;
  install fleet as a systemd user service, check it serves, uninstall
- ci.yml: wire both in behind the change detector and ci-ok; skills/
  changes run the fleet suite

Co-authored-by: Cursor <cursoragent@cursor.com>
The new container smoke found the image's VOLUME was created root-owned,
so the non-root fleet user could not create its data directory. The new
cargo check found the resource named fleet collided with the sidecar copy
at target/debug/fleet.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
collector: /api/v1 control API, OpenAPI, generated client, opt-in token auth
fleet: work commands, MCP server and remote OAuth gateway on /api/v1; agent skill
ci: compile the desktop app and the Roku channel; container and systemd smoke
Version 0.8.0 everywhere, the changelog section, and docs brought up to
date: releases exist, the container image and the systemd backend are run
by CI, the desktop Rust and the Roku channel compile. README and the docs
home gain the agent CLI, MCP and skill; CONTRIBUTING gains the release
steps.

Co-authored-by: Cursor <cursoragent@cursor.com>
@addisdev addisdev mentioned this pull request Oct 15, 2026
@addisdev addisdev changed the title Promote develop to main Release 0.8.0 Oct 15, 2026
@addisdev
addisdev merged commit d1a34c1 into main Oct 15, 2026
63 of 65 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant