Skip to content

v0.4.0

Latest

Choose a tag to compare

@V3RON V3RON released this 30 Oct 10:39
· 47 commits to main since this release

What's Changed

  • feat(http): expose the control plane over HTTP by @V3RON in #68
  • feat: on-demand component installation for iOS runtimes and Android system images by @V3RON in #69
  • feat(ios): opt-in slim simulators (closes #87) by @V3RON in #89
  • fix(daemon): let the daemon exit while a detached lease is outstanding by @V3RON in #90
  • docs(adr): 0003 — one typed daemon contract behind every frontend by @V3RON in #92
  • feat(contract): declare every daemon operation once, negotiate protocol ranges by @V3RON in #93
  • feat(daemon): one dispatcher for every transport, roles, ownership, admin credential by @V3RON in #94
  • feat(client): a typed, single-connection daemon client by @V3RON in #95
  • feat(cli): render the contract; move token operations into the daemon by @V3RON in #96
  • feat(mcp): derive tool schemas from the contract, delete the session cache by @V3RON in #97
  • fix(contract): a lease grant returns a device projection, not the whole record by @V3RON in #98
  • docs: document the contract, the client, the security model, and 0.3.0's breaks by @V3RON in #99
  • fix(client): make abort actually cancel, and keep the mismatch escape hatch alive by @V3RON in #100
  • fix(daemon,http): one error table for both transports; fail a bind failure loudly by @V3RON in #102
  • fix(mcp,contract): scope lease_status to this session; restore the §1 boundary test by @V3RON in #103
  • fix(client): stop swallowing a second crash on the same lease by @V3RON in #104
  • fix(contract,daemon): narrow status.get, close the stop-during-start window by @V3RON in #105
  • fix(http,mcp,docs): one answer per operation across transports by @V3RON in #106
  • test(cli): restore coverage deleted under a false justification by @V3RON in #107
  • fix(daemon): reject instead of hanging when the socket claim loses the race by @V3RON in #108
  • feat(devices): Simlock-owned device roots for iOS and Android by @V3RON in #111
  • fix: resolve hardware verification findings B2, D1, D2, D3 by @V3RON in #113
  • docs(adr): 0004 TTL-first leases, 0005 gateway and worker modes by @V3RON in #121
  • docs: describe TTL-first leases end state, accept ADR 0004 (PR C) by @V3RON in #122
  • chore: ignore agent worktrees under .claude/worktrees/ by @V3RON in #133
  • docs: describe gateway and worker modes end state, accept ADR 0005 (#120) by @V3RON in #127
  • feat(cli,mcp)!: clients renew their own leases (ADR 0004, PR A) by @V3RON in #124
  • feat!(daemon,contract): one TTL-first lease on every transport (ADR 0004, PR B) by @V3RON in #125
  • feat(contract,daemon,http,cli): device.exec runs simctl/adb on the worker and streams output (ADR 0005, #116) by @V3RON in #128
  • feat(gateway,ports,http,cli): gateway skeleton — config.mode, uplink, worker views, worker.* (ADR 0005, #117) by @V3RON in #129
  • feat(gateway,core,contract,daemon): fleet queue, routing, and lease + exec forwarding (ADR 0005, #118) by @V3RON in #130
  • test(gateway,e2e): prove drain, WORKER_UNREACHABLE and reconnect rebuild end to end (ADR 0005, #119) by @V3RON in #131
  • test(e2e): prove the gateway happy path over the CLI and HTTP (ADR 0005) by @V3RON in #132
  • fix(lefthook): exclude docs/** from the format pre-commit job by @V3RON in #135
  • docs: split end-user docs from maintainer/agent docs by @V3RON in #134
  • docs(delivery): GitHub Issues as spec and queue — kind:state labels, issue forms, spec templates by @V3RON in #136
  • chore(skills): spec-session, triage-bug, and deliver skills for the delivery rule by @V3RON in #137
  • ci(issues): enforce the delivery label rules with one issue-state workflow by @V3RON in #138
  • docs(delivery),chore(skills): plain-language rule and triage fixes from the first live triage by @V3RON in #140
  • docs(delivery),chore(skills),ci(issues): blocked state, task demotion, request close, claim release by @V3RON in #142
  • fix(ios): write the download-lock separator as \0 instead of a raw NUL byte by @V3RON in #143
  • feat(core): create a new device identity for each lease under lease.identity fresh by @V3RON in #145
  • docs(delivery),chore(skills): two blind reviews before every PR — rule 14 and the review skill by @V3RON in #146
  • feat(cli,mcp): take the requester id from the agent session by @V3RON in #165
  • fix(ios): report downloaded runtimes whose disk nothing can reclaim by @V3RON in #144
  • feat(config,android): configure emulator launch options from the config file (#148) by @V3RON in #149
  • docs(adr): 0006 — events and the daemon log are two records; docs follow the implementing PR by @V3RON in #175
  • test(e2e): stop gateway and restart tests from sitting out 30s waits by @V3RON in #176
  • docs(adr): 0007 — a lease request chooses the device mode by @V3RON in #182
  • docs(adr): 0008 — the catalog pairs models with runtimes, and status carries host facts by @V3RON in #183
  • feat(core,http): store lease requests and replay them by idempotency key by @V3RON in #185
  • feat(bus,cli,config): keep business events on disk so the history survives a restart by @V3RON in #184
  • feat(daemon,cli): daemon log records every request and background failure; daemon logs --follow by @V3RON in #187
  • docs(adr): 0009 — gateway routing is a list of stages, and a request that cannot be served fails at once by @V3RON in #196
  • refactor(gateway): routing is an ordered list of stages by @V3RON in #197
  • feat(catalog,gateway,cli): the catalog pairs each model with its installed runtimes by @V3RON in #198
  • feat(contract,core,http,cli): report the device mode as mode on every surface by @V3RON in #199
  • test(e2e): read the nested lease grant in the slow lanes by @V3RON in #200
  • feat(gateway): a worker that answers NO_CAPACITY is asked once per change of its state by @V3RON in #201
  • feat(daemon,gateway,cli): status and the worker list report host facts by @V3RON in #203
  • feat(catalog,gateway,cli): the catalog lists every accepted Android name and every installed image by @V3RON in #202
  • feat(contract,core,ios,cli,http,gateway): a lease request chooses slim or full; ios.defaultMode sets the default by @V3RON in #204
  • feat(gateway): a worker is sent only requests its catalog says it can serve by @V3RON in #207
  • docs(adr): 0010 — components are installed, recorded and removed through one owner in the core by @V3RON in #222
  • feat(core,contract,daemon,gateway,cli): count each device against the RAM budget by its mode, and report the budget in status by @V3RON in #223
  • test(e2e): the slim lane reaches Simlock's device set, and the suite keeps one test per claim by @V3RON in #205
  • fix(ios,docs): stop saying iOS runtimes cannot be downloaded by @V3RON in #224
  • feat(core,ios,android,daemon): lease-triggered downloads go through one installer in the core by @V3RON in #225
  • feat(core,contract,daemon,gateway,cli): status lists the installs in progress by @V3RON in #227
  • feat(contract,daemon,http,cli,client): simlock component install installs a component from the CLI, the client and HTTP by @V3RON in #228
  • feat(core,ios,android,contract,daemon,cli,client,http): simlock component list shows every installed component, its size, and who installed it by @V3RON in #229
  • feat(android,contract,gateway,cli): mark custom Android models in the catalog, and document where models come from by @V3RON in #230
  • feat(core,drivers,daemon,cli,contract): doctor names each missing prerequisite and how to install it by @V3RON in #231
  • feat(contract,gateway,daemon,http,cli,client): simlock component install installs on a gateway's workers by @V3RON in #232
  • feat(core,contract,http,mcp): a lease request reports the downloading stage while it waits on a download by @V3RON in #233
  • feat(core,ios,android,contract,daemon,cli,client,http,gateway): simlock component remove removes a component Simlock installed by @V3RON in #234
  • feat(core,contract,drivers,gateway,cli,http,mcp): a lease request names the Android image tag by @V3RON in #235
  • docs(cli): a gateway routes a request that uses a model's other name by @V3RON in #244
  • fix(drivers): a devices.xml that cannot be checked is unreadable, not a catalog failure by @V3RON in #245
  • fix(core): one driver's failing catalog no longer fails catalog.get for every platform by @V3RON in #246
  • fix(core): wake the lease queue after every handoff grant by @V3RON in #247
  • feat(ios,core,docs): component remove removes an iOS runtime whose only other simulators were never used by @V3RON in #249
  • fix(android): cancel the shutdown wait's timer so a stopped daemon exits by @V3RON in #250
  • fix(android): list a devices.xml profile avdmanager also prints in customModels by @V3RON in #248
  • fix(ports,drivers): read an installer's progress at each carriage return by @V3RON in #251
  • fix(android): read devices.xml RAM from <d:ram unit>, the shape Android Studio writes by @V3RON in #262
  • fix(drivers): read an installer's percentage written with a decimal comma by @V3RON in #265
  • fix(core): a removal of a recorded version that is no longer installed says none is installed by @V3RON in #267
  • fix(ios): wait while simctl lists a removed runtime as deleting before verifying the removal by @V3RON in #268
  • docs(adr): 0010 is Accepted by @V3RON in #281
  • fix(core,daemon): redact gateway.token in the start log line and daemon.started by @V3RON in #279
  • fix(docs): move loop.md to docs/internal/ and drop HTTP-API.md's internal-doc pointer by @V3RON in #278
  • fix(android): refuse to ready a device another emulator answers for by @V3RON in #282
  • fix(android): skip a console port another instance's emulator is listening on by @V3RON in #269
  • test(android): the two-instance fake answers emu avd path by @V3RON in #287
  • fix(cli): write config.json owner-only from config set by @V3RON in #266
  • fix(android): reserve a console port per daemon process, not per device by @V3RON in #283
  • fix(http,gateway): serve GET /v1/leases/{id} on a gateway and name the worker on its leases by @V3RON in #285
  • fix(ios,core,daemon): state the leftover download's measured size, round progress fractions, and end a successful install at 100% by @V3RON in #290
  • docs(adr): 0011–0013 — the web console by @V3RON in #277
  • feat(contract,daemon,http,docs): a worker answers the fleet operations as a fleet of one by @V3RON in #288
  • feat(ui,http,cli,docs): the daemon serves a web console at / with operator sign-in by @V3RON in #289
  • feat(ui,docs): live data and the workers views in the console by @V3RON in #291
  • feat(console,cli,http): waiting requests view and simlock list --requests by @V3RON in #296
  • feat(ui,core,daemon,cli): attention view and stalled devices by @V3RON in #295
  • feat(ui,http,docs): leases view, lease details and holder labels in the console by @V3RON in #293
  • feat(ui,docs): the events view in the console by @V3RON in #294
  • feat(ui): the Apex look in the console by @V3RON in #298
  • feat(ui,docs,e2e): page every console table and draw only the events in view by @V3RON in #300
  • test: close audit gaps — cleanup --rule, per-boot address, one admission check, boundary scans, timeout tests by @V3RON in #303
  • refactor: delete production code only tests kept alive (audit step 2) by @V3RON in #304
  • refactor(gateway): retire the legacy routing policy only tests ran (audit step 3) by @V3RON in #306
  • test: delete tests a stronger keeper already proves (audit step 4) by @V3RON in #308
  • fix(core): a daemon that loses the identity race reads the winner's id, not an empty file by @V3RON in #302
  • chore(agents): unattended delivery — orchestrator, forked stages, merge gate, mutation testing by @V3RON in #301
  • chore(agents): weekly delivery stats, tests-after-red for the spec review, releases from a workflow by @V3RON in #313
  • feat(gateway): a request no worker can serve fails at once by @V3RON in #310
  • feat(bus): every event carries an id that survives a restart by @V3RON in #322
  • feat(gateway): retry a worker's cannot-serve refusal on another worker by @V3RON in #315
  • feat(bus): a relayed event keeps the worker's id and timestamp by @V3RON in #323
  • feat(gateway): the pick accounts for health, the worker's queue, and its RAM budget by @V3RON in #321
  • docs(adr): 0014 — an event has one id, minted where the fact happened by @V3RON in #311
  • docs(adr): 0015 — a lease request is a set of constraints, and the catalog says which class each model is by @V3RON in #330
  • chore(agents): severity-gated reviews, review inputs script, spec check before ready, assumptions by @V3RON in #331
  • chore(agents): pin model and effort for every delivery stage, reviewers as agent definitions by @V3RON in #338
  • chore(agents): delivery stats read GitHub through repo-scoped endpoints only by @V3RON in #339
  • feat(catalog): the catalog names each model's class by @V3RON in #340
  • feat(catalog): the catalog shows the default model per class (#333) by @V3RON in #342
  • docs(adr): 0016 — usage figures are derived on read from the event history by @V3RON in #344
  • feat(lease): a request names a class or nothing, and is served by a fitting idle device first by @V3RON in #343
  • feat(events): events carry the grant facts; capacity and queue depth become events by @V3RON in #351
  • feat(events): the event log keeps a window of time, bounded by size by @V3RON in #353
  • feat(lease): --os accepts a version range by @V3RON in #352
  • chore(agents): faster delivery runs and specs a person can follow by @V3RON in #355
  • feat(gateway): warm hits respect the request's mode and the runtime the worker would pick by @V3RON in #325
  • feat(gateway): a class or range request is served by a worker that fits by @V3RON in #357
  • docs(adr): 0017 — the warm pool is a module beside the lease transaction, not a step in it by @V3RON in #361
  • docs(adr): 0017 is accepted, not yet implemented by @V3RON in #367
  • docs(adr): 0018–0019 — leasing is one module, and startup ends every lease whose device is not running by @V3RON in #360
  • chore(agents): run delivery stages as background custom agents by @V3RON in #376
  • chore(agents): implementer audits its diff before reporting by @V3RON in #377
  • chore(agents): session titles and status lines show delivery progress by @V3RON in #378
  • feat(lint): imports past a module's index into core or gateway fail lint by @V3RON in #374
  • chore(agents): agents run only the tests their change reaches; hooks run the other checks by @V3RON in #379
  • chore(test): run fast e2e files four at a time by @V3RON in #380
  • chore(agents): a session stops itself when it passes a token or time budget by @V3RON in #384
  • chore(agents): implementer never runs hook checks by hand, never waits on CI, never stashes by @V3RON in #385
  • refactor(core): reclaim is its own coordinator and commits what the driver returns by @V3RON in #375
  • feat(core): the warm pool module: one budget, a keep rule that fits, and an off switch by @V3RON in #386
  • chore(agents): agents and skills take every command from toolchain.md, and say it in fewer words by @V3RON in #390
  • chore(agents): review converges: a claims review, two code-review lenses, later rounds review only the fix by @V3RON in #391
  • chore(agents): the spec check plans, runs a pre-mortem and gives every category a status by @V3RON in #392
  • refactor(leasing): leasing is its own module, entered through src/leasing/index.ts by @V3RON in #383
  • fix(leasing): a grant and its request's result are one write by @V3RON in #397
  • feat(core): a reserve of running slots, and a request waits for a device on its way by @V3RON in #394
  • feat(contract): status answers only health and host while the daemon is starting by @V3RON in #398
  • feat(core): warm targets boot or create devices ahead of demand by @V3RON in #408
  • feat(leasing): startup ends every lease whose device is not running by @V3RON in #409
  • chore(ci): the console lane runs on a PR only when it changes what the lane exercises, in Chromium only by @V3RON in #413
  • chore(agents): review rounds run while they converge, every stage is a new agent, and a spec fix is checked closed by @V3RON in #415
  • chore(agents): the gate re-runs CI once for a missing runner or a known flaky test, and the budget guard counts only active time by @V3RON in #416
  • feat(core): the warm pool in status, doctor, one event and the console by @V3RON in #411
  • docs(adr): a requester may choose its lease id (ADR 0020) by @V3RON in #418
  • fix(leasing): wake the queue after a failed boot whose destroy also fails by @V3RON in #426
  • fix(daemon): dial the gateway uplink from onSocketClaimed by @V3RON in #427
  • feat(leasing): let requesters choose the lease ID by @V3RON in #423
  • fix(gateway): a worker that was starting when first read gets its full view once its startup ends by @V3RON in #421
  • fix(leasing): free the waiter's running slot when a failed boot's device is claimed before the destroy by @V3RON in #425
  • docs(adr): a gateway dispatch is a probe, and the gateway records every fleet request's outcome (ADR 0021) by @V3RON in #432
  • feat(leasing): a gateway dispatch is a probe; workers record lease.declined, the gateway records every fleet outcome by @V3RON in #433
  • test(e2e): slow lane: a target boots a real simulator and emulator, and off shuts both down by @V3RON in #419
  • docs(adr): a physical device is a second device type the core knows (ADR 0022) by @V3RON in #435
  • docs(adr): ADR 0022 amendments from the #428 spec check by @V3RON in #443
  • chore(agents): comments say why or go; a Haiku comment review; incremental mutation testing by @V3RON in #446
  • chore(agents): implementer in phases, restricted tools, raw hand-offs, coding.md by @V3RON in #447
  • feat(core): core knows physical devices: the device union, its state machine, and virtual-only paths leave them alone by @V3RON in #444
  • chore(agents): CI failures reach fix runs; audit runs the comment review; fewer pushes; Stryker failures say why by @V3RON in #452
  • feat(enrollment): simlock device add / remove enroll and unenroll physical devices by @V3RON in #449
  • fix(core): doctor and the lease health monitor make their registry writes in the decision section by @V3RON in #454
  • test(daemon): wait for leasing's held startup write, not 25 ms of real time by @V3RON in #453
  • fix(daemon): a frame for a peer that hung up is dropped, not an unhandled rejection that kills the daemon by @V3RON in #457
  • fix(e2e): withDaemon waits for admin.token before handing the daemon over by @V3RON in #455
  • feat(leasing): lease --physical grants, queues and reclaims enrolled physical devices by @V3RON in #456
  • chore(agents): faster delivery: no Stryker on push, foreground helpers, report and budget guards, reviews sized to the diff by @V3RON in #459
  • chore(agents): comment lint, two review rounds, briefs, agent caps and delivery measurement by @V3RON in #460
  • feat(leasing): physical devices that go missing become absent, end their lease device-lost, and come back reclaimed by @V3RON in #458
  • feat(ios): iOS physical devices through devicectl by @V3RON in #461
  • feat(android): physical Android devices through the host's default adb server by @V3RON in #462
  • feat(gateway): remote agents lease and reach physical devices on workers by @V3RON in #463
  • docs(adr): agent-device edge module and lease-end port (ADRs 0023, 0024) by @V3RON in #467
  • feat(leasing): an agent id may hold more than one lease, up to lease.maxPerRequester by @V3RON in #468
  • docs(adr): ADR 0023 amendments from the #466 spec check by @V3RON in #478
  • docs(events): list lease-engine among device.deleted initiators by @V3RON in #479
  • docs(console): show simlock status's real output by @V3RON in #480
  • fix(events): list cleanup-reaper and nuke as device.deleted initiators by @V3RON in #482
  • docs(adr): 0025 — Simlock ships Android device profiles, and a model pairs only with images that fit it by @V3RON in #486
  • docs(adr): 0025 amendments from the #485 spec check by @V3RON in #492
  • test(e2e): commit prebuilt Appduct playground apps as fixtures by @V3RON in #493
  • feat(leasing): call lease-end participants before reclaiming a device by @V3RON in #494
  • feat(agent-device): simlock runs the agent-device daemon by @V3RON in #495
  • feat(doctor): simlock doctor says why agent-device is not available by @V3RON in #496
  • feat(agent-device): an agent opens and closes a device through simlock by @V3RON in #497
  • chore(reviews): calibration brief for the four weeks to 2026-10-13 by @V3RON in #498
  • feat(agent-device): heartbeats keep the simlock lease alive by @V3RON in #499
  • chore(fallow): remove four stale suppressions and fail on stale ones by @V3RON in #500
  • test(e2e): spend less time waiting by @V3RON in #504
  • test(e2e): a failed warm-pool boot is held back, checked in 2 s instead of a 60 s wait by @V3RON in #503
  • ci: split Quality into parallel static, unit and sharded e2e jobs by @V3RON in #501
  • chore(agents): one worktree per task, hook guards, decide-and-tell, stacked feature PRs by @V3RON in #505
  • chore(agents): the weekday routines finish in a cloud session by @V3RON in #502
  • test(ports): process runner waits are constructor options by @V3RON in #382
  • feat(agent-device): simlock ending a simlock lease ends the agent-device session first by @V3RON in #506
  • feat(agent-device): the front refuses requests that reach past the agent's device by @V3RON in #507
  • test(e2e): agent-device through a simlock worker on real devices by @V3RON in #509
  • feat(android): Simlock ships its own Pixel 9 Android profile, read before the SDK by @V3RON in #521
  • feat(android): the other 15 library profiles, and Medium Tablet as Android's tablet by @V3RON in #523
  • feat(android): an Android lease only uses a system image that fits its model by @V3RON in #526
  • feat(android): a devices.xml profile's API range, Play Store and screen size decide its images by @V3RON in #530
  • feat(android): doctor reports a devices.xml profile hidden by a library profile by @V3RON in #535
  • chore(comments): delete comments that say what, not why, since b2c5b2e by @V3RON in #541
  • chore(reviews): calibration brief for the four weeks to 2026-10-14 by @V3RON in #540
  • docs(adr): 0026 agent-device through a gateway by @V3RON in #512
  • feat(fleet): workers advertise their capabilities in status and worker list by @V3RON in #520
  • feat(fleet): a lease request may require a capability by @V3RON in #524
  • feat(gateway): relay one HTTP exchange to a module on a worker by @V3RON in #528
  • feat(agent-device): open, commands, heartbeats and close through a gateway by @V3RON in #536
  • feat(agent-device): devices, uploads, artifacts and diagnostics through a gateway by @V3RON in #537
  • feat(agent-device): an unreachable worker answers try again, and a gateway restart keeps the agent's device by @V3RON in #539
  • test(e2e): agent-device through a gateway on real devices by @V3RON in #538
  • chore(agents): specs cite their facts, and a split ends with tasks ready by @V3RON in #548
  • chore(agents): the hardware loop, a slow lane that reports and cleans up, and no dialogs in delivery by @V3RON in #549
  • chore(agents): audits probe every test, reviews stop overlapping, searches go to Haiku by @V3RON in #550
  • chore(agents): modules hand out capabilities, extension points share one shape, and dependency-cruiser guards every component (ADR 0027) by @V3RON in #555
  • fix(cli): daemon start reports the config error instead of a socket timeout by @V3RON in #543
  • fix(e2e): a daemon on a free port picks another when the port is taken before it binds by @V3RON in #560
  • chore: typechecks reuse a local tsbuildinfo cache between runs by @V3RON in #572
  • docs(adr): 0028 the uplink carries byte channels and small messages go first by @V3RON in #554
  • feat(agent-device): a download over 2 GB fails, never cut short by @V3RON in #561
  • feat(link): the uplink carries byte channels beside its messages, and small messages go first by @V3RON in #562
  • feat(gateway): relayed bodies travel on byte channels by @V3RON in #570
  • test(e2e): transfers through a gateway keep their promises by @V3RON in #571
  • docs(adr): 0027 host load is an optional module whose readings ride on status by @V3RON in #529
  • feat(host-load): a worker's simlock status shows its CPU, memory and disk load by @V3RON in #563
  • feat(host-load): Linux load is measured against a cgroup v2 limit by @V3RON in #565
  • feat(gateway): a gateway carries each worker's load in its view and reports its own by @V3RON in #567
  • feat(ui): the console shows each worker's load on its card and detail page by @V3RON in #568
  • docs(readme): add the global install step to Getting started by @alcpereira in #167
  • chore(comments): delete comments that say what, not why, since b2f9a95 by @V3RON in #578
  • refactor(link): the link needs no Node, so it can move to a browser or its own package by @V3RON in #579
  • docs(adr): record which accepted ADRs the code now implements by @V3RON in #589
  • fix(daemon): the daemon reports the version in package.json by @V3RON in #588
  • chore(docs): every module has a manual beside its code by @V3RON in #581
  • chore(docs): ARCHITECTURE.md becomes the map by @V3RON in #582
  • chore(docs): known gaps move into the manuals; COMPONENTS, KNOWN-PITFALLS, LOOP and drafts go by @V3RON in #583
  • chore(testing): fewer, stronger tests — new testing rules, a leasing pilot, and a weekly audit-tests routine by @V3RON in #604
  • chore(agent-device): require agent-device 0.21.28 and take its proxy from npm by @V3RON in #603
  • fix(agent-device): release the simlock lease when a command reports endedLeases by @V3RON in #608
  • docs(adr): 0030 optional modules share one contract and load only when turned on by @V3RON in #592
  • feat(doctor): one module-unavailable finding for every optional module by @V3RON in #599
  • feat(modules): optional modules load through one host and are turned on under capabilities by @V3RON in #601
  • feat(agent-device): agent-device runs as an optional module on workers by @V3RON in #605
  • feat(agent-device): the gateway side runs as an optional module by @V3RON in #606
  • feat(stream): a leased simulator's or emulator's screen, streamed to whoever is looking at it by @V3RON in #576
  • feat(ui): the console draws each worker's load as gauges on its detail page by @V3RON in #575
  • feat(ui): the load gauges colour over the console's own thresholds by @V3RON in #586
  • chore(comments): delete comments that say what, not why, since df35f28 by @V3RON in #611
  • feat(ui): console redesign — sidebar, overview, list and inspector pages, a design system by @V3RON in #610
  • fix(agent-device): release the simlock lease when close is streamed without --json by @V3RON in #615
  • fix: wait for the iOS app installer before readiness by @V3RON in #614
  • fix(http): preserve concurrent token mutations by @V3RON in #616
  • chore(agents): one spec session, explored first, tasks written from tables by @V3RON in #621
  • fix(http,core): write tokens.json and state.json owner-only (#258) by @V3RON in #640
  • fix(contract): retire helper import boundary exemptions by @V3RON in #627
  • fix(gateway): refuse to republish a worker-pushed request.dispatched by @V3RON in #641
  • fix(gateway): drop a worker's framing headers from a relay head by @V3RON in #643
  • test(e2e): wait for reclaim before cleanup preview by @V3RON in #628
  • fix(daemon): preserve replacement admin credential during restart by @V3RON in #625
  • test: daemon status prints a human view, pinned at unit cost (#422) by @V3RON in #644
  • fix(drivers/android): class a listed model by the profile its name resolves to by @V3RON in #646
  • fix(leasing): a boot failure whose destroy also fails quarantines the device and frees the slot by @V3RON in #649
  • fix(leasing): a ready grant marks its waiter in flight before the write (#402) by @V3RON in #650
  • fix(console): show fleet lease and device figures as unknown while a worker is starting by @V3RON in #651
  • fix(leasing): a handoff grant whose commit throws defers its waiter by @V3RON in #658
  • chore(comments): delete comments that say what, not why, since e25f8de by @V3RON in #652
  • fix(core): the warm pool skips devices of a platform whose driver was refused by @V3RON in #661
  • fix(core): the cleanup reaper finishes its run when one action fails by @V3RON in #664
  • fix(core): the operator reset finishes when one device has no driver by @V3RON in #668
  • fix(console): the workers-list lane helpers drop a re-read refused by a stopping gateway (#642) by @V3RON in #667
  • fix(leasing): the #armTimeout comment states what its markNew callers do by @V3RON in #670
  • fix(leasing): no device work for a waiter the queue has just rejected (#665) by @V3RON in #669
  • docs(agents): make exploration selective and evidence-bearing by @V3RON in #655
  • feat(agents): make review incremental and risk-routed by @V3RON in #656
  • chore(ci): bump actions/setup-node from 7.0.0 to 7.1.0 in the github-actions group by @dependabot[bot] in #638
  • fix(core): attribute the recovered interrupted reclaim's device.shutdown to the reclaim coordinator by @V3RON in #679
  • fix(cli): wait 20 s for a daemon a command auto-launched (#663) by @V3RON in #674
  • fix(cli): the auto-launch startup timeout reports DAEMON_STARTUP_FAILED, not INTERNAL (#672) by @V3RON in #683
  • fix(console): the overview's Activity lists the latest events, not the highest seq by @V3RON in #680
  • docs(adr): 0031 a physical iPhone streams through CoreDevice's media stream, behind a screen-source seam by @V3RON in #622
  • feat(drivers/android): stream a leased physical phone's screen behind a capture target by @V3RON in #662
  • test(ios): stream a leased physical iPhone's screen by @V3RON in #645
  • fix(gateway): a lone refusal after an aged-out window omits count (#677) by @V3RON in #682
  • fix(e2e/console): a failed daemon start reports the daemon's own cause (#673) by @V3RON in #687
  • fix(ports,core): write daemon.log, events.jsonl and instance.json owner-only (#685) by @V3RON in #689
  • perf(cli): load a command's modules only when that command runs by @V3RON in #671
  • Rank an installed Android image by the API level it records, so a preview is never the newest runtime by @V3RON in #681
  • chore(agents): spec sessions approve once, source facts while asking, and recheck only the fix by @V3RON in #725
  • chore(agents): one home for each procedure, short skill descriptions, AGENTS.md as a router by @V3RON in #726
  • docs(internal): DELIVERY.md says what the maintainer does and sees, and links to where each step is defined by @V3RON in #728
  • feat(history): history.db, usage projections, simlock stats, GET /v1/stats and console figures (#623) by @V3RON in #626
  • docs(adr): 0033 a sign-in proxy is a second way to sign in, and auth is one module by @V3RON in #704
  • refactor(auth): auth checks live in one module, src/auth by @V3RON in #732
  • feat(identity): identity.get, GET /v1/identity and simlock whoami by @V3RON in #735
  • feat(auth): sign in through a sign-in proxy's signed JWT by @V3RON in #736
  • feat(http): refuse cross-site changing requests from a proxy sign-in by @V3RON in #737
  • feat(console): sign in through the sign-in proxy and show who is signed in by @V3RON in #738
  • test(real-proxy): run Simlock behind oauth2-proxy and Dex in CI by @V3RON in #740
  • chore(agents): measured review and test strategy — mutation by tool, a self-filling ledger, fewer notes and tests by @V3RON in #739
  • fix(agent-device): stop renewing commands answered unreachable by @V3RON in #723
  • fix(cli): a daemon launched by daemon start that dies during that start reports DAEMON_STARTUP_FAILED by @V3RON in #710
  • docs(daemon): drop manual rows for files moved to src/auth by @V3RON in #742
  • fix(android): verify emulator identity before kill or snapshot load after a restart (#284) by @V3RON in #722
  • docs(adr): 0035 a gateway sends chosen fleet events to webhook destinations by @V3RON in #718
  • feat(alerts): a gateway sends chosen fleet events to generic webhook destinations by @V3RON in #741
  • feat(alerts): a gateway sends fleet events to Slack by @V3RON in #744
  • feat(alerts): simlock alerts test sends one sample to a destination by @V3RON in #746
  • feat(website): a product website on GitHub Pages by @V3RON in #747
  • fix(android): stop an orphan's running emulator before deleting its AVD by @V3RON in #751
  • docs(adr): 0034 one worker per user, and the folder says what a daemon is by @V3RON in #702
  • feat(daemon): the folder says what a daemon is, and --gateway reaches the gateway by @V3RON in #745
  • feat(cli): commands find the daemon by folder, with a --gateway hint on a worker refusal by @V3RON in #749
  • fix(android): prove running emulator root membership by @V3RON in #733
  • fix(core): startup removes dead writers' temporary files by @V3RON in #752
  • fix(agent-device): a lease the gateway's list omits is not ended (#750) by @V3RON in #753
  • docs(adr): 0036 daemon state is a SQLite database, and each owner keeps its own tables by @V3RON in #755
  • feat(state): the worker's registry lives in state.db by @V3RON in #775
  • feat(drivers): iOS and Android device-data tables by @V3RON in #783
  • feat(auth): tokens live in state.db on both daemon kinds by @V3RON in #784
  • feat(cli): the CLI and MCP show why an auto-started daemon did not start by @V3RON in #785
  • feat(gateway): the gateway's drained workers live in state.db by @V3RON in #786
  • docs(adr): 0038 the warm pool learns its kinds from leases, and boots for every queued request by @V3RON in #772
  • feat(warm-pool): the warm pool boots or creates one device per queued request by @V3RON in #788
  • feat(warm-pool): a worker with no targets keeps the kinds it has leased ready by @V3RON in #792
  • chore(agents): review replay harness, and the 2026-10-21 process review by @V3RON in #794
  • chore(agents): the process review's small fixes and counters by @V3RON in #796
  • chore(agents): stacks merge forward, the ADR PR is reviewed first, and CI failures are triaged before a fix run by @V3RON in #798
  • chore(agents): round 1 reports every site of a defect class, safety candidates are probed, mechanical fix-checks spawn no lens, the audit proves coverage by @V3RON in #799
  • chore(agents): AGENTS.md routes by role, the implementer carries its checklist, and briefs and the protocol ride in the prompt by @V3RON in #800
  • chore(agents): the feature is checked before its tasks, tests are invoker scenarios from one Behaviours table, and tasks hold about ten by @V3RON in #801
  • test(audit): 124 weak tests fixed, 33 fewer tests, since 56b5a49 by @V3RON in #795
  • fix(warm-pool): protect devices serving queued requests by @V3RON in #804
  • feat(website): the hero plays one machine, then a fleet of them by @V3RON in #763
  • docs(website): end-user docs live only on the website, rewritten as guides and lookup tables by @V3RON in #816
  • docs(adr): 0037 the console acts through the HTTP routes, on Base UI by @V3RON in #762
  • feat(history): events.replay pages back with before and limit by @V3RON in #773
  • feat(console): a new frame, and Leases in a drawer by @V3RON in #780
  • feat(console): Fleet is a table, and each worker has a page with tabs by @V3RON in #787
  • feat(console): Queue and Activity move to the shared frame, and Activity scrolls back through the history by @V3RON in #789
  • feat(console): Overview home and Inbox by @V3RON in #793
  • feat(console): release, cancel, drain, undrain and remove from the console by @V3RON in #803
  • feat(console): search or jump to anything with ⌘K by @V3RON in #834
  • test(audit): remove low-value tests across the suite, 140 fewer tests by @V3RON in #849
  • docs(adr): joined workers refuse their own HTTP listener by @V3RON in #809
  • feat(config): refuse HTTP on joined workers and migrate fixtures by @V3RON in #842
  • feat(agent-device): --os and --image-tag reach lease.request by @V3RON in #840
  • feat(warm-pool): status, doctor, console and docs show learned kinds with their origin by @V3RON in #844
  • fix(startup): reject daemon sockets closed before hello by @V3RON in #805
  • docs(adr): 0040 every HTTP route is declared once, and the OpenAPI document is built from the declarations by @V3RON in #815
  • feat(contract): move to Zod 4 by @V3RON in #845
  • feat(http): declare the System and Devices routes and build the OpenAPI document by @V3RON in #850
  • feat(website): publish openapi.json and render the HTTP API reference by @V3RON in #855
  • feat(http): declare the lease and lease-request routes by @V3RON in #854
  • feat(http): declare the admin routes in the OpenAPI document by @V3RON in #857
  • feat(http): the OpenAPI document lists the stream route by @V3RON in #858
  • test(agent-device): drop download-cap.test.ts by @V3RON in #862
  • docs(adr): 0041 the daemon can run under the user's service manager by @V3RON in #826
  • feat(daemon): SIGTERM is a clean stop, and a start beside a running daemon exits 0 by @V3RON in #841
  • feat(daemon): macOS service install, uninstall and supervised starts by @V3RON in #856
  • feat(daemon): daemon status shows the service state, doctor reports a broken service by @V3RON in #859
  • feat(ports): systemd user unit service manager on Linux by @V3RON in #861
  • fix(ci): shard the console lane over four jobs by @V3RON in #868
  • fix(ui): console e2e Activity:371 and Queue:163 by @V3RON in #869
  • test(console): stop the Overview's route stubs before the fleet shuts down by @V3RON in #867
  • ci: run console e2e only in Chromium by @V3RON in #880
  • test: wait for Console semantic state by @V3RON in #882
  • docs(adr): 0043 physical devices reach agent-device through a second daemon by @V3RON in #872
  • feat(agent-device): a worker runs a phone daemon beside the simulator daemon by @V3RON in #876
  • feat(agent-device): lease a physical phone through agent-device by @V3RON in #879
  • test: make E2E resource cleanup await process exit by @V3RON in #884
  • ci(release): plan, build on macOS, tag, publish through npm OIDC, release, deploy the site by @V3RON in #833
  • test: remove wall-clock waits from usage stats by @V3RON in #883
  • docs(adr): 0042 simlock updates itself through a frozen call and a detached updater by @V3RON in #827
  • feat(updater): the updater, its npm port and launcher port, and the install check by @V3RON in #878
  • feat(daemon): simlock update and POST /v1/update update one daemon after its leases end by @V3RON in #885
  • feat(daemon): every daemon knows the latest Simlock version, simlock status says when a newer one exists by @V3RON in #887
  • feat(gateway): update one worker from the gateway (#838) by @V3RON in #888
  • feat(ui): update a worker from the console (#839) by @V3RON in #896
  • fix(ios): stream a physical iPhone on CoreDevice 642 or later, not 642 only by @V3RON in #902
  • chore(agents): the test rules ride in the briefs, and the audit proves the spec instead of extending it by @V3RON in #904
  • fix(e2e): move the mouse off the menu so a new toast is not hovered (#900) by @V3RON in #903
  • test(daemon): relay timing test keeps every transfer sending until its windows close (#688) by @V3RON in #901
  • fix(gateway): a stale worker view no longer brings back a released lease (#898) by @V3RON in #899
  • fix(agent-device): an allocate with every capable worker drained answers NO_CAPACITY by @V3RON in #935
  • fix(e2e): the drain test answers the Fleet page without the host's load (#886) by @V3RON in #938
  • docs(adr): 0044 the console reads what its page shows, and events mark it stale (#905) by @V3RON in #907
  • feat(http): unchanged JSON answers under /v1 are 304 with an ETag by @V3RON in #920
  • feat(gateway): read one lease directly, name the worker's lease id (#913) by @V3RON in #922
  • feat(ui): the console reads per page and marks data stale from events by @V3RON in #926
  • feat(inbox): the daemon says what needs the operator: GET /v1/inbox by @V3RON in #937
  • feat(ui): the console sends ETags back and patches lease renewals in place by @V3RON in #927
  • feat(ui): one shared recent-events list per console tab by @V3RON in #939
  • test(console): an idle console on a 50-worker fleet stays under 40 requests a minute (#918) by @V3RON in #940
  • fix(ports): a worker redials a gateway that vanished without closing the uplink by @V3RON in #942
  • docs(adr): 0045 a device's screen streams to operators, not a lease's (#906) by @V3RON in #908
  • feat(stream): stream any running device by worker and device id by @V3RON in #923
  • fix(ui): console layout fixes from the 2026-10-23 review by @V3RON in #924
  • feat(ui): watch a device's screen from the device drawer by @V3RON in #928
  • feat(history): the worker's Events tab scrolls back through its history by @V3RON in #943
  • fix(http): an Android lease over HTTP names its adb serial as address by @V3RON in #953
  • feat(cli): simlock instructions prints the rules an agent must follow by @V3RON in #960
  • feat(cli): simlock setup installs the Simlock skill into Claude Code and Codex by @V3RON in #969
  • docs(adr): 0046 a standalone worker serves agent-device by default with a private profile by @V3RON in #954
  • feat(modules): a standalone worker writes a private agent-device profile for local agents by @V3RON in #963
  • feat(core): a standalone worker serves agent-device on localhost by default by @V3RON in #967
  • fix(e2e): keyboard console tests wait for the sign-in form before tabbing by @V3RON in #936
  • docs(adr): 0047 device.exec fills in the lease's device, and refuses another (#951) by @V3RON in #955
  • feat(drivers): device.exec fills in the lease's device and refuses another by @V3RON in #972
  • feat(mcp): run_on_device runs a command on the session's lease by @V3RON in #974
  • feat(leasing): lease.maxPerRequester per platform by @V3RON in #975
  • fix(android): quote a 4 s boot for a device with a matching clean baseline by @V3RON in #978
  • fix(capacity): a shut-down device holds no RAM budget by @V3RON in #979
  • docs(agent-device): parallel agents need their own session by @V3RON in #980
  • fix(e2e): a daemon start that exits 0 but never listened fails with its log tail by @V3RON in #982
  • fix(agent-device): sharding names a leased device by agent-device's device list name by @V3RON in #986
  • fix(daemon): log the Android driver's diagnostics by @V3RON in #987
  • test(agent-device): an over-limit download never delivers more than 2 GiB by @V3RON in #988
  • docs(gateway): a starting worker's leases are hidden from their owner by @V3RON in #989
  • fix(daemon): daemon start fails when the HTTP listener cannot bind by @V3RON in #990
  • fix(gateway): an expired lease on a disconnected worker no longer blocks its requester by @V3RON in #992
  • fix(daemon): a stop waits for the cleanup run in flight (#985) by @V3RON in #999
  • docs(website): --lease waits for a starting worker (#991) by @V3RON in #1002
  • chore(agents): process review 2026-10-25: turbo, bug:new triage, feature auto-close, orchestrator scripts, tool-guard waiting rules by @V3RON in #993
  • chore(checks): the test lint and nine repo tests hold the rules agents did not read, and fix the drift they found by @V3RON in #994
  • chore(checks): guards and PR checks make the delivery rules mechanical by @V3RON in #995
  • chore(checks): the spec lint runs before the checker, and a public-surface change needs its page by @V3RON in #996
  • build(deps): Fallow 3.35 with an honest CI gate by @V3RON in #997
  • fix(daemon): a stop mid-startup refuses parked requests (#1001) by @V3RON in #1003
  • fix(daemon): a stop waits for health ticks and recoveries in flight by @V3RON in #1004
  • fix(e2e): the keyboard-scroll test notes the row in view after the end row moves it by @V3RON in #1007
  • fix(e2e): the Events tab test reads every /v1/events body before the page closes by @V3RON in #1008
  • chore(comments): delete comments that say what, not why, since 4429038 by @V3RON in #1011
  • docs(adr): 0048 an iOS worker may keep its simulators in Xcode's device set by @V3RON in #964
  • feat(drivers): iOS simulators can live in Xcode's device set (drivers.ios.deviceSet) by @V3RON in #976
  • chore(ci): keep the release token out of the push URL; website install without scripts by @V3RON in #889
  • chore(deps): update ws, hono and the MCP SDK past their advisories; Dependabot for npm by @V3RON in #890
  • fix(daemon): exec children get a minimal environment; docs say what an agent token can do by @V3RON in #891
  • fix(daemon): the data directory is created owner-only by @V3RON in #892
  • fix(daemon): status.get shows an agent only its own leases and queue entries by @V3RON in #893
  • fix(contract): an exact osVersion is bounded like a component version by @V3RON in #895
  • feat(gateway): a join token is pinned to the first worker that uses it by @V3RON in #894
  • chore(comments): delete comments that say what, not why, in src/contract by @V3RON in #1032
  • chore(comments): delete comments that say what, not why, since 1f42ae4 by @V3RON in #1033
  • docs(adr): 0049, a lease holder watches and drives its device's screen (#1013) by @V3RON in https://github.com/callstackincubator/simlock/pull/1014
  • feat(stream): a lease holder watches its own lease's screen by @V3RON in https://github.com/callstackincubator/simlock/pull/1025
  • feat(gateway): a gateway module can hold a two-way byte channel to a worker module by @V3RON in https://github.com/callstackincubator/simlock/pull/1026
  • feat(input): a lease's input socket by @V3RON in https://github.com/callstackincubator/simlock/pull/1034
  • feat(input): Android input through scrcpy's control channel by @V3RON in https://github.com/callstackincubator/simlock/pull/1036
  • feat(input): iOS simulator input through the HID injector by @V3RON in https://github.com/callstackincubator/simlock/pull/1037
  • fix(gateway): drop agent-device instance pins when relaying by @V3RON in https://github.com/callstackincubator/simlock/pull/1045
  • feat(warm-pool): configured targets come first, and warmPool.learn turns learning off by @V3RON in https://github.com/callstackincubator/simlock/pull/1043
  • feat(warm-pool): simlock warm forget forgets a learned kind by @alcpereira in https://github.com/callstackincubator/simlock/pull/1040
  • chore(comments): delete comments that say what, not why, since 65df1b6 by @V3RON in https://github.com/callstackincubator/simlock/pull/1038
  • chore(deps): bump @readme/openapi-parser from 9.1.0 to 10.0.0 by @dependabot[bot] in https://github.com/callstackincubator/simlock/pull/1023
  • feat(cli): simlock --version prints the installed version by @V3RON in https://github.com/callstackincubator/simlock/pull/1059
  • feat(cli): simlock setup runs the doctor checks and reports the machine by @V3RON in https://github.com/callstackincubator/simlock/pull/1062
  • docs(website): one install recipe, MCP by scope, for-agents box by @V3RON in https://github.com/callstackincubator/simlock/pull/1065
  • ci(release): build the package on the xcode-27 image by @V3RON in https://github.com/callstackincubator/simlock/pull/1072

New Contributors

Full Changelog: v0.2.0...v0.4.0