Backlog source: docs/bugs.md
Related:
Implemented for template authoring, project-scoped launches, runtime instances, pause/resume/stop controls, and on-completion transition delivery.
The template editor is a visual canvas. Session cards define agent, optional model override, input mode, auto-approval behavior, and instructions. Transitions connect card anchors and define trigger, result mode, and prompt template. Runtime instances create ordinary sessions and drive follow-up prompts when source sessions become prompt-ready.
Delegation waits intentionally reuse the same runtime idea without requiring a
visible template. When a parent asks TermAl to wait for delegated child sessions,
those children behave like completion sources and the parent behaves like the
destination session. A wait with mode="all" mirrors a Consolidate node: the
parent receives one synthesized prompt after all watched delegations are
terminal. A wait with mode="any" mirrors ordinary queued transition delivery:
the parent resumes when the first watched delegation is terminal.
Unlike orchestration templates, delegation waits are ad hoc and parent-owned. The parent session can yield after scheduling the wait; TermAl persists the wait, exposes it through state/SSE for UI visibility, and re-activates the parent by queueing the synthesized resume prompt when the fan-in condition is satisfied.
TermAl can already run many agent sessions in parallel, but the developer still does the routing: read a session’s reply, decide who should act next, copy the result into the next prompt, and keep switching tabs. That is coordination work, not product work.
The missing piece is orchestration: a reusable graph of sessions and transitions that lets TermAl move work forward automatically once a session finishes its turn and is ready for the next prompt.
An orchestration template is a graph:
idle, so it is
prompt-ready again.There is no special orchestrator node in the data model. If the workflow needs a lead, coordinator, or reviewer session, that is just another regular session on the canvas.
Example graph:
Planner -> Builder -> Reviewer
^ |
|--------------------|
In that graph:
Planner can assign work to BuilderBuilder can hand implementation results to ReviewerReviewer can hand review results back to PlannerAll three are ordinary sessions with ordinary prompts and ordinary histories.
Templates are design-time only. They define reusable session slots, transition rules, and canvas layout. They do not contain runtime status, live session IDs, or pending work.
{
"name": "Feature Delivery",
"description": "Implement, review, and loop until ready.",
"sessions": [
{
"id": "planner",
"name": "Planner",
"agent": "Claude",
"model": "claude-sonnet-4-5",
"instructions": "Plan the work and decide who should act next.",
"autoApprove": false,
"position": { "x": 640, "y": 120 }
},
{
"id": "builder",
"name": "Builder",
"agent": "Codex",
"model": "gpt-5",
"instructions": "Implement the requested changes.",
"autoApprove": true,
"position": { "x": 220, "y": 420 }
},
{
"id": "reviewer",
"name": "Reviewer",
"agent": "Claude",
"instructions": "Review the changes and report issues.",
"autoApprove": false,
"position": { "x": 980, "y": 420 }
}
],
"transitions": [
{
"id": "planner-to-builder",
"fromSessionId": "planner",
"toSessionId": "builder",
"trigger": "onCompletion",
"resultMode": "lastResponse",
"promptTemplate": "Use this plan and implement it:\n\n"
},
{
"id": "builder-to-reviewer",
"fromSessionId": "builder",
"toSessionId": "reviewer",
"trigger": "onCompletion",
"resultMode": "summaryAndLastResponse",
"promptTemplate": "Review this implementation:\n\n"
},
{
"id": "reviewer-to-planner",
"fromSessionId": "reviewer",
"toSessionId": "planner",
"trigger": "onCompletion",
"resultMode": "lastResponse",
"promptTemplate": "Reviewer finished. Decide the next action:\n\n"
}
]
}
Templates persist to ~/.termal/orchestrators.json.
Phase 1 supports a single trigger:
enum TransitionTrigger {
OnCompletion,
}
OnCompletion means:
idleTransitions do not fire while a session is still active, waiting on approval, or waiting on user input.
Cyclic graphs are supported. Templates can represent both one-shot flows and intentionally long-running loops such as planner-reviewer-fixer cycles.
A transition defines how the completed session’s output is transformed before it is sent to the destination.
enum TransitionResultMode {
None,
LastResponse,
Summary,
SummaryAndLastResponse,
}
None: ignore the source result and rely entirely on promptTemplateLastResponse: use the source session’s latest assistant replySummary: create or use a concise summary of the source sessionSummaryAndLastResponse: include bothEach transition has a promptTemplate. The backend renders it using the processed result and then
queues that rendered prompt into the destination session.
At minimum the backend should support:
Example:
Builder finished its turn.
Use this implementation result and decide whether to approve or request changes:
The developer designs a reusable session graph on a canvas:
The developer instantiates a template for a selected project.
The backend:
At runtime the loop is edge-driven:
Session A becomes prompt-ready after replying
-> backend identifies matching outgoing transitions
-> backend builds transition payload(s)
-> backend renders prompt template(s)
-> backend queues prompt(s) into destination session(s)
If a session has multiple outgoing transitions, multiple destination sessions may be prompted.
struct OrchestratorTemplate {
id: String,
name: String,
description: String,
sessions: Vec<OrchestratorSessionTemplate>,
transitions: Vec<OrchestratorTemplateTransition>,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
}
struct OrchestratorSessionTemplate {
id: String,
name: String,
agent: Agent,
model: Option<String>,
instructions: String,
auto_approve: bool,
position: CanvasPoint,
}
struct OrchestratorTemplateTransition {
id: String,
from_session_id: String,
to_session_id: String,
trigger: TransitionTrigger,
result_mode: TransitionResultMode,
prompt_template: Option<String>,
}
struct CanvasPoint {
x: f64,
y: f64,
}
Constraints:
from_session_id and to_session_id must reference existing sessionsThis is the shape the runtime should move toward once orchestration execution is implemented:
struct OrchestratorInstance {
id: String,
template_id: String,
template_snapshot: OrchestratorTemplate,
status: OrchestratorStatus,
session_instances: Vec<OrchestratorSessionInstance>,
pending_transitions: Vec<PendingTransition>,
created_at: DateTime<Utc>,
completed_at: Option<DateTime<Utc>>,
}
struct OrchestratorSessionInstance {
template_session_id: String,
session_id: String,
last_completion_revision: Option<u64>,
last_delivered_completion_revision: Option<u64>,
}
struct PendingTransition {
id: String,
transition_id: String,
source_session_id: String,
destination_session_id: String,
completion_revision: u64,
rendered_prompt: String,
created_at: DateTime<Utc>,
}
enum OrchestratorStatus {
Running,
Paused,
Completed,
Stopped,
}
The runtime hook is not “generic commit happened.” It is the explicit session lifecycle edge:
active -> idle after reply
That edge is what creates transition work.
When a source session completes a turn:
from_session_id matches the completed sessionresult_modeprompt_templatePendingTransitionPersisting undelivered transitions is important for restart safety.
Running orchestrations survive restart.
On boot:
pending_transitionsThis keeps transition delivery deterministic and avoids duplicate prompts after restart.
Phase 1 template management API:
GET /api/orchestrators/templates
POST /api/orchestrators/templates
GET /api/orchestrators/templates/{id}
PUT /api/orchestrators/templates/{id}
DELETE /api/orchestrators/templates/{id}
The path can stay /api/orchestrators/... even though the template model no longer contains a
special orchestrator node.
Later phases can add:
GET /api/orchestrators
POST /api/orchestrators
GET /api/orchestrators/{id}
POST /api/orchestrators/{id}/pause
POST /api/orchestrators/{id}/resume
POST /api/orchestrators/{id}/stop
The control panel and settings should let the developer:
The canvas is a graph editor:
There is no visually special orchestrator card by default. If the developer wants a central coordinator session, they create one explicitly and place it where they want on the canvas.
Resolved. The orchestration graph contains only regular sessions. A “main” or “planner” role is
just another session in sessions.
Resolved. OnCompletion means the source agent replied and the session became prompt-ready again.
Resolved. A transition belongs to the edge, not the node. It defines how a completed result is processed and how the destination prompt is built.
Resolved. Transitions do not create sessions. They route work only between sessions that already exist in the orchestration instance.
Resolved. Summary-based result modes use a fresh summarizer session if and when those modes are implemented. The summary is not generated by asking the source session to summarize itself.
~/.termal/orchestrators.jsonOnCompletion edges from session lifecyclepending_transitionsSummary and SummaryAndLastResponseBackend
Frontend
Integration