Vancetope — Run View

A Run View (runs.html) displays the instances of all runtimes that produce them: Magrathea workflow runs, plan-shaped ThinkProcesses (Vogon, Marvin), and Damogran Compose runs. It never shows definitions — those have their own editors.

Each runtime is behind the same SPI (RunSource), so list, detail, and control only know one case.


1. Definition vs. Instance

   
Definition The written artifact — workflow YAML, Vogon strategy, Compose manifest. Lives in the Document Layer.
Run A running or completed instance of it. Has a start, state, and end.

The view is named runs.html and not workflows.html: “Workflow” is the definition, and once Vogon runs are included, the name would equate two different things.


2. RunSource — one SPI per runtime

public interface RunSource {
    String sourceId();                                  // "workflow" | "process" | "compose"
    List<RunSummaryDto> list(tenantId, projectId, limit);
    Optional<RunDetailDto> get(tenantId, projectId, nativeId);
    default Set<RunAction> allowedActions() { return Set.of(); }
    default void perform() { throw new UnsupportedOperationException(); }
}

A pure interface without Spring dependency — an addon with its own runs can implement it. RunSourceRegistry collects the beans like KindRegistry collects the KindHandlers.

Each implementation enforces its own authorization. The facade does not consolidate this: Magrathea checks the Project, the Process view checks the Process resource, and merging them would silently shift a permission boundary. The controller additionally checks Project READ.

A source that throws an exception is logged and skipped — a broken runtime must not empty the list.

The two control methods have defaults, so a new source only needs to implement the read side: allowedActions then reports an empty set, perform throws UnsupportedOperationException (the controller translates this to 501). The three included sources override both — see §6.

2.1 Not every Engine is a Run

The process source does not filter by engine names, but queries the engine: ThinkEngine.planShaped() (default false). Vogon has phases, Marvin a task tree — both have a plan that can be followed. Ford is an endless worker without a concept of progress, Arthur and Eddie are conversations.

The criterion is thus a property of the engine, not a list in the view that becomes outdated.


3. Addressing

Composite key in URL and interface: <source>:<native id>, e.g., workflow:2f1c… or process:6a7d….

The prefix is not decorative: a 32-hex run ID and a Mongo ObjectId are otherwise indistinguishable, and the view would have to guess which source to query. Only the first colon separates — a source may include colons in its own IDs.


4. Common Model

4.1 Status Vocabulary

Six values that all runtimes map to:

Value Magrathea ThinkProcess Compose
RUNNING RUNNING INIT, RUNNING running
WAITING IDLE, BLOCKED
PAUSED PAUSED PAUSED, SUSPENDED
STOPPING termination requested
DONE DONE CLOSED+DONE/AUTO_CLOSE completed without error
FAILED FAILED CLOSED+INCOMPLETE/STALE completed with error
STOPPED TERMINATED CLOSED+STOPPED/ARCHIVED/USER_DELETE/ABANDONED terminated

The mapping is intentionally lossy. IDLE and BLOCKED are the same situation for the observer — “waiting for something external” —, and SUSPENDED is a halt like a pause, even if the Session owns it and not the user.

4.2 RunDetailDto

Four blocks that all sources can populate:

Block Magrathea Vogon Compose
steps[] entered states + task result phaseHistory + current phase completed tasks
variables storeAs variables flags
children[] sub-runs via parent pointer workerProcessIds
waitingOnInboxItemId Gate item pendingCheckpoint.inboxItemId

Additionally, errorMessage — for Magrathea, the reason from the terminal StatusRecord. The three ways a run ends without finishing otherwise look identical: someone stopped it, a deadline expired, the watchdog found it unresponsive (workflows §12a). A reader must be able to distinguish exactly these.

Also links[] (open definition, open session) and extra — a source-specific block rendered by its own component. Without it, a lowest-common-denominator effect would occur: Magrathea’s start parameters, Vogon’s engine and target, Compose’s transience would disappear, even though that’s precisely why the page was opened.

allowedActions indicates what the run offers at this moment (§6) — the UI renders its buttons based on this and does not need its own rule.


