Repository navigation
Conversation
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>
… 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>
Merged
Release 0.8.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes develop to main for v0.8.0. After this merges, tag
v0.8.0on the merge commit to runrelease.yml./api/v1with OpenAPI and a generated client; the agent CLI (fleetr); the local MCP server and the remote MCP gateway with OAuth; thefleet-runnerskill; CI that runs the container image and the systemd backend and compiles the desktop app and the Roku channel. Fixes the container image failing to start on a fresh volume, and a hung-up long-poll swallowing the next job.See CHANGELOG.md.