docs/mcp/MCP_TASKS.md).
The switch makes it a task. Every asset with a manifest carries an Available switch on
its own lane in Studio. Switched on, it is offered to every canvas in the org and lands on
the map in the task category. Nothing is written twice: the task reads its title,
description and selection text from the asset’s own manifest.
A task is named by one URI, everywhere: on the map, in the task table, in every hand-off,
on MCP. unoverse://tasks/unoverse/about-you is the about-you task, whatever it is built from;
written short, unoverse/about-you. The URI is an address, not a file: it points at the asset
that is the task (docs/mcp/MCP_TASKS.md § 2a, ruled 2026-10-10).
Every task has the same shape
A presentation, an application form and a chooser are built the same way. Four things make a task, whatever it is for:
Every task also carries the same discovery meta:
title, description and whenToUse.
The skill is the only thing that changes. An Agent presenting a deck and an Agent taking
a person through a form are handed the same thing: the step on screen and its props. The
presenter’s skill says what is on them; the application’s skill asks for each input: true
prop still empty. Nothing about the task itself says which it is.
A workflow or a pipeline is a task that runs its own steps. It is loaded the same ways
and takes the standard input (its Task Start’s schema). Its steps are the nodes of its
workflow: nobody else fills or moves them, and it reports its progress node by node on its
card. It finishes as every task does, handing back its output. Its run is its state: it is
not saved to the task table, so it cannot yet be picked up again after a restart
(docs/mcp/MCP_TASKS.md step 11).
“Agent” no longer names a task. Until 2026-10-10 an Agent file (agents/*.yaml) listed a
workflow on the map. That job is Task Start’s now, and “Agent” means someone who calls tasks:
a role with instructions, ways to be reached and woken, memory, and the tasks it may call
(PROPOSED, the coming Agents design).
Where it shows
A task shows wherever it is needed, and it is the same task in each place:How it is loaded
One state
While a task is active it has one state: the step it is on and the values it holds. Everything connected to it reads and writes that one state: the screen, a voice, a chat, an LLM, the node running it. A value typed on screen is the value the voice hears; a value the voice fills shows on screen; a step moved anywhere moves everywhere. Nothing keeps its own copy. A task with a finish line is saved, however it was loaded. Each attempt at it is one row in the task table, owned by the person and naming the task’s URI. The person is who is signed in, or the guest the runtime issued; a guest’s rows become theirs when they sign in. Opening a task finds the person’s open attempt at that URI, or starts a new one, so a form half filled on a page carries on in an app, after a reload, or after a dropped call, and a task finished once can be done again. Every step handed out carries the attempt’s id as itsref, and every hand-off and submit sends it back, so a call always names the attempt it
belongs to.
A task with no finish line is never saved. A presentation carries its position in each
call, the step marker, and the screen holds it.
Each confirmed step saves the screen’s state to the row. A change made anywhere else (a
voice, a node, an Agent) is published to the screen. The submit carries the screen’s answers
and they win over what was saved, so nothing typed is lost. The submit completes the row, and
a completed row is kept, marked completed, never deleted: a call that names a completed
attempt does nothing, rather than finding no row and starting the task again.
What can be done to it
Every task offers the same features, however it is worked:
A field is required unless the task marks it optional (
required on its outputs, and on an
input: true prop). Only a required field holds back a step or the submit.
Two different ways reach them, with the same features and the same rules:
An LLM may fill many fields in one go, from whatever it can reach: Spatial, the
conversation, memory. What it fills is
held, not final: the person sees it and may change
it, and only their submit makes it the answer.
Connecting a node is a choice made on the canvas: a task with no voice or LLM wired to it is
worked by typing alone. Either way nothing skips an empty field and nothing finishes a task
short, and every fill is saved to the row and shown on the screen.
Run by a node
The Task Runner is the node that runs a task in a workflow. It is aPromiseNode: each call
is one step of the state machine. It reads the task and its state, applies what arrived,
saves it, publishes where the task now is, and answers. It holds nothing between calls; the
task’s row is the memory.
Each call publishes the task’s position to the person’s screen as app state,
tasks.<ref> = { step, stepNumber, stepsTotal }, so whatever shows that task follows it.
The call that finds nothing outstanding fires done, and result carries what the task
finished with: its outputs, or an Agent’s result. Wire result into the next Task Runner to
start the next task with it.
Nothing but the task decides when it is finished. A voice, a chat or the person typing
are only ways of filling it; the task runs without any of them, and none of them can finish
it short.
Its events
A task is a node, and it sends its own events at the moments it saves, however it was run:
Nothing else sends a task’s events.
analytics is separate: it only reports to the page’s
analytics tool, when declared, and never decides whether an event is written
(Analytics).
The submit
The submit is the standard one, the same as any form’s, and it is just the last call:- The person presses Enter. The page sends
submitwith the task’s URI and every answer. - The submit goes to whoever opened the task. An Agent that opened it from Spatial gets the
answers as its tool call’s result. A task with a
bindinghas its own workflow called at its trigger, with the task’s URI and the answers, as the person. A binding may name a pipeline instead,binding: { pipeline: <name> }: the answers go to the pipeline’s Input Trigger,POST /api/pipelines/<org>/<name>/executewith the app’s own org, and the run is shown live in an agent-card, as any task that runs its own steps is. The pipeline is a task in its own right too: called directly, it is started and followed the same way. - The Task Runner in that call lays the answers over the saved row. If nothing is
outstanding, it completes the row and fires
doneandresult; if a field is still empty, it holds the step back and publishes it, and the page shows what is short.
A voice still on the call hears the task is
done and ends. One task may span several runs;
the timeline links them through the row.
A page that leaves stops its runs. When the last connection of a page’s conversation
closes and does not come back within ten seconds, every run on that conversation stops: its
Task Runners, its voice, all of it. The run is marked failed with the reason “the page
closed”, and nothing fires done or result, so no next task starts from a page nobody is
on. Ten seconds rides out a reconnect, and the task’s saved row keeps what it held.
Not built yet
The standard above is ruled. These parts of it are not yet true in the code:- One name. The page saves a task under its short name (
unoverse/about-you), the Task Runner under what it was given, and map rows carry aunoverse://tasks/address. All becomeunoverse://tasks/<org>/<name>(docs/mcp/MCP_TASKS.md§ 2a, steps U1 to U9). - Saved by URI. A task loaded by URI is not saved today.
- Analytics decides saving. The page saves a task, and its submit completes it, only when
the manifest has an
analyticsaction. The finish line (outputsand a submit) decides both; an analytics event on submit stays optional and changes neither. - Attempts. A row is found by workflow, person and task today, and a finished task cannot be started again for the same person. It becomes one row per attempt, found by the person’s open attempt at the task’s URI, and every step carries the attempt’s id.
- Optional fields. Whether a prop can be marked
requiredis not checked; today every collected answer holds the step back. - Workflow tasks saved. A workflow’s or a pipeline’s run is not saved, so it cannot be
resumed after a restart (
docs/mcp/MCP_TASKS.mdstep 11). - Its own events. Today seven places in the conversation code send a task’s events, the
finish only when the manifest declares an
analyticsaction, and memory keeps its own task rows beside the journey. A task run by the Task Runner on a page sends none, so Signal never sees it. All of it moves into the task’s node. Built so far (2026-09-28): the Task Runner sendsopenedandfinisheditself. Not yet:step(it needs the saved attempt to know the step changed), and removing the old senders. - Guest rows linked on sign-in. Designed, not checked.
- The submit calling the binding. Built for a page’s template (2026-09-28): its submit
calls the template’s binding with the answers, and the Task Runner in that call finishes it;
the wait is gone. Seen live on about-you: typed, no voice, Enter fired
doneandresult. Not built for a task in an app or on the map. - A page’s task starting itself. Built for a page’s template (2026-10-10): a binding with
autoTrigger: truestarts its task once as the page draws it, so a template that only shows what its workflow reads fills without a press. Not yet seen live. - Completing once. Completing a row today clears it, and nothing stops two calls both
firing
result. - The definition on every call. A Task Runner loaded by id reads the saved answers but not the task’s definition.
- Who may submit. How a guest’s submit passes the check for starting a workflow
(
/api/executionsneedsworkflow:author) is not checked. - The trigger rule. Nothing checks that once-per-task nodes hang off the Task Runner.
- The voice ending on
done. Not built. - Props handed out. A step should carry its empty
input: trueprops as fields to fill (each with its description), its filled ones asheldwith the value from the row, and never aninput: falseprop, which is fixed content. Today the Task Runner hands out only emptyoutputsas fields, and passes every prop on the step asheld,input: falseincluded, valued with its Studiopreviewordefault: a mock that looks filled, so aninput: trueprop is never asked for. - Update Task is not saved. What the Update Task node fills is published to the screen but never written to the row.
- The MCP tools. Which tools an LLM uses to read, fill, move and submit is not checked.
Doors
A task opens onto one of three doors:- Interface. A component or a template the customer completes on screen: a chooser, a form, a transfer, a page.
- Workflow. A workflow on a canvas, fired at its Input Trigger (its Task Start), or an app whose binding fires a workflow, a workflow with an interface in front. A pipeline is the same.
- MCP. A tool on another system. Reserved.
Agents
An Agent is not a task: it is who calls tasks. One shape for every Agent, a folderagents/<name>/ holding agent.yaml and SOUL.md: its skills, character, model, daily budget,
ways in (Spatial, Slack, email, its calendar) and the tasks it may call, each by its one URI
(unoverse://tasks/<org>/<name>). An Agent lives in Spatial, so a person or another Agent finds
it there and wakes it. How to write one: the unoverse-create skill’s agent reference; the
design: docs/architecture/signal/AGENTS.md in the platform repo.
What happens when a workflow task is called
- A search surfaces the task row. The harness mints it as a tool.
- The caller calls it. The server creates an MCP task, pushes the face into the conversation with the task’s name and message, and fires the workflow at its trigger with the caller’s own identity.
- As the run moves, the face shows the latest four steps: the one running now and the
ones just before it. An older step drops off the top as a new one starts, and the count
(
12 of 40) keeps the whole run. A step that waits on the person shows as waiting, and the person answers in the same conversation. - When the run completes, the output node’s text lands in the face and returns to the caller as the tool’s result. The caller carries on with it in the same turn.
On the execution timeline
A call to a task is drawn as a task, in amber with a door, under the Agent that called it. An MCP tool stays purple, a skill in force stays cyan. The model chose the task among its tools like any other, and the row keeps that count; what changes is what the person reading the timeline sees first: the outcome, not the plumbing.Where the switch lives
On the asset’s own lane in Studio: Components, Templates, Apps, and the Agents lane for the Agent files. On a canvas, a switched-on task is installed from the Content Library, exactly like a skill.Related
- Components and Templates: interfaces, with the selection text on their manifest.
- Apps: an app keeps its manifest and its binding; switched on, it is an Agent with an interface in front.
- Agent skills: what an Agent follows while doing a task. A skill’s switch makes it a skill on the map, never a task.

