Surfaces
A surface is a piece of UI that a service renders on its own node, inside the task canvas. Not a link to somewhere else, not a generic JSON view: the service ships a real component, and Requence draws it on the node that published it, in the task you are watching.
That is the whole idea. A node stops being a box you read logs out of and becomes something you can look at and use:
- a panel that shows what a long-running node is doing — progress, a preview, a diff, a chart
- a form the task waits on, answered days later by whoever gets to it
- a button that does something in the service, pressed while the node runs
The User Interaction service is the ready-made version of the second and third. Everything on this page applies to any service that draws one, and writing your own is Surfaces in the Implementation guide.
What you can count on
Section titled “What you can count on”It renders on the node. Wherever the node sits in the graph, that is where its surface is. Move the node and the surface moves.
A surface only ever shows what the service last said. The service sends props, the component draws them, and each render is a patch — it updates the fields it names and leaves the rest standing. Nothing about the surface changes on its own; if a panel looks stale, the service has not sent anything new.
A click is accepted, not answered. Pressing something tells the service what happened; it does not wait for a result. What you see afterwards is whatever the service renders next — so a form’s Send is followed by the service redrawing the form as submitted, not by the button reporting back. A well-built component never pretends the work is done before the service says it is.
Interacting needs update access. read on a task shows you its surfaces; update is what lets you drive them. Being able to watch a task is not enough to answer for it.
Controls come and go, and that is correct
Section titled “Controls come and go, and that is correct”The UI behind a surface is drawn by Requence, but the code behind its buttons runs in the service. So a control is only usable while somebody is home to run it — and Requence withholds it when nobody is:
- No instance of that service version is running. Every button on every surface that version drew is withheld. Start it, or scale it back up, and they come back on the open page, with no reload.
- The task has ended. Nothing is listening any more, so nothing is pressable — the surface stays, as the record of what the node showed.
- A live panel’s node has moved on. Some controls belong to the message that drew them (a Cancel on a running node, for example) and go quiet the moment that work finishes. A form on a waiting node is the opposite: it stays usable for the whole wait, however long that is.
A withheld control is not an error, and it is not something to retry. It means not right now.
When something goes wrong
Section titled “When something goes wrong”Failures of a surface show in one box, beneath the frame. Two different things land there, and both can stand at once, oldest first:
- The click was refused — you lack
updateaccess, the task has ended, or the control was pressed just after it went quiet. - The click was accepted but could not run — no instance picked it up in time, or the service’s handler threw. The service is the one that failed, and the sentence says which.
The box is Requence’s own report, not the component’s. It clears when a later interaction is accepted; nothing announces a success, because the service’s next render is the success.
If the component itself is broken — it fails to load, or throws while mounting — the node shows a named error state instead of the component. That is a bug in the service’s UI, and the node’s debug log is where to read about it.
One surface per node, and the pager walks traces
Section titled “One surface per node, and the pager walks traces”A node can be fed many messages — a continuous node normally is — and each message can draw its own surface. A service may even render a different component per message, so stacking them all on one node would be a pile of unrelated UIs.
So a node shows one surface at a time, and a small pager (2 of 7) appears when there are more. The pager is not a local carousel: paging selects that trace, and the whole canvas rewinds with it — node states, edge data, and every other node’s surfaces show that same run through the graph. It walks only the traces that have a surface on this node, because paging to somewhere the node is empty reads as broken.
With nothing selected you are at the newest, which is where the walk ends. If a trace selected somewhere else in the canvas never reached this node, the node says no surface in this trace and keeps its pager — so an empty node reads as a selection, not as a surface that failed to load.
A service that renders more than one surface on the same node (say a preview above a form) keeps both, stacked on the node. Those are not paged against each other; only traces are.
What it costs to allow
Section titled “What it costs to allow”A surface is third-party code from the service’s author, running in your browser. Requence isolates it: it runs in a sandboxed frame with no access to your session, no cookies, and no way to act as you. It cannot load code from anywhere else.
What it can do is talk to the network — and the props it was given are its to send. A component’s author can post what the service rendered to it wherever they like, and Requence does not audit that. Treat a service’s UI with the trust you give the service itself.