# Demo Workflows — v0.3.6

Four example workflow packages that prove the engine works end-to-end.
Each is a self-contained `.pkg` archive installable via Admin > Packages.

## Feature Capability Matrix

| Feature | Bug Report | Onboarding | Content Approval | Webhook |
|---------|:----------:|:----------:|:----------------:|:-------:|
| Public entry (anonymous) | ✅ | | | |
| Progressive fieldsets | ✅ | ✅ | | |
| Branch rules (conditional routing) | ✅ | | | |
| Team assignment + claim | ✅ | | | |
| SLA timer | ✅ | | | |
| Starlark automated stage | | ✅ | | ✅ |
| db.insert (ext_data tables) | | ✅ | | ✅ |
| notifications.send | | ✅ | | |
| http.post (outbound webhook) | | | | ✅ |
| connections.get (credentials) | | | | ✅ |
| Signoff gate (required_role) | | ✅ | | |
| Multi-party signoff (quorum) | | | ✅ | |
| Rejection reroute | | ✅ | ✅ | |
| Review cycle (loop) | | | ✅ | |

---

## Package 1: Bug Report Triage

**Slug:** `bug-report-triage`
**Entry mode:** `public_link` — anyone with the link can submit
**Tier:** browser (no Starlark needed)

### Stage Flow

```
[submit]  →  [classify]  →  [fix-critical]  →  [verify]
  public       team        ↘ [fix-normal]  ↗     team
                              team
```

### Stages

| # | Name | Mode | Audience | Key Feature |
|---|------|------|----------|-------------|
| 0 | submit | form | public | 2 fieldsets: "Report Details" (email, summary, component, severity) + "Reproduction" (steps, expected, actual) |
| 1 | classify | form | team | Team reviews. Branch rules: severity=critical → fix-critical, else → fix-normal |
| 2 | fix-critical | form | team | Senior team queue. SLA: 3600s |
| 3 | fix-normal | form | team | Standard fix queue |
| 4 | verify | review | team | Verify fix, close out |

### Branch Rules (stage 1)

```json
[
  {"field": "severity", "op": "eq", "value": "critical", "target_stage": "fix-critical"},
  {"field": "severity", "op": "neq", "value": "critical", "target_stage": "fix-normal"}
]
```

### API Walkthrough

```bash
# 1. Start public instance (no auth required)
curl -X POST $BASE/api/v1/public/workflows/$WF_ID/start \
  -H 'Content-Type: application/json' \
  -d '{"data": {"reporter_email": "user@example.com", "summary": "Login broken", "component": "ui", "severity": "critical", "steps": "1. Click login", "expected": "Login page", "actual": "500 error"}}'

# Response includes entry_token for anonymous continuation
# {"id": "inst-xxx", "entry_token": "tok-yyy", ...}

# 2. Resume (check status)
curl $BASE/api/v1/public/workflows/resume/tok-yyy

# 3. Team member claims the classify assignment
curl -X POST $BASE/api/v1/teams/$TEAM/assignments/$ASSIGN_ID/claim \
  -H "Authorization: Bearer $TOKEN"

# 4. Advance classify stage (triggers branch rule)
curl -X POST $BASE/api/v1/teams/$TEAM/instances/$INST/advance \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"data": {"severity": "critical", "notes": "Confirmed production issue"}}'
# → Routes to fix-critical (not fix-normal)
```

---

## Package 2: Employee Onboarding

**Slug:** `employee-onboarding`
**Entry mode:** `team_only`
**Tier:** starlark
**Permissions:** `db.write`, `notifications.send`

### Stage Flow

```
[new-hire-info]  →  [provision-accounts]  →  [manager-signoff]  →  [it-setup]  →  [welcome]
    team              automated (Starlark)      review + signoff       team         automated
                      auto-advances             reject → stage 0                   auto-advances
```

### Stages

| # | Name | Mode | Audience | Key Feature |
|---|------|------|----------|-------------|
| 0 | new-hire-info | form | team | 2 fieldsets: "Personal Info" + "Access Requirements" |
| 1 | provision-accounts | automated | system | Starlark: db.insert provisioning records, notify HR |
| 2 | manager-signoff | review | team | 1 approval required, required_role: "manager", reject → new-hire-info |
| 3 | it-setup | form | team | IT confirms hardware + access |
| 4 | welcome | automated | system | Starlark: log completion, send welcome notification |

### Starlark Hooks

**`on_provision(ctx)`** — Automated provisioning:

```python
def on_provision(ctx):
    data = ctx["stage_data"]
    name = data.get("full_name", "unknown")
    dept = data.get("department", "general")

    # Record provision in ext_data table
    db.insert("provisions", {
        "employee_name": name,
        "department": dept,
        "needs_vpn": str(data.get("needs_vpn", False)),
        "needs_admin": str(data.get("needs_admin", False)),
        "equipment": data.get("equipment", "standard laptop"),
        "status": "provisioned"
    })

    # Notify the HR user who started this workflow
    notifications.send(
        ctx["started_by"],
        "Accounts provisioned for " + name,
        "Department: " + dept + ". Ready for manager review."
    )

    return {"advance": True, "data": {
        "provision_status": "complete",
        "accounts": ["email", "vpn" if data.get("needs_vpn") else None, "admin" if data.get("needs_admin") else None]
    }}
```

