VIEWPORT
Concepts

Workflows

Repo-local YAML contracts that bind plans, gates, context, and runs.

A workflow is a YAML file your team commits at .viewport/workflows/NAME.yaml. It declares: under what triggers an agent runs, what context to attach, which steps need a human gate, and where decisions route. Workflows are validated against a JSON Schema, persisted as versioned definitions, and executed as runs.

The contract should travel with the code. There is no UI-only state that drifts from what's checked in.

Schema

Canonical schema: packages/daemon/schemas/workflow-v1.schema.json.

Top level:

schema: viewport.workflow/v1
name: review-auth-change
title: Review an auth middleware change
description: Plan-first auth refactor with reviewer sign-off.

inputs:
  branch: { type: string }
  paths: { type: json }

context:
  - ctx_acme_runbooks            # vault attachment

requires:
  agents: [claude-code]
  tools: []
  integrations: []
  secrets: []

nodes:
  inspect:  { type: read_only }
  plan:     { type: plan, review: { required: true, reviewers: team_platform } }
  execute:  { type: apply_plan, gate: { destructive: ask } }
  capture:  { type: capture_outputs }

Validate locally:

vpd workflow validate path/to/workflow.yaml

inputs[*].type accepts string, number, boolean, json. outputs[*].type adds file and artifact.

The five built-in node types

typeBehavior
read_onlyInspect the repo. No file writes, no shell.
planProduce a plan artifact. Optionally gated on review.
apply_planExecute an approved plan. Per-action gates apply.
shellRun a specific command. Allow-listed and logged.
capture_outputsPersist run artifacts.

Custom node types come from the workflow plugin SDK below.

Runs

Every invocation creates a workflow run with its own page in the app. Steps stream live; gated steps pause and surface a decision. The run carries every plan, gate, and decision back to the workflow that produced it.

Data capture policy

The daemon decides what content the platform stores per run. Three independent flags travel on the run launch:

{
  "transcripts": true | false,
  "logs": true | false,
  "artifacts": true | false
}

Off means the platform stores none of that content. On means it persists what the daemon emits. The daemon is the source of truth for what gets sent.

Execution policy

Per run, the daemon can isolate execution:

{ "mode": "current_tree" }                            // run in the active working tree
{ "mode": "isolated_worktree" }                       // create a fresh worktree
{ "mode": "named_branch", "branch": "auto/auth-fix" } // run on a specific branch

See vpd worktree for the operator-side primitives.

Approvals

A plan or apply_plan node can declare a managed approval gate. When such a node fires, an inbox item appears for the configured audience. The decider hits Approve or Deny; the daemon reads the result over its workflow-run subscription and continues or aborts.

CLI helpers:

vpd workflow validate PATH
vpd workflow run PATH [--input k=v]...
vpd workflow runs --limit 20
vpd workflow show RUN_ID
vpd workflow rerun RUN_ID
vpd workflow approve RUN_ID NODE_KEY [--message]
vpd workflow cancel RUN_ID

The plugin SDK

@viewportai/workflow-sdk lets you write custom node types.

import { defineNode } from '@viewportai/workflow-sdk';

export const lintNode = defineNode({
  type: 'eslint.lint',
  contract: 'viewport.workflow-plugin/v1',
  async run(ctx, inputs) {
    // ctx exposes the run context, secrets, artifacts, logs.
    return { ok: inputs.errors === 0 };
  },
});

Plugins are loaded from ~/.viewport/plugins.json at daemon startup.

Preflight

Dry-run a workflow before persisting it:

vpd workflow validate workflow.yaml

Or via the API:

POST /api/resources/{workspace}/workflow-runs/preflight

Preflight validates the YAML against the schema, resolves resources, and reports what would run without creating a run record.

  • Plans for what plan nodes produce
  • Inbox for where approval gates surface
  • Spec for the full viewport.workflow/v1 field reference
  • API for the REST and WS surface

On this page