V
Volker Schukai
Guest
Designing a browser handoff means separating permission, local execution, and effects that a local scheduler cannot undo.
Imagine a browser agent clicking Save in an administration interface. Before the confirmation arrives, its permission to control the browser expires. A human wants to take over.
What, exactly, has stopped?
The agent may no longer be authorized. Its request might still be in flight. The website might already have accepted the change. Those are different facts, and a local expiration timestamp cannot distinguish them.
I am a software developer and entrepreneur. My team and I are working on a project we call SessionDock. Its goal is to let people prepare persistent Chromium workspaces and temporarily delegate control to software agents. The state machine and scheduler have tested implementations; the complete path to a real agent action in the browser is still missing.
The handoff is the part of the architecture that interests me most. It forces us to separate three things that can otherwise disappear behind the word "session": browser data, permission to act, and the effects of an action.
SessionDock’s design organizes browser workplaces into slots. Each slot has its own Chromium user-data and download directories. A binding associates the workplace with a project, worktree, and other declared context. A lease grants temporary, exclusive control to an authorized actor.
These mechanisms answer different questions. The directories separate local browser data. The binding identifies the intended context. The lease governs who may use the workplace now.
For someone who only needs two separate logins, this would be unnecessary machinery. Separate profiles, or a small launcher using different user-data directories, may be enough. The additional problem here is controlled reuse by different actors, including agents whose work can arrive late.
Even that does not settle application-level conflicts. Two separate slots can access the same product record on the same website. The scheduler can coordinate declared conflicts through conflict keys, but local separation does not make remote work independent.
That does not mean agents cannot interfere with one another. The coordination only covers the conflicts the system knows about.
A lease needs more than an expiration time when control can change hands. SessionDock’s state machine also tracks an epoch: the generation of the current control permission.
Consider this illustrative sequence, not a recorded browser test:
The implemented check examines the control owner, the session’s readiness, and whether the action’s expected epoch matches the current epoch. Those checks have unit tests. They are not yet connected to a complete real-browser dispatch path.
The epoch check has one specific job: a permission that was once valid must not remain usable simply because an old request eventually arrives. Rejecting outdated authority in this way is called fencing.
But the sequence changes if the action passed its check before control changed. Increasing the epoch does not, by itself, stop already admitted work. The handoff must account for that work rather than treating the counter increment as its cancellation.
And once the website has accepted a change, no later local epoch can undo it.
I therefore treat the epoch as an authorization mechanism, not as a receipt proving that everything authorized under an earlier generation has finished.
The same distinction appears inside the local software, before a request ever reaches a website.
SessionDock’s architecture notes document a concrete problem with an earlier broker design. It used an in-process policy interface. A call that ignored its cancellation context could continue after the caller’s timeout; the timeout did not reliably terminate that call.
The Go `CancelFunc` documentation makes the distinction explicit: cancellation requests that work stop; it does not wait for that work to finish. The caller can be finished waiting while the callee is still running.
The resulting architecture decision keeps the daemon as the sole persistent authority for policy and scheduler state. A narrowly scoped broker child is intended to belong to a particular slot and browser launch, without carrying the scheduler’s bearer capability.
The child is not supposed to become another policy authority. The process boundary is intended to give its local execution a lifecycle the supervising system can terminate, rather than depending entirely on cooperative cancellation.
That is a narrower claim than "the operation has stopped." Terminating a broker does not automatically prove that work already handed to another component has stopped too. It certainly does not establish what a remote website did.
This is still an architectural decision with tested protocol components, not a completed daemon-to-browser integration. Its costs are already explicit: interprocess communication, process attestation, TLS, fencing, and lifecycle management.
An extra process is not a free reliability upgrade. Here, it is a deliberate cost for a more controllable local execution boundary.
Return to the hypothetical Save operation. At the point where a local timeout occurs, several histories remain possible:
Those histories can require different recovery actions. The absence of a reply does not identify which one occurred.
That is why I would not automatically translate "timed out" into "failed; retry." Repeating an action whose first outcome is unknown could create another effect rather than complete the original one. The example illustrates that risk. SessionDock does not yet reconcile remote results.
The distinction also explains the role of quarantine in the design. An unclear workplace should not immediately look like ordinary available capacity. Quarantine is not evidence that nothing happened; it preserves the fact that the system does not yet know enough.
The more demanding recovery path, including verified process termination and quarantine, is not fully connected to the workspace prototype. Nor is there a finished user interface explaining when someone should inspect the website, retry an action, or intervene.
Those are not cosmetic details to add after the state machine. They determine what the person taking over can reasonably conclude.
Why accept this complexity instead of creating a fresh browser profile for every run?
The documented trade-off is the manually prepared login. A disposable profile starts clean, but does not retain that preparation. One globally shared profile retains browser state while creating contention between actors. SessionDock’s chosen design is a bounded pool of persistent, administratively bound slots.
Retaining a real login across restarts remains an intended workflow, not one verified by the project’s real authentication-and-restart test matrix.
Persistence also means that a workplace can outlive the operation using it. Capacity planning, queues, and recovery become part of the system rather than incidental launcher behavior.
The scheduler persists state in SQLite and reloads it after a daemon restart. Its recovery core has tests. But recovering the scheduler’s records is not the same as proving that a real browser workplace is ready for reuse after a crash.
That gap is one of the reasons I would rather describe the design’s boundaries now than promise a seamless handoff before the full integration exists.
The project review dated September 5, 2026 establishes tested state-management, scheduling, persistence, and browser-protocol components. It does not establish an end-to-end human-to-agent handoff.
The workspace is a separate, explicitly unverified prototype. Its real Chromium launch could not be checked on the review host because the required namespaces were blocked. Its readiness output remains `security_ready=false`.
The normal CLI is not yet wired to the daemon. Agent adapters and the complete action executor are missing, and the repository’s overall checks are not all passing. Some failures reflect intentionally tests-first owner-control functionality that is not implemented yet; they remain failures, not a release approval.
These limits matter to the argument. A passing authorization test is evidence about an authorization check. It is not evidence that an in-flight browser action is safely contained during a real handoff.
The design leaves me with three separate questions: Who may act now? What local work might still be running? What is known about the result on the website?
Leases and epoch checks contribute to the first answer. Execution boundaries help with the second. Neither supplies the third merely by existing.
Before a workplace is offered to another actor, those questions need separate answers, or a clear indication that an answer is still unknown.
An expired lease tells me not to accept further work under that permission. It does not tell me that the last Save failed.
Imagine a browser agent clicking Save in an administration interface. Before the confirmation arrives, its permission to control the browser expires. A human wants to take over.
What, exactly, has stopped?
The agent may no longer be authorized. Its request might still be in flight. The website might already have accepted the change. Those are different facts, and a local expiration timestamp cannot distinguish them.
I am a software developer and entrepreneur. My team and I are working on a project we call SessionDock. Its goal is to let people prepare persistent Chromium workspaces and temporarily delegate control to software agents. The state machine and scheduler have tested implementations; the complete path to a real agent action in the browser is still missing.
The handoff is the part of the architecture that interests me most. It forces us to separate three things that can otherwise disappear behind the word "session": browser data, permission to act, and the effects of an action.
A separate profile does not decide who may act
SessionDock’s design organizes browser workplaces into slots. Each slot has its own Chromium user-data and download directories. A binding associates the workplace with a project, worktree, and other declared context. A lease grants temporary, exclusive control to an authorized actor.
These mechanisms answer different questions. The directories separate local browser data. The binding identifies the intended context. The lease governs who may use the workplace now.
For someone who only needs two separate logins, this would be unnecessary machinery. Separate profiles, or a small launcher using different user-data directories, may be enough. The additional problem here is controlled reuse by different actors, including agents whose work can arrive late.
Even that does not settle application-level conflicts. Two separate slots can access the same product record on the same website. The scheduler can coordinate declared conflicts through conflict keys, but local separation does not make remote work independent.
That does not mean agents cannot interfere with one another. The coordination only covers the conflicts the system knows about.
An epoch rejects an old permission
A lease needs more than an expiration time when control can change hands. SessionDock’s state machine also tracks an epoch: the generation of the current control permission.
Consider this illustrative sequence, not a recorded browser test:
Code:
Agent A receives control at epoch 17.
A prepares an action, but delivery is delayed.
A human takes control; the epoch becomes 18.
A's action arrives carrying epoch 17.
The authorization check rejects it.
The implemented check examines the control owner, the session’s readiness, and whether the action’s expected epoch matches the current epoch. Those checks have unit tests. They are not yet connected to a complete real-browser dispatch path.
The epoch check has one specific job: a permission that was once valid must not remain usable simply because an old request eventually arrives. Rejecting outdated authority in this way is called fencing.
But the sequence changes if the action passed its check before control changed. Increasing the epoch does not, by itself, stop already admitted work. The handoff must account for that work rather than treating the counter increment as its cancellation.
And once the website has accepted a change, no later local epoch can undo it.
I therefore treat the epoch as an authorization mechanism, not as a receipt proving that everything authorized under an earlier generation has finished.
A timeout is not an execution boundary
The same distinction appears inside the local software, before a request ever reaches a website.
SessionDock’s architecture notes document a concrete problem with an earlier broker design. It used an in-process policy interface. A call that ignored its cancellation context could continue after the caller’s timeout; the timeout did not reliably terminate that call.
The Go `CancelFunc` documentation makes the distinction explicit: cancellation requests that work stop; it does not wait for that work to finish. The caller can be finished waiting while the callee is still running.
The resulting architecture decision keeps the daemon as the sole persistent authority for policy and scheduler state. A narrowly scoped broker child is intended to belong to a particular slot and browser launch, without carrying the scheduler’s bearer capability.
The child is not supposed to become another policy authority. The process boundary is intended to give its local execution a lifecycle the supervising system can terminate, rather than depending entirely on cooperative cancellation.
That is a narrower claim than "the operation has stopped." Terminating a broker does not automatically prove that work already handed to another component has stopped too. It certainly does not establish what a remote website did.
This is still an architectural decision with tested protocol components, not a completed daemon-to-browser integration. Its costs are already explicit: interprocess communication, process attestation, TLS, fencing, and lifecycle management.
An extra process is not a free reliability upgrade. Here, it is a deliberate cost for a more controllable local execution boundary.
A stopped process is not an undone action
Return to the hypothetical Save operation. At the point where a local timeout occurs, several histories remain possible:
- The request never reached the website.
- The website received it, but has not finished processing it.
- The website applied the change, but the confirmation never reached the caller.
Those histories can require different recovery actions. The absence of a reply does not identify which one occurred.
That is why I would not automatically translate "timed out" into "failed; retry." Repeating an action whose first outcome is unknown could create another effect rather than complete the original one. The example illustrates that risk. SessionDock does not yet reconcile remote results.
The distinction also explains the role of quarantine in the design. An unclear workplace should not immediately look like ordinary available capacity. Quarantine is not evidence that nothing happened; it preserves the fact that the system does not yet know enough.
The more demanding recovery path, including verified process termination and quarantine, is not fully connected to the workspace prototype. Nor is there a finished user interface explaining when someone should inspect the website, retry an action, or intervene.
Those are not cosmetic details to add after the state machine. They determine what the person taking over can reasonably conclude.
Persistence makes recovery part of the product
Why accept this complexity instead of creating a fresh browser profile for every run?
The documented trade-off is the manually prepared login. A disposable profile starts clean, but does not retain that preparation. One globally shared profile retains browser state while creating contention between actors. SessionDock’s chosen design is a bounded pool of persistent, administratively bound slots.
Retaining a real login across restarts remains an intended workflow, not one verified by the project’s real authentication-and-restart test matrix.
Persistence also means that a workplace can outlive the operation using it. Capacity planning, queues, and recovery become part of the system rather than incidental launcher behavior.
The scheduler persists state in SQLite and reloads it after a daemon restart. Its recovery core has tests. But recovering the scheduler’s records is not the same as proving that a real browser workplace is ready for reuse after a crash.
That gap is one of the reasons I would rather describe the design’s boundaries now than promise a seamless handoff before the full integration exists.
Where the evidence ends
The project review dated September 5, 2026 establishes tested state-management, scheduling, persistence, and browser-protocol components. It does not establish an end-to-end human-to-agent handoff.
The workspace is a separate, explicitly unverified prototype. Its real Chromium launch could not be checked on the review host because the required namespaces were blocked. Its readiness output remains `security_ready=false`.
The normal CLI is not yet wired to the daemon. Agent adapters and the complete action executor are missing, and the repository’s overall checks are not all passing. Some failures reflect intentionally tests-first owner-control functionality that is not implemented yet; they remain failures, not a release approval.
These limits matter to the argument. A passing authorization test is evidence about an authorization check. It is not evidence that an in-flight browser action is safely contained during a real handoff.
Before giving the slot to someone else
The design leaves me with three separate questions: Who may act now? What local work might still be running? What is known about the result on the website?
Leases and epoch checks contribute to the first answer. Execution boundaries help with the second. Neither supplies the third merely by existing.
Before a workplace is offered to another actor, those questions need separate answers, or a clear indication that an answer is still unknown.
An expired lease tells me not to accept further work under that permission. It does not tell me that the last Save failed.