Found during live iOS/Android validation of #1390's #1384 fix.
Repro (real backends, both platforms)
printf 'context platform=android device="Pixel 9 Pro XL" kind=emulator theme=unknown\nopen "com.android.settings" --relaunch\n' > /tmp/open-only.ad
AGENT_DEVICE_STATE_DIR=/tmp/ad-state node bin/agent-device.mjs replay /tmp/open-only.ad --json
# => success, data.session = "cwd:<hash>:default", data.sessionActive = true
AGENT_DEVICE_STATE_DIR=/tmp/ad-state node bin/agent-device.mjs session list --json
# => { "sessions": [] } <-- empty, but the session is NOT gone
AGENT_DEVICE_STATE_DIR=/tmp/ad-state node bin/agent-device.mjs snapshot --json --session "cwd:<hash>:default"
# => succeeds, real snapshot of the still-open app
Reproduced identically on a booted iOS simulator (open "com.apple.Preferences").
Root cause (traced, not yet fixed)
session_list (src/daemon/handlers/session-inventory.ts:33-40) filters sessionStore.toArray() by sessionMatchesScope(session, resolveImplicitSessionScope(req)) (src/daemon/session-routing.ts) — comparing the CURRENT request's freshly-computed cwd scope against a session.sessionScope field stored on the session at creation time (src/daemon/handlers/session-open.ts:433,520: sessionScope: existingSession?.sessionScope ?? resolveImplicitSessionScope(req)).
The session's storage key (cwd:<hash>:default) is computed correctly at the top-level replay request's own dispatch (resolveEffectiveSessionName), using the CLI's real meta.cwd. But the session's sessionScope field gets set by whatever req reaches session-open.ts when the script's open action is dispatched internally during replay (invokeReplayAction → the real open handler) — and that internally-constructed request apparently doesn't carry the same meta.cwd a top-level CLI request does, so resolveImplicitSessionScope returns undefined for it, leaving session.sessionScope unset.
Later, session_list's own top-level request computes a proper (non-undefined) scope from the CLI's real cwd, compares it to the session's undefined sessionScope, and filters it out — even though the session is fully alive and reachable by its exact name.
Impact
Cosmetic/discovery only — the session is never actually lost, and every command that names the session explicitly (--session <name> or --state-dir <dir>, matching #1390's new address-hint contract) works. But session list (the common no-argument way to discover what's running) silently omits every cwd-scoped session created via a replay script's internal open, which will read as "nothing is running" to an agent or human relying on it.
Scope
Distinct from #1384/#1391 — this is a sessionScope propagation gap in the replay-internal open-dispatch path, not the client/daemon-lifecycle teardown #1390 fixes. Worth its own fix: either propagate the outer replay request's meta.cwd into the internally-dispatched open request, or have session-open.ts fall back to computing scope from a scope already resolved by the enclosing replay dispatch.
Found during live iOS/Android validation of #1390's #1384 fix.
Repro (real backends, both platforms)
Reproduced identically on a booted iOS simulator (
open "com.apple.Preferences").Root cause (traced, not yet fixed)
session_list(src/daemon/handlers/session-inventory.ts:33-40) filterssessionStore.toArray()bysessionMatchesScope(session, resolveImplicitSessionScope(req))(src/daemon/session-routing.ts) — comparing the CURRENT request's freshly-computed cwd scope against asession.sessionScopefield stored on the session at creation time (src/daemon/handlers/session-open.ts:433,520:sessionScope: existingSession?.sessionScope ?? resolveImplicitSessionScope(req)).The session's storage key (
cwd:<hash>:default) is computed correctly at the top-levelreplayrequest's own dispatch (resolveEffectiveSessionName), using the CLI's realmeta.cwd. But the session'ssessionScopefield gets set by whateverreqreachessession-open.tswhen the script'sopenaction is dispatched internally during replay (invokeReplayAction→ the real open handler) — and that internally-constructed request apparently doesn't carry the samemeta.cwda top-level CLI request does, soresolveImplicitSessionScopereturnsundefinedfor it, leavingsession.sessionScopeunset.Later,
session_list's own top-level request computes a proper (non-undefined) scope from the CLI's real cwd, compares it to the session'sundefinedsessionScope, and filters it out — even though the session is fully alive and reachable by its exact name.Impact
Cosmetic/discovery only — the session is never actually lost, and every command that names the session explicitly (
--session <name>or--state-dir <dir>, matching #1390's new address-hint contract) works. Butsession list(the common no-argument way to discover what's running) silently omits every cwd-scoped session created via areplayscript's internalopen, which will read as "nothing is running" to an agent or human relying on it.Scope
Distinct from #1384/#1391 — this is a
sessionScopepropagation gap in the replay-internal open-dispatch path, not the client/daemon-lifecycle teardown #1390 fixes. Worth its own fix: either propagate the outer replay request'smeta.cwdinto the internally-dispatchedopenrequest, or havesession-open.tsfall back to computing scope from a scope already resolved by the enclosing replay dispatch.