5. REST

GET /brain/{tenant}/runs?projectId=<p>&limit=<n>     → List<RunSummaryDto>, newest first
GET /brain/{tenant}/runs/{runId}?projectId=<p>       → RunDetailDto
GET  /brain/{tenant}/runs/sources?projectId=<p>      → active source IDs
POST /brain/{tenant}/runs/{runId}/actions/{action}?projectId=<p>&reason=<r>
                                                     → RunDetailDto (fresh state)

Project-scoped because this is the axis all sources share: a Magrathea run belongs directly to the Project, a ThinkProcess via its Session. A run from an external Project responds 404, not 403 — the same form as the workflow controller, so the endpoint does not become an existence test.

The action route requires Project WRITE, not READ — it changes something. Unknown verb → 400, unknown run → 404, a source without control → 501. The freshly read detail state is always returned, not what the caller expected from the action.

Two ways to do nothing — and they intentionally look different.

  • Not applicableNo-op, no 409. The button was rendered from a snapshot, and by the time the click arrives, the run may legitimately have progressed. Stopping an already stopped run is not an error.
  • Not visibleRejection, 404. RunSourceRegistry.perform filters like any read via visibleTo; a run that the source hides from this subject will not be touched for it either. The opposite case would be the worst of both: the effect occurs and the response is still 404. Indistinguishable from “we don’t know it” is intentional — the endpoint must not become an existence test.

The difference is not cosmetic: one says “it was already like that”, the other “not yours”. A no-op at this point would mislead a UI into thinking it had achieved something.


6. Control

RunAction is a closed vocabulary of three: PAUSE (start nothing new, finish what’s running), RESUME (undo exactly that), STOP (pause, wind down what’s possible, mark as ended). All three concern execution — the record of a run always survives.

Which of these are offered is derived by each source from the current state, not from a declaration per source. A completed run offers nothing, regardless of who produced it:

Source State Offered
process INIT/RUNNING PAUSE, STOP
  IDLE/BLOCKED only STOP — see below
  PAUSED RESUME, STOP
  SUSPENDED only STOP — the halt belongs to the Session; a second owner of the same state is how a state starts to fluctuate
  CLOSED
workflow RUNNING PAUSE, STOP
  PAUSED RESUME, STOP
  DONE/FAILED/TERMINATED
compose running only STOP
  terminal / termination already requested

IDLE and BLOCKED do not offer Pause, for the simplest reason: SessionLifecycleService.pauseProcess only pauses what isInterruptible affirms — and those are exclusively RUNNING and INIT. A button that is rendered, pressed, and then has no effect is worse than a missing one. It is also not the same as a rejection: that is received by someone who touches a run they are not allowed to see (§5).

Compose knows no Pause: the runner executes a fixed task list and has no safe breakpoint — a button that silently does nothing would be worse than none. Termination is cooperative: the run remains STOPPING until the current task is finished.

perform is idempotent for every source: an unoffered action is a logged no-op, not an error.

6a. What v1 does not do

  • No deletion. Neither per run nor as retention. For Magrathea, this would remove exactly the audit trail that the append-only design protects.
  • No live push. Snapshot plus refresh button, like the Insights workflows tab.
  • No removal of the view from the deep-link path. The usual way remains the deep link: from the Cortex Flow view after start and from the Insights workflows tab. On the landing page, the view is in the Expert level next to Areas/Server Tools/Insights — a list of instances is not something one browses daily, but the path there must not exclusively depend on another editor. Without ?project=, it opens with its own project sidebar.

6a.1 Compose Runs are a time window

ComposeRunRegistry is an in-memory map: pod-local, capped, terminal runs swept away after ten minutes. There is no history.

This covers the case for which the view exists — “I just started something, where is it” — and nothing beyond that. The detail page names the transience, so a disappeared run does not look like an error.


7. Relationship to the Insights Workflows Tab

The tab shows definitions from _vance/workflows/ and their runs; it remains. The Run View shows runs from all sources, including those started from documents outside the Cascade path that never appear there. A link leads from the tab to each run.

Design history and action levels: planning/runs-view.md.