.viewport YAML
Reference for the git-backed policy and route files validated by vpd check.
Viewport configuration lives in git under .viewport/. The launch path uses two
file types:
.viewport/policy.yamldefines what the agent may touch, which gates humans must clear, how context is captured, and how the agent is invoked..viewport/routes/*.yamldefines which external events should run that policy.
Run this before pushing:
vpd check .vpd check is strict. Unknown keys are rejected so typos fail before a route is
synced. Execution posture is derived from the policy gate and the compiled node
contract.
Layout
.viewport/
policy.yaml
routes/
github-pr.yaml
slack-support.yaml.viewport/ must be committed. If it is ignored by git, vpd check warns
because the control plane cannot reconcile files that never reach the repo.
policy.yaml
Minimum launch policy:
version: 1
repos:
- repo: acme/sandbox
access: read-write
credential: brokered
branches:
push_allowed: ["agent/**"]
restricted: ["main"]
paths:
restricted: ["src/security/**"]
gates:
- name: plan-review
type: plan
reviewers:
tags: ["tech-lead"]
timeout: 4h
on_timeout: escalate
context:
capture_policy:
store_transcripts: false
store_diffs: true
redact_patterns: ["sk-[A-Za-z0-9]+", "ghp_[A-Za-z0-9]+"]
invoke:
agent: claude-code
role: >
Draft a plan, wait for tech-lead approval, then implement on an agent
branch and open a PR.
notify:
provider: slack
credential_ref: slack/oauth
channel: C1234567890Top-level fields
| Field | Required | Description |
|---|---|---|
version | yes | Must be 1. |
repos | yes | Repositories the run may read or write. At least one repo is required. |
gates | no | Human, plan, auto, or budget gates referenced by invoke steps or used by the composer. |
context | no | Context sources and data-capture policy. |
invoke | yes | Agent, role, optional steps/workflow, and optional completion notification. |
execution | no | Shell policy for explicit shell/test execution. Shell is denied unless policy and workflow allow it. |
Repos
repos:
- repo: acme/sandbox
access: read-write
credential: brokered
branches:
push_allowed: ["agent/**"]
restricted: ["main"]
paths:
write_allowed: ["src/**", "tests/**"]
restricted: ["src/security/**"]| Field | Values | Description |
|---|---|---|
repo | owner/name | Repository slug. |
access | read-write, read-only | Maximum repo access the run may receive. |
credential | brokered, runner-local | Use brokered provider credentials for launch. Runner-local is for explicit self-hosted setups. |
branches.push_allowed | glob array | Branches the worker may push. Use agent/** for the launch path. |
branches.restricted | glob array | Branches the worker must not push directly. |
paths.write_allowed | glob array | Optional allowlist for write paths. |
paths.restricted | glob array | Paths the worker must not modify. |
Gates
gates:
- name: plan-review
type: plan
reviewers:
tags: ["tech-lead"]
timeout: 4h
on_timeout: escalate| Field | Values | Description |
|---|---|---|
name | string | Stable gate id. |
type | plan, approval, auto, budget | plan means the agent produces a plan before write steps continue. |
reviewers.tags | string array | Team reviewer tags. Sync fails visibly if no team member has the tag. |
timeout | string | Human-readable duration such as 4h. |
on_timeout | escalate, auto-approve, cancel | What the timeout resolver should do. Use escalate for first launch policies. |
auto_approve_if.tests_pass | boolean | Optional auto gate condition for later automation. Do not use it for the first launch path. |
auto_approve_if.files_changed_outside | string array | Optional file-boundary condition for later automation. Do not use it for the first launch path. |
max_cost_usd | number | Optional budget cap. |
max_tokens | number | Optional token cap. |
on_exceed | cancel, escalate | Budget behavior. |
For v1 launch, keep the model simple: one explicit plan-review gate before
implementation, with on_timeout: escalate. Do not use auto-approval conditions
or workflow-specific user permission overrides until the human-gated path is
working cleanly.
Context
context:
sources:
- name: runbook
type: file
path: docs/runbook.md
- name: trigger
type: trigger
capture_policy:
store_transcripts: false
store_diffs: true
redact_patterns: ["sk-[A-Za-z0-9]+"]Supported source types in vpd check are file, vault, trigger, and
git-repo. Use capture_policy to keep launch defaults conservative:
metadata and diffs over raw transcripts.
Invoke
invoke:
agent: claude-code
role: >
Review the routed item, produce a plan, wait for approval, then implement.
notify:
provider: slack
credential_ref: slack/oauth
channel: C1234567890| Field | Values | Description |
|---|---|---|
agent | claude-code, codex | Agent adapter requested by the policy. |
role | string | High-level operating instruction for the agent. |
notify.provider | slack | Completion notification provider. |
notify.credential_ref | string | Credential reference, usually slack/oauth. |
notify.channel | string | Fallback Slack channel. Slack-originated runs should reply to the source thread when a source timestamp is present. |
steps | array | Optional explicit invoke steps. Most launch policies should let the composer generate the standard plan/gate/implement/publish shape. |
workflow | string | Optional existing workflow name. Cannot be used together with steps. |
Execution
execution:
shell_policy:
allowed:
- "npm test*"
denied:
- "rm -rf*"Shell remains a privileged surface. Prefer explicit tests and provider actions over broad shell access. If shell is used, keep patterns narrow and prove denial paths.
Route Files
Route files are wrapped in a route: object.
GitHub Pull Request Route
route:
name: github-pr-review
team: platform
trigger:
integration: github
events: ["pull_request.*"]
conditions:
repo: acme/sandbox
policy:
source: git
repo: acme/sandbox
ref: main
path: .viewport/policy.yaml
priority: 20Slack Support Route
route:
name: slack-support
team: platform
trigger:
integration: slack
events: ["app_mention"]
conditions:
channel: C1234567890
mentionsAny:
- viewport
policy:
source: git
repo: acme/sandbox
ref: main
path: .viewport/policy.yaml
priority: 30Route Fields
| Field | Required | Description |
|---|---|---|
route.name | yes | Lowercase route id. Must match ^[a-z0-9-]+$. |
route.team | yes | Team label that owns the route. |
route.trigger.integration | yes | github, slack, jira, or linear. |
route.trigger.events | yes | Provider event names. At least one is required. |
route.trigger.conditions | no | Provider-specific matchers. |
route.policy.source | yes | Must be git. |
route.policy.repo | yes | Repo containing the policy. |
route.policy.ref | no | Git ref. Defaults to main. |
route.policy.path | no | Defaults to .viewport/policy.yaml. |
route.fan_out | no | Defaults to false. Leave off for launch. |
route.priority | no | Integer priority. Defaults to 10. Higher-specificity routes should be more explicit. |
Route Conditions
| Integration | Accepted condition keys |
|---|---|
| GitHub | repo, files_match, branch, pr_label |
| Slack | channel, mentionsAny |
| Jira | project, status_transition, issue_type, label, assignee |
| Linear | team_id, project, label |
Use mentionsAny in Slack routes.
Validation Errors
vpd check prints the file and path that failed:
✗ routes/slack-support.yaml 1 error(s)
route.trigger.conditions: Invalid inputFix validation locally before pushing. The platform should not be the first place a team discovers a malformed route.
Common causes:
| Error | Fix |
|---|---|
unsupported key executionMode | Remove it unless you are editing a compiled prompt or agent execution node. In .viewport config, execution posture comes from gates, reviewer tags, branch/path fences, and route policy. |
route.trigger.conditions: Invalid input | Use the condition keys for the selected integration. For Slack mentions, use mentionsAny. |
Gate 'x' referenced in steps but not defined | Add a matching entry in gates[] or remove the step gate reference. |
.viewport/ appears to be ignored by git | Remove .viewport/ from .gitignore and commit the files. |