Repository navigation
fix(daemon): end the session and clear its claim when its lease ends - #3480
Conversation
Releasing a lease (tenant lease_release or host DELETE) or letting it expire left the session opened under it holding its device claim, so the next tenant's open failed with DEVICE_IN_USE and the owner could no longer close it. The daemon now tears that session down under its execution locks and clears the claim even when teardown fails, and sweeps the lease registry every second so expiry no longer waits for a later request.
|
Thanks for this. The fix for ending a session and clearing its claim when the lease ends looks right at 77f8e43, but I found a shutdown race that needs fixing first. The five checks on this commit pass. The PR has merge conflicts with main, because #3431 rewrote
Not blocking, and you can take or leave these: Simplicity question: could I did not run the tests or reproduce the live run, and I relied on your PR body output for it. I found the shutdown window by reading the code. Please rebase onto main first, then fence lease-end teardown against shutdown and rerun CI on the new head. I did not check how the change rebases onto #3431 or #3430. |
* origin/main: (83 commits) feat(ios): interpret React Native accessibility value semantics (#3589) fix(android): reinstall and reselect the test IME helper after it disappears (#3606) feat(snapshot): carry provider keyboard observations through acquisition (#3593) chore(web): bump managed agent-browser backend to 0.39.0 (#3591) fix(fold): report a dispatched pose as unverified when devicectl has no hinge-angle stream (#3587) feat(android): expose system-bar membership on snapshot nodes (#3585) fix(android): align ADB and Appium field values (#3584) test(closure): read committed JSON modules on the merge-base side (#3602) fix(ci): preserve manual Android suite timing (#3554) refactor(daemon): make the layer folders the declaration (PR 10) (#3520) refactor(move): extract the daemon server into @agent-device/daemon (11b) (#3546) refactor(daemon): package-local runtime fakes for the remaining daemon ports [11a-4] (#3544) refactor(daemon): cut daemon test edges into root data fixtures and fakes [11a-3] (#3517) refactor(11a-2): package-local fakes at the daemon ports; root-own command-surface composition tests (#3519) test(replay): verify Settings queries and bound scrolling (#3560) feat(snapshot): measure Android Appium viewport (#3586) fix(ios): verify a fill whose unidentified field moved while it kept focus (#3596) refactor(coverage): one fallow config with per-function CRAP ceilings (#3588) test: bound CLI promotion cases and align provider budget (#3590) fix(metro): report what the dev-server stop proved instead of assuming success (#3575) ... # Conflicts: # packages/daemon/src/server/http/daemon-runtime-lease-expiry.test.ts # packages/daemon/src/server/http/daemon-runtime.ts # packages/daemon/src/sessions/__tests__/lease-lifecycle.test.ts # packages/daemon/src/sessions/lease-lifecycle.ts
Lease ends now run through one server-side owner that takes the session's execution lock pair itself, re-checks shutdown inside it, and tracks every end in flight. Shutdown cancels new ends and drains running ones before the runner hand-off and session teardown.
|
Thanks for the review. New head is 5780eec. I merged main into the branch. The lease wiring now lives in the moved
Checks run: |
|
The earlier findings from 77f8e43 are fixed, and one new problem remains in 5780eec: a session that a lease ends no longer gets its script written.
Not blocking: the composition test in I did not run any tests, and I read the missing CI is still pending. All 17 lanes were queued after the merge of main and none has failed. Integration, Coverage, and Typecheck & Package exercise the daemon runtime and lease paths this diff changes, so their results decide CI. There are no conflicts. Before merge, |
Size Report
Startup median (7 runs, lower is better):
|
* origin/main: feat(remote): name the held lease on upload, artifact download and diagnostics requests (#3594) fix(android): record the launched intent owner from the device (#3600) fix(selectors): type rect-requiring targets as addressable (#3608) feat(snapshot): interpret iOS node facts once at producer seams (#3592) refactor(gates): own the client-only replay rule as daemon-process reachability (R82) (#3604)
* origin/main: feat(leasing): proxy CLI names each new lease, daemon keeps a caller-chosen lease id (#3479)
…nd into the execution scope endLeasedSession now runs finalizeRepairTeardown before retire, so lease release, host DELETE and the expiry sweep write the session's .ad like every other session-ending route (ADR 0012 R7). createLeaseSessionEnd moves into request-execution-scope.ts, which already owns the lease registry's execution locks and evaluates the same modules, so the daemon's eager closure does not grow and no new package entry is added. The composition test records cancel and drain and asserts shutdown runs both before it tears sessions down.
|
New head is 85220c8. Main is merged in (562e798, including #3479 and #3594; no conflicts).
Gates run on this head: |
|
This PR is ready on the code. The follow-up commit 85220c8 fixes what the earlier review raised: the daemon now writes the script when a lease ends, and the new test fails without that fix because the old endLeasedSession had no write path. Not blocking: the lease-end fan-out in packages/daemon/src/server/request-execution-scope.ts runs on the daemon lifecycle, not inside a request, and it sits in a module that is now 716 lines, while its sibling createSessionIdleExpiry stays in server/http/daemon-session-idle-expiry.ts, so a later split could give both lock-scoped session-ending sweepers one shared module, and you can take or leave that. CI is still pending on 85220c8. Smoke Tests, Typecheck & Package and Coverage are queued and none has failed. Typecheck & Package and Coverage exercise the daemon runtime and the lease-lifecycle and request-execution-scope modules this change touches, so their results decide CI. I ran no tests. I did not check the eager-closure budget or the layering claim. The new .ad test covers an ordinary armed authoring session, not the repair tombstone (REPAIR_SESSION_EXPIRED / REPAIR_COMMIT_FAILED). That path runs through finalizeRepairTeardown, which is shared and already covered on the other routes. The live device run is from 77f8e43. This change only touches how the daemon writes the script and where the fan-out module lives, so I am not asking for a new run. Before merge, CI must be green on 85220c8 and the PR must leave draft. |
|
* origin/main: fix(daemon): end the session and clear its claim when its lease ends (#3480)
* origin/main: fix(daemon): end the session and clear its claim when its lease ends (#3480)
* origin/main: fix(daemon): end the session and clear its claim when its lease ends (#3480)
…e-after-close Keep both ADR 0007 rules: #3480's lease end ends the session, and the close reply owns the lease outcome. A close that finds its lease already inactive reports released.
Summary
When a lease was released or expired, the session opened under it stayed alive and kept its device claim. Its owner could not
closeit, and the next tenant'sopenfailed withDEVICE_IN_USE. Expiry was only acted on by a later request, which often found nothing.Now, when a lease ends, the daemon tears down the session bound to it under that session's execution locks and clears its claim, even if teardown fails. This covers tenant
lease_release, hostDELETE /admin/leases/<id>, and expiry (onLeaseExpired, plus a 1 s sweep so no request is needed). In-flight work and human-control holds still keep a lease alive.closeandretainOnCloseare unchanged. Help text and ADR 0007 updated. 13 files.Validation
Tested commit
77f8e43.pnpm check:affected --run: all runnable checks passed (incl. layering, fallow, 2211 related tests).New tests fail without the fix and pass with it: release ends the session; host DELETE ends it; expiry ends it with no request (runtime timer); an unreachable device still loses its claim; release waits for a running command.
Live run on a private-set iOS simulator, two proxy tenants, local daemon:
Existing daemon and server suites pass (2010 tests). CI pending.