**`on_welcome(ctx)`** — Completion logging:

```python
def on_welcome(ctx):
    data = ctx["stage_data"]

    db.insert("onboarding_log", {
        "employee_name": data.get("full_name", ""),
        "department": data.get("department", ""),
        "completed_by": ctx["started_by"],
        "status": "complete"
    })

    notifications.send(
        ctx["started_by"],
        "Onboarding complete: " + data.get("full_name", ""),
        "All stages finished. Welcome aboard!"
    )

    return {"advance": True, "data": {"onboarding_complete": True}}
```

### Signoff Gate (stage 2)

```json
{
  "validation": {
    "required_approvals": 1,
    "required_role": "manager",
    "reject_action": "new-hire-info"
  }
}
```

### Prerequisites

- Team must have a custom "manager" role (via Team Admin > Members > Team Roles)
- At least one team member with the "manager" role assigned

---

## Package 3: Content Approval

**Slug:** `content-approval`
**Entry mode:** `team_only`
**Tier:** browser

### Stage Flow

```
[draft]  →  [review]  →  [publish]
   team       team  ↕      team
             [revision]
              team
```

### Stages

| # | Name | Mode | Audience | Key Feature |
|---|------|------|----------|-------------|
| 0 | draft | form | team | Author submits title, body, category |
| 1 | review | review | team | 2 approvals required. Reject → revision |
| 2 | revision | form | team | Author revises. Branch rule always routes back to review |
| 3 | publish | form | team | Final confirmation |

### Review Cycle

The rejection reroute creates a loop:

1. Author submits draft → enters review
2. Reviewer A approves, Reviewer B rejects → `reject_action: "revision"` routes to stage 2
3. Author revises → branch rule routes back to review (stage 1)
4. Both reviewers approve → advances to publish (stage 3)

### Signoff Gate (stage 1)

```json
{
  "validation": {
    "required_approvals": 2,
    "reject_action": "revision"
  }
}
```

---

## Package 4: Webhook Notifier

**Slug:** `webhook-notifier`
**Entry mode:** `team_only`
**Tier:** starlark
**Permissions:** `api.http`, `connections.read`, `db.write`

### Stage Flow

```
[configure]  →  [fire-webhook]  →  [result]
    team          automated          review
                  (http.post)
```

### Stages

| # | Name | Mode | Audience | Key Feature |
|---|------|------|----------|-------------|
| 0 | configure | form | team | URL + event name + payload |
| 1 | fire-webhook | automated | system | Starlark: http.post to webhook URL |
| 2 | result | review | team | Shows delivery status |

### Starlark Hook

**`on_fire(ctx)`** — Outbound HTTP:

```python
def on_fire(ctx):
    data = ctx["stage_data"]
    url = data.get("webhook_url", "")

    # Fallback to configured connection
    if not url:
        conn = connections.get("webhook")
        if conn:
            url = conn.get("url", "")

    if not url:
        return {"error": "No webhook URL configured"}

    payload = json.encode({
        "event": data.get("event_name", "workflow.notify"),
        "instance_id": ctx["instance_id"],
        "workflow_id": ctx["workflow_id"],
        "data": data
    })

    resp = http.post(url, body=payload, headers={
        "Content-Type": "application/json",
        "X-Switchboard-Event": data.get("event_name", "workflow.notify")
    })

    # Log the delivery
    db.insert("webhook_log", {
        "url": url,
        "status_code": str(resp["status"]),
        "event": data.get("event_name", ""),
        "instance_id": ctx["instance_id"]
    })

    return {"advance": True, "data": {
        "delivery_status": "delivered" if int(resp["status"]) < 400 else "failed",
        "status_code": resp["status"],
        "response_body": resp["body"][:500]
    }}
```

---

## Demo Surface

A browser-tier surface (`workflow-demo`) auto-installed on first run. Provides:

- Workflow cards with feature badges and stage flow diagrams
- "Try It" buttons for interactive exploration
- Starlark code viewer showing hook source
- API curl examples for each operation

Can be uninstalled by admin like any other package.

---

## Quick Start

```bash
# Build all example packages
cd packages && ./build.sh bug-report-triage employee-onboarding content-approval webhook-notifier workflow-demo

# Install via admin API
for pkg in dist/bug-report-triage.pkg dist/employee-onboarding.pkg dist/content-approval.pkg dist/webhook-notifier.pkg dist/workflow-demo.pkg; do
  curl -X POST $BASE/api/v1/admin/packages/install \
    -H "Authorization: Bearer $TOKEN" \
    -F "file=@$pkg"
done
```

Or with bundled packages (v0.3.8+): just `docker compose up` — all examples are auto-installed.
