Skip to content

feat(agent-device): an agent opens and closes a device through simlock - #497

Merged
V3RON merged 10 commits into
mainfrom
task/472
Oct 13, 2026
Merged

V3RON merged 10 commits into
mainfrom
task/472

Conversation

@V3RON

@V3RON V3RON commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Closes #472

Status

Implement: done (49/49 green; stop-time cancel per the maintainer's decision; no protocol bump) Review: round 2 + claims-only passed Hardware: n/a Gate: running
Next: gate

Done when

  • Every line under Tests is a test in src/agent-device/edge.test.ts (the adapter and authenticate lines in src/agent-device/host/upstream.test.ts, the front map lines in src/agent-device/front/front-map.test.ts, the shared line in src/agent-device/shared/rpc.test.ts), and each fails when the behaviour it names is removed.
  • With agentDevice.enabled true and HTTP on, /v1/agent-device/health with a simlock token answers the proxy's health; with it false, 404.
  • An allocate followed by a release leaves the device free in simlock list --devices, shown by the release test.
  • docs/HTTP-API.md documents /v1/agent-device/*, the one-token-per-agent rule and the messages above.
  • No line needs real hardware: End-to-end tests: agent-device through a simlock worker on real iOS and Android devices #477 runs these flows with the real CLI on iOS and Android.

Assumptions

  • Assumption: names the spec leaves open (AgentDeviceHost.tag / .authenticate, SupervisorAgentDeviceUpstream, FrontMap.add / get / markEnded / refresh, commandLeaseId) — the red commit holds inert stubs with these names so the tests typecheck; green may rename them and update the tests that call them, since no spec line fixes them.
  • Assumption: the "no idempotency key" half of the allocate test is not observable at the daemon seam (the stored request record is not exposed) — the test proves the request names no osVersion, mode, imageTag or physical device and offers no download; green's code must still send no idempotency key.
  • Assumption: FakeAgentDevice reads the tenant from the x-agent-device-tenant header, else params.tenantId, else params.meta.tenantId, as the proxy on agent-device's simlock-integration branch stamps it.
  • Assumption: HostLink.forward(request, identity) takes the identity, and the host tags the request it hands the proxy with a random header value (a map the host owns), not a WeakMap keyed on the Request: the proxy gives authenticate a copy of the request, so a WeakMap would never match. The header is overwritten on every forward and never reaches agent-device.
  • Assumption: --target values other than mobile, tv and desktop ask for no class (the smallest change; the spec names no message for them).
  • Assumption: the host and front map are built right after the supervisor, not before createLeasing: they need nothing from leasing, and TokenStore stays where it is because the host does not use it (requireAuth is the one token check).
  • Assumption: before lease.request, the front waits on a lease.list dispatch (raced against the CLI's hang-up). A request parked while the daemon starts cannot be called back, and this keeps a CLI that left before startup finished from ever being admitted (the "hangs up before simlock admitted" test).
  • Assumption: a refusal answers HTTP 400, not available 503, a failure 502/504, an operator token 403; the CLI reads the body.
  • Fixture fixes in edge.test.ts (no assertion changed): the slow and no-address drivers get phone models; the held lease in the 330 s test gets the maximum TTL so it does not expire during the wait; the --target test allows 8 running devices for its 5 leases; the two hang-up tests wait 50 ms after the hang-up so the server sees the closed socket before the daemon moves on.

Review

Spec review: 8 blocking, 4 fixed, 4 notes. Code review: 9 blocking, 7 fixed, 0 notes. Claims review: 0 blocking, 0 fixed, 2 notes.
Rounds: 2. Tokens: 19.8M.

Rejected:

  • spec: edge.test.ts:493 "no idempotency key" unproven — unobservable at the daemon seam (Assumption 2)
  • spec: allocate.ts:77 unknown target silently ignored — decided by Assumption 5
  • spec: host.ts:42 randomUUID bypasses the IdGenerator port — no rule requires it; Assumption 4
  • spec: front.ts:91 /rpc matched by exact path only — speculative, no evidence the proxy routes that path
  • spec: edge.test.ts:515 no baseline for "offers no download" — duplicate of the code finding at :514, fixed
  • code: edge.test.ts:849 repeated cancel — duplicate of the spec finding at :849, fixed

Written by an agent.

V3RON added 10 commits October 13, 2026 01:33
7 failing -> 1 failing

The /v1/agent-device mount, the proxy host, the front map and the lease flow.
Fixture fixes in edge.test.ts: phone models for the slow drivers, a long TTL
for the held lease, room for 5 devices, timing gaps after a hang-up.
1 failing -> 1 failing

Splits the refusal check and the settle steps so Fallow's complexity check passes.
… able to fail; fix the cross-tenant comment (#472)
@V3RON

V3RON commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Review notes

Not blocking, not verified. Each is one reviewer's claim.

  • spec: src/agent-device/front/allocate.ts:77 an unknown target is silently ignored and any device of that platform is leased; Assumption 5 chose this.
  • spec: src/agent-device/host/host.ts:42 the tag comes from node:crypto randomUUID, not the injected IdGenerator port.
  • spec: src/agent-device/front/front.ts:91 /rpc is matched by exact path only; a trailing-slash path the proxy also treats as rpc would skip simlock.
  • spec: src/agent-device/edge.test.ts:515 "offers no download" has no baseline showing the bare driver would send allowDownload.
  • claims: src/agent-device/index.ts:12 comment named the wrong reader of AgentDeviceAvailability (fixed in the round 1 fix run).
  • claims: src/agent-device/testing.ts:156 comment overstated the front's owner check (fixed in the round 2 fix run).

Written by an agent.

@V3RON
V3RON marked this pull request as ready for review October 13, 2026 06:13
@V3RON
V3RON merged commit fb4669f into main Oct 13, 2026
37 checks passed
V3RON added a commit that referenced this pull request Oct 13, 2026
## What changed

- `.github/workflows/ci.yml`: the Quality job ran `pnpm check` serially.
Its steps now run as parallel jobs:
- **Static checks**: typecheck, typecheck:e2e, lint, lint:comments,
format:check (keeps `fetch-depth: 0` for the comment lint).
  - **Unit tests**: `pnpm run test:unit`.
- **E2E (1/3)**, **(2/3)**, **(3/3)**: `pnpm run build && pnpm run
build:e2e`, then `vitest run --project e2e --tags-filter='!slow'
--shard=i/3`. `fail-fast: false`, so one red shard does not hide the
others.
- **Quality** still exists. It `needs` all of the above, runs `if:
always()`, and fails unless every one of them succeeded or was skipped.
The required check keeps its meaning.
- `.agents/scripts/merge-pr.sh`: when it decides whether red CI may be
re-run, it skips the Quality check. Quality fails when a job it needs
was cancelled, so judging it would call a cancelled job "this PR's to
fix". The jobs it needs are checks of their own and are judged directly.
`src/merge-pr.test.ts`: the fake `gh` now models the split (one red job
plus the Quality check that needs it); the cancelled-job test fails
without the skip.
- `docs/internal/agent-rules/toolchain.md` and
`docs/internal/DELIVERY.md` describe the split.
- `pnpm check` is unchanged.

`ci-failures.mjs` needs no change: it reads `--log-failed` for the whole
run, which covers every shard.

## Before

CI run 38355362705 (PR #497): Quality took about 5 min. Static checks
about 19 s, unit tests 45 s, then build and the e2e project 228 s, one
after another.

## After

CI run 38359780444 on this PR, job wall times:

| Job | Time |
| --- | --- |
| Static checks | 32 s |
| Unit tests | 61 s |
| E2E (1/3) | 103 s |
| E2E (2/3) | 117 s |
| E2E (3/3) | 102 s |
| Quality (sums them up) | 4 s, done 124 s after the run started |

Before: Quality 312 s. After: 124 s. Console (194 s) is now the slowest
check.

The shards are close. Shard 3 holds `warm-targets.test.ts` (69 s), most
of it one test that waits out a fixed 60 s warm-pool retry delay.

## Review

Spec review: skipped. Code review: 2 blocking, 1 fixed, 1 notes. Claims
review: 0 blocking, 0 fixed, 0 notes.

Rejected:

- code: .github/workflows/ci.yml:100 CI copies pnpm check's steps and
the e2e vitest filter, so editing one leaves the other stale — not
blocking: splitting jobs repeats the commands, and a comment says to
keep them in step

Rounds: 2. Tokens: n/a.

*Written by an agent.*
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.

An agent opens and closes a device through simlock

1 participant