---
title: "Workflow Studio"
slug: workflow-builder
section: Program Configuration
status: stable
description: "A visual workflow editor for designing, versioning, and managing applicant workflow definitions across programs and seasons"
last-updated: 2026-10-07
---

# Workflow Studio

A visual workflow editor for designing, versioning, and managing applicant workflow definitions across programs and seasons

> **Workflow Studio in Globus**
>
> The Workflow Builder (internally called Workflow Studio) is the primary tool for program administrators to define, configure, test, and publish the applicant workflows that power the Applicant Lifecycle . Every step an applicant sees in their portal, every document they upload, and every compliance gate they pass through is defined here.

## Architecture Overview

The Workflow Builder operates on a layered architecture that separates workflow definition from workflow execution. Definitions are authored in the builder and published as immutable versioned snapshots. The runtime engine ( Layer 4 — Workflow State and Evidence ) consumes these snapshots to drive applicant progress.

## 4-Node-Type System

Every element on the workflow canvas is one of four node types. This type system enforces structural consistency across all workflows while allowing flexible composition.

> **Composition Rules**
>
> Standard nodes can contain subtask children. Hub nodes can contain standard or subtask children. Placeholder nodes are always leaf nodes. These rules are enforced by the builder canvas — invalid nesting is prevented at drag-and-drop time, not at publish time.

## Step Library

The Step Library is a centralized catalog of reusable step templates. Program administrators browse, search, and insert steps from the library into their workflows. Steps are versioned independently from the workflows that use them.

**Test extraction route.** `POST /api/step-library/test-extraction` (API key scope `workflows.read` or `admin.write`, permission `programs.manage`) runs a trial AI extraction or translation against an uploaded sample. The body needs `fileBase64`, `fileName`, `mimeType` (JPEG, PNG, GIF, WebP or PDF), `action` (`extract` or `translate`) and `fieldLabel`; a missing field, another action, an unsupported type or a base64 string over 10485760 characters returns `400 VALIDATION_ERROR`. The response is `{ result, action, model }` and the call is audited as `step_library.test_extraction`.

### Versioning & Publishing

Each step in the library follows a semantic versioning model. When a step template is updated, existing workflows referencing the previous version are not affected until the workflow author explicitly upgrades. This ensures stability for in-flight applicants.

Version Model

Major version: breaking changes to step schema or behavior / Minor version: new optional fields or configuration / Patch version: content or label updates only / Each version is an immutable snapshot
Upgrade Policy

Patch upgrades: auto-applied to draft workflows / Minor upgrades: surfaced as suggestions, manual apply / Major upgrades: require explicit migration and review / In-flight applications: never affected by upgrades
### Deprecation Lifecycle

Steps move through a defined lifecycle from creation to retirement. Deprecation is a soft-removal mechanism that preserves existing workflows while preventing new usage.

> **Deprecation Safety**
>
> When a step is deprecated, the builder displays a warning badge on every workflow that still references it. Staff can continue to use those workflows, but new workflows cannot insert deprecated steps. A bulk migration tool assists with upgrading deprecated steps to their replacements.

### Browse, Search & Insert

The Step Library panel slides open from within the builder canvas. It provides:

## Builder Canvas

The builder canvas is the visual workspace where workflows are assembled. It renders the workflow as a structured sequence of phases, each containing ordered steps.

### Drag-and-Drop Editor

The canvas supports full drag-and-drop interaction for workflow assembly:

Reorder steps within a phase by dragging vertically / Move steps between phases by dragging horizontally / Insert new steps from the Step Library by dragging from the side panel / Nest subtasks under parent nodes by dragging onto a hub or standard node / Visual drop indicators show valid drop targets and insertion points / Undo/redo support for all canvas operations / Node composition rules enforced in real-time during drag
### Phase Management

Phases are the top-level containers in a workflow. They correspond to the Applicant Lifecycle phases and can be managed directly from the canvas:

Add Phase / Rename Phase / Delete Phase / Reorder Phases
Deleting a phase requires confirmation and only succeeds if the phase contains no steps. Steps must be moved or removed first. Phase reordering updates the sequence for new applications; in-flight applications remain on their bound workflow version.

### Inline Step Editing

Clicking any step on the canvas opens an inline configuration panel. Edits are reflected immediately on the canvas and auto-saved to the draft. The panel provides access to all step configuration options: labels, descriptions, role assignments, SLA targets, AI settings, visibility rules, and markdown content.

For an existing hub step (`nodeType` of `hub_step`, or `isHub` set), submitting the edit form calls the staged hub editor's `saveHub` with the step data. On success a "Step and subtasks saved" toast appears and the edit dialog closes. If the save fails, the staged editor shows the error and keeps every pending edit.

In the Workflow Studio, the Configure Hub panel manages the sub-tasks of a hub step. It also manages the hub's placeholder hub mappings and their conditions, through the `/api/placeholder-hubs` and `/api/placeholder-conditions` routes. Hub sub-task membership lives in two stores: `portal_hub_sub_tasks` (what the panel reads and writes through the `/api/hub-sub-tasks` routes) and the draft's `workflow_steps` rows (`node_type` `subtask_step` with `hub_key`), which publish validation and the runtime actually read. Every hub sub-task mutation (add, remove, required-flag change, reorder) is therefore projected into each draft definition of the same program and workflow type that already contains the hub step (a legacy row with no workflow type matches any type). Adding converts an existing same-key step in place or inserts a new `subtask_step` row; removal only deletes rows that are sub-tasks of that hub. When a qualifying draft and hub exist the projection must succeed or the request fails with an error (a failed add also deletes the just-created `portal_hub_sub_tasks` row). Before this projection, hubs configured in the panel failed publish with "Hub X has no subtasks"; the `20270811000000_backfill_hub_subtask_draft_steps` migration repaired drafts created earlier.

## Version Management

Every workflow has a complete version history. The builder enforces a strict draft/published lifecycle to prevent accidental changes to live workflows.

### Draft & Published States

Editable — all canvas operations available / Not visible to applicants or portals / Can be previewed via Admin Preview / Can be tested via Test Application Flow / Auto-saved on every change / Only one draft version exists at a time per program and workflow type (enforced by a database unique index, workflow_definitions_one_draft_uniq) / A draft whose version is at or below the current published version cannot be published (WorkflowVersionRegressionError) — create a new draft from the current published version instead
Immutable — no edits allowed / Active for new applications entering the workflow / Bound to each application at creation time / Diff-able against any other version / Full audit trail of who published and when (publishedBy / publishedAt on the definition) / Previous published versions retained indefinitely (archived)
> **Publish Flow**
>
> Publishing a draft creates a new immutable version snapshot. The system validates that every placeholder node has valid routing (an existing source step and field, at least one mapping with a value and a target hub, and mapped and default hubs that exist in the workflow), all required step configurations are complete, the prerequisite graph is valid (see Step Prerequisites), the effective V2 rule coverage has no blocking gaps (see Bundle Publish Gate), and the structural caps in Publish-Time Cap Validation are not exceeded before allowing publish. Publish requires the system-admin role; editing a draft requires programs.manage. When the program has active applications, publish also requires an explicit acknowledgement (acknowledgeActiveApplications: true), otherwise it returns a structured 409 carrying the active-application count.

### Version Invariants & Publication History

Workflow Studio selects the authoring heads deterministically: the current published head is the highest-numbered published version and the current draft head is the highest-numbered draft for the program and workflow type. Publication is a single guarded transaction:

- The publish transaction takes the workflow carrier lock and a pair-level (program + workflow type) publication lock, so two drafts can never publish concurrently and a reconciliation cannot restore the portal mirror for a definition that was just superseded.
- The transaction asserts the draft version is greater than the published version; a lower or equal draft is refused rather than replacing the published head.
- Callers that prepared a draft earlier (for example startup seeders) pass the publication head they expected; if another writer published in the meantime the publish is refused with WORKFLOW_PUBLICATION_HEAD_CHANGED.
- The previous published version is archived and the draft is promoted, recording publishedBy, publishedAt and a content hash that covers steps, phases and prerequisite edges.
- Every publish writes an immutable publication-history row (workflow_publish_history) holding the publisher, version, notes, and a snapshot of the definition, its steps, phases, prerequisite edges and the step-library versions in effect. Publication history is separate from Applicant Portal delivery/mirror attempts, which are audited separately (portal_mirror.synced / skipped_superseded / sync_failed).
- **Pinned snapshot resolution.** The Applicant Portal reads a workflow through `GET /api/integrations/applicant-portal/applications/:applicationId/workflow-snapshot` (integration scope `integrations.portal`). It resolves the version pinned on the application (`workflow_version_id`) from the publication snapshot recorded for that version (a legacy publication with no snapshot reads the pinned version's own rows), never the program's current head, and an archived version that was previously published is still served. Unresolvable pins return `422` with the code at the top level of the error: `WORKFLOW_VERSION_NOT_PINNED` (no pin), `WORKFLOW_VERSION_NOT_FOUND` (the pinned definition is gone), `WORKFLOW_VERSION_NOT_PUBLISHED` (never published), `WORKFLOW_SNAPSHOT_MALFORMED` (the stored publication snapshot cannot be read, with `details.issues`, or its frozen step-key list names steps the pinned version no longer defines, with `details.missingStepKeys`) and `WORKFLOW_PHASE_METADATA_UNAVAILABLE` (steps reference phases with no stored metadata). A broken pin is reported rather than silently shortening the applicant's journey.

> **Startup seeds never overwrite human publications**
>
> Seeded workflows are bootstrap defaults, not an authority over Workflow Studio. If the published version was not created by the seed and published by the seed actor, the seeder leaves it untouched and reports it as protected, even when the repository fixture differs. Seeds only create or update seed-owned drafts and publications, and they publish through the same guarded path (with the expected-head check above).

### Publish-Time Cap Validation (7 / 6)

Workflow Studio enforces two structural caps so the applicant portal stepper renders cleanly on the first screen an applicant ever sees: maximum 7 steps per phase and maximum 6 phases per workflow. The caps are enforced at three points in the draft &rarr; published lifecycle.

> **The 7-step cap counts applicant-facing steps only**
>
> Because the cap protects the applicant stepper, it counts only applicant-facing steps . Behind-the-scenes steps (e.g. sponsor or host interviews) never render on the stepper, so a phase may hold more than 7 total nodes as long as no more than 7 are applicant-facing. A phase can, for example, carry six applicant-facing steps plus a behind-the-scenes interview node and still publish.

Soft warning, non-blocking. When an admin drags in the 8th step of a phase or the 7th phase of a workflow, the canvas surfaces an inline warning recommending the workflow be restructured. The draft still saves. This is the same mechanism the help page calls out under Limits & Guardrails .

Hard validation gate, blocking. The publish endpoint runs the cap-validation module against every phase in the draft. Any phase with more than 7 steps, or a workflow with more than 6 phases, is rejected with a structured error naming each offending phase. No new published version is created.

Same gate, applied to legacy versions. If a workflow was published before the caps existed and a new draft of it is re-published, the same cap-validation module runs and rejects the publish until the workflow has been manually restructured into compliant shape. See Legacy Workflow Remediation .

> **Single source of enforcement**
>
> The cap-validation module is the only enforcement point. The applicant portal renders defensively with horizontal scroll if over-cap data ever reaches it (see Applicant Lifecycle — Stepper Fallback ) but never rejects, warns, or modifies workflow data. Conversely, the canvas warning is informational only — it does not block the draft save. All blocking happens at publish.

Two further refusals carry stable codes. **`WORKFLOW_STRUCTURE_INVALID`**: the publish transaction re-validates the draft's steps and phases and refuses a draft with structural problems with a validation error whose `details.code` is `WORKFLOW_STRUCTURE_INVALID` and whose `details.errors` lists each problem. Phase checks: a workflow with no steps or no phases; blank, padded, duplicated or undeclared phase keys; phases without an applicant-facing name or without steps; and steps not assigned to a phase. Step checks (the publish route also runs these first and reports them as a plain validation error without that code): hubs with no subtasks, subtasks whose parent hub is missing or unknown, hub or standard steps without an applicant journey position, placeholders with missing or invalid routing (source step and field, mappings, target hubs, default hub), and visibility conditions that reference a missing step or one that is not earlier in the workflow order. Invalid prerequisite edges (duplicates, self-reference, non-approvable or permanently unavailable prerequisites, and cycles) are checked by the publish route and reported the same plain way. **`WORKFLOW_STEP_DOMAIN_MISMATCH`**: HTTP 422 when a step is attached to a definition of the opposite domain, host-side or applicant-side, judged from the step library's `supported_workflow_types`, for example a `jo_*` job-order step on an `application` workflow. `details` carries `definitionWorkflowType` and `offending` (step key and domain). Steps whose library entry is unknown or mixed-domain pass. The guard runs on draft step creation and update, portal program-config population, data import, step upsert, bootstrap seeding and cloning a definition to a draft.

### Legacy Workflow Remediation

Workflows that were published before the caps were introduced and that already exceed 7 steps in any phase or 6 phases total are flagged as non-compliant. Their currently-published version continues to run for in-flight applications — existing applicants are never disrupted by a structural rule change — but the workflow is blocked from re-publishing until it has been restructured by hand. There is no automatic migration; the redesign is manual.

Notification

A single staff-targeted notification event, workflow_cap_violation_detected, is emitted by the cap-validation module:

Recipient: Karin Brettenny (single named recipient, configured in the notification catalog) / Channel: email only (no in-app, no SMS) / Backfill: one-time sweep on first deployment lists every currently-published workflow that exceeds the caps / Ongoing: re-fires when a newly-detected violation appears (e.g. a previously-compliant workflow that was just edited) / Payload: program name, sponsor, phase name, and the violated cap (steps-per-phase or phases-per-workflow) for each offending workflow
> **No silent migration, no automatic split**
>
> The platform never auto-splits a phase, never auto-merges phases, and never modifies workflow structure on behalf of an admin. Restructuring an over-cap legacy workflow is always a deliberate editorial action by Karin’s team. The notification exists so they know which workflows need attention without scanning every program manually.

### Content-Aware Reconciliation

Publishing a new workflow version is followed by a reconciliation pass that decides which in-flight applicants are repinned to the newly published version. The reconciliation pass is content-aware: it looks at what changed step-by-step, not just whether a new version was minted, and requires explicit human approval before mutating in-flight applicants when material content differs. Publish and reconciliation are separate calls: publish only needs the active-application acknowledgement described above; the approval gate lives on the apply-reconciliation call.

Reconciliation Contract

GET /api/workflow-studio/programs/:programId/reconciliation-preview returns contentChangedSteps[] — steps present in both the current published definition and the most recently archived one whose isRequired, configJson or conditionalConfigJson differ (the matching step-library name, description and schema are attached as informational after-values). Pure ordering/cosmetic changes are excluded. / When contentChangedSteps is non-empty the preview sets requiresApproval = true. POST /api/workflow-studio/programs/:programId/reconcile-applications without an approval returns 422 RECONCILIATION_APPROVAL_REQUIRED naming the changed step keys. / Re-call reconcile-applications with approval: { confirmedBy, reason } in the body (both non-empty) to acknowledge the impact and proceed. The approval envelope is persisted on the reconciliation run alongside the content diff so the decision is queryable later. / The approval requirement is not exempted for auto-reconcile or cron-driven triggers — the apply service never derives confirmedBy on the caller's behalf. / If contentChangedSteps is empty (e.g. pure ordering change, presentation-only tweak) reconciliation proceeds without an approval envelope.
Apply Behaviour

Apply runs in one transaction under the same pair-level publication lock as publish; if the published head changed since the preview it fails with a 409 conflict ("Preview and retry") instead of repinning to a superseded version. / Inside that transaction the Applicant Portal mirror (structure and field configs) for the published definition is rebuilt first — including no-op reconciliations where every application is already current — so reconciliation doubles as the repair path for a mirror that missed a publication. / Every affected application's workflowVersionId is repinned to the published definition in the same transaction, including content-only reconciliations whose structural step diff is empty. Steps added by the target version are stamped with the target version; previously removed steps are restored from their reconciliation tombstone instead of staying archived. / After commit, conditional visibility is re-evaluated for affected applications; a failure there marks the run completed_with_warnings rather than rolling back.
> **Why a hard gate, not a warning**
>
> Material content changes (new validation rule, new required attachment, changed completion contract) can retroactively invalidate work an applicant has already submitted. A soft warning would let an auto-reconcile job silently break in-flight applicants overnight. The RECONCILIATION_APPROVAL_REQUIRED gate forces a named human to sign off before that side-effect lands.

### Step-Edit Guard (Active Applicants)

Library-level step edits (changes to a shared step template that flows into every workflow using it) and direct workflow-step edits are guarded against destructive change while applicants are mid-flight on a workflow that references the step.

Guarded Endpoints

POST /api/step-library / PATCH /api/step-library/:id / PATCH /api/workflow-steps/:id / DELETE /api/workflow-steps/:id
Guard Contract

Before the mutation commits, the guard counts active applicants currently on any published workflow version that references the target step. / If activeApplicantCount > 0 the call returns 409 STEP_EDIT_BLOCKED_BY_ACTIVE_APPLICANTS with so the UI can surface exactly which programs are blocking and how many applicants are affected. / The guard never auto-clones the step or auto-bumps a version — restructuring is a deliberate editorial action (draft a new step, publish a new workflow version through Content-Aware Reconciliation, deprecate the old step via the standard lifecycle). / Read paths (GET) are never blocked. The guard is mutation-only. / The guard runs before the database transaction opens; a 409 leaves zero side-effects.
> **409, not 422**
>
> The guard is a conflict error, not a validation error. The edit itself is well-formed; the system state (active applicants on the step) is what makes it unsafe. Clients should treat the response as “retry after restructuring,” not as “fix your payload.”

### Side-by-Side Diffing

The version diff view displays two workflow versions side-by-side with visual indicators for changes. This is available between any two versions (draft vs. published, or any two historical versions).

Diff Indicators

### 360 Audit Trail

Every change to a workflow — at both the workflow level and individual step level — is recorded in a 360 audit trail. This provides a complete history of who changed what, when, and why.

Who changed it / What changed / Old value / New value
Tracked Operations

Step added, removed, or reordered / Step configuration changed (label, SLA, role, AI) / Visibility rule added, modified, or removed / Phase added, renamed, deleted, or reordered / Conditional branch created or modified / Version published or rolled back / Step Library template version upgraded / Markdown content or attachments updated
> **Step-Level History**
>
> The audit trail is queryable at the step level. Clicking any step shows its full change history across all versions — useful for understanding why a step was modified and by whom.

### Rollback

Any previously published version can be restored as the new draft. Rollback does not affect in-flight applications — they remain bound to their original workflow version. Rollback creates a new draft that is a copy of the selected historical version and must still go through the normal publish flow.

## Conditional Logic

The Workflow Builder supports conditional logic that controls step visibility, branching paths, and automated phase progression based on applicant data, program rules, and external signals.

### Branching Visualization

Conditional branches are rendered visually on the canvas as diverging paths from a decision point. Each branch is labeled with its condition and shows the steps that execute when the condition is met. The canvas highlights which branch an applicant would follow based on sample data in Test Application Flow Mode.

### Step Visibility Rules

Each applicant-facing step can have visibility rules that control whether it appears to the applicant. Unlike branching (which controls execution flow), visibility rules control portal rendering. A hidden step is skipped in the portal UI but its position in the workflow is preserved.

> **Hidden is not the same as behind-the-scenes**
>
> Visibility rules conditionally hide an applicant-facing step for some applicants based on their data — the step is still part of the applicant’s journey and may render for others. This is distinct from a behind-the-scenes step (e.g. a sponsor or host interview), which is staff/host-only by audience and never renders on the applicant portal for anyone. See Step Audience for that model.

Visibility Rule Examples

camp / returning / condition
> **Visibility vs. Branching**
>
> Visibility rules are for portal rendering — the step exists in the workflow but may not be shown to the applicant. Branching is for execution flow — the step may or may not execute based on conditions. A step can have both: it may only execute for certain branches AND have additional visibility rules within that branch.

### Step Prerequisites

A step can require one or more other steps to be staff-approved before applicants can start it. Unlike a visibility rule, a prerequisite keeps the step visible but locked, with an explanation that it is waiting for review. Prerequisites are explicit edges in the workflow definition (workflow_step_prerequisites); nothing is inferred from sort order or isRequired, and existing workflows without edges behave exactly as before.

- Authored on draft steps beside Visibility (PUT /api/workflow-steps/:id/prerequisites replaces a draft step’s prerequisite set; staff-only, cannot modify published or archived definitions). Multiple prerequisites use AND semantics.
- Only the tracked approved status satisfies an edge. Applicant submission, in-progress work, rejection and untracked snapshot completion do not unlock the dependent step; staff approval does.
- The canonical Step Access resolver evaluates the edges dynamically against the application’s pinned workflow version and returns access: locked with lock_reason_code UPSTREAM_STEP_INCOMPLETE and unlock conditions step.<key>.status equals approved. No section-lock rows are materialized. Malformed or cross-definition edges fail closed for the affected step.
- Validation rejects self-references, duplicate edges, edges to steps outside the workflow, non-approvable sources (only standard and subtask steps can be prerequisites), sources whose visibility is permanently unavailable, and directed cycles. It runs on draft edits, bundle import, Publish Preview and publish.
- Edges belong to a definition version: clone, publish (content hash and publication-history snapshot), export and import all preserve them, keyed by stable step keys.
- The builder surfaces prerequisite locks in list view (a “Blocked until staff approves” line), the Mind Map (count of required approvals) and Preview as Applicant (step stays visible but labelled locked).

### Step Audience (Behind-the-Scenes Steps)

Every step node carries an audience: applicant-facing or behind-the-scenes (staff-only or host-only). Audience is derived from the step’s role assignment : a step whose acting roles are all staff or host roles — with no applicant role — is behind-the-scenes. A sponsor or host interview is the canonical behind-the-scenes step.

> **Behind-the-scenes interviews stay in the step tree — they just never reach the applicant**
>
> An interview node remains a real node in the program’s step tree. Admins author it, see it on the canvas, order it, and bind its outcome like any other step. It is simply flagged behind-the-scenes by role assignment, so it is never sent to the Applicant Portal and never renders on the applicant stepper. This is the single model for interviews across the docs — an interview is not removed from the workflow, and it is not an applicant-facing step that happens to be hidden.

Behind-the-Scenes (audience)

- Staff-only or host-only by role assignment
- Never applicant-facing for any applicant
- Excluded from the applicant payload entirely — not transmitted with access = "hidden"
- Does not count toward the 7-steps-per-phase stepper cap
- Still produces facts/evidence that gate the phase (see below)
Conditionally Hidden (visibility)

- An applicant-facing step suppressed by a data-driven rule
- May render for other applicants whose data matches
- Carried in the contract as access = "hidden" on an applicant-facing step
- Counts as an applicant-facing step for the stepper cap
> **Gate bridge — a behind-the-scenes interview still gates the phase**
>
> Hiding an interview from the applicant does not weaken it as a gate. The interview node produces an outcome fact through the Step &rarr; Fact Contract (for the sponsor interview, e.g. screening.sponsor_interview.recommended_outcome), and that fact is what phase completion and the Rules Engine read. The host interview &rarr; job-order link is preserved — the outcome still carries its job_order_id so a passed (accepted) host interview can feed placement confirmation. Detailed outcome capture lives in the Interview Result Capture (P11.3 Work Type) spec.

### Phase Completion & Auto-Advance

The workflow engine continuously evaluates phase completion based on step states. When all required steps in a phase are complete (accounting for visibility rules and conditional branches), the engine can automatically advance the applicant to the next phase.

Detection Logic

Evaluates only visible steps (hidden steps skipped) / Hub completion requires all child nodes complete / Optional steps do not block phase completion / Branching: only the active branch is evaluated
Auto-Advance

Configurable per phase (on/off) / When enabled, phase completes automatically / Rules Engine gate still required between phases / Audit log records auto-advance events
Manual Override

Staff can manually advance phases / Override requires RBAC permission / Override recorded in audit trail / Rules Engine bypass requires separate permission
## Workflow-to-Rules Coverage Panel

The coverage panel provides condition-level traceability between the workflow steps and the Rules Engine structured rule definitions they satisfy. It reads from the rule_definitions table and maps each rule’s typed conditions and evidence sources to the workflow steps that produce the required evidence. It answers: “Does this workflow produce all the evidence the Rules Engine needs to make its decisions?”

Effective rules are resolved live — the rule set is the high-wins resolution of the published V2 regulatory bundles matching the program (the same resolution the Rules workspace uses), each rule pinned to its bundle-pinned (or current published) rule version, with fact sources taken from coverage bindings, the system-process registry and workflow-step producer metadata in the facts catalogue. Only facts reachable for the program’s category and visa type count: branches of a rule tree that cannot apply to the program are skipped, and program context is treated as structural rather than applicant evidence. The panel splits into two stacked sections: a Regulatory Coverage section grouped by regulatory rule bundle (where every rule must carry an explicit fact-source binding before publish is allowed), followed by the existing business-rule rows (where uncovered rules are warnings, not errors). The two sections share the same step → fact and rule → fact contracts, but only the regulatory section gates publish.

| Field | Description |
| --- | --- |
| label | description |

_(row above is a template, repeated for each data row)_

> **Behind-the-scenes steps still count as coverage**
>
> Coverage is keyed off the Step &rarr; Fact Contract , not off portal visibility. A behind-the-scenes interview node that declares an outcome fact (for the sponsor interview, e.g. `screening.sponsor_interview.recommended_outcome`) is credited as the covering source for any rule condition that requires it — exactly as an applicant-facing step would be. Keeping an interview off the applicant stepper never opens a coverage gap.

> **Gap Suggestions**
>
> When the coverage panel detects an uncovered condition, it suggests Step Library templates that could satisfy it. The evaluator metadata shows which rule evaluator (eligibility, compliance, SEVIS readiness) depends on the condition, helping administrators prioritize coverage gaps.

### Phase-Scoped Evaluation

The coverage panel uses lifecycle phase scoping to determine which rules are relevant to the current workflow. Each structured rule definition in the Rules Engine declares which lifecycle phase(s) it applies to via the lifecycle_phases array. The coverage panel only evaluates rules scoped to the workflow’s target phases.

| Rule Phase Scope | Coverage Panel Treatment | Publish Impact |
| --- | --- | --- |
| Application Flow (Phases 1–6) | Counted toward coverage percentage. Uncovered rules appear as gaps. | Compliance rules block publishing. Business rules produce warnings. |
| In Program (Phase 7) | Displayed in a separate informational section. Not counted toward coverage %. | No publish impact. Shown for awareness only. |
| Completion (Phase 8) | Displayed in a separate informational section. Not counted toward coverage %. | No publish impact. Shown for awareness only. |

> **Coverage Percentage Calculation**
>
> The coverage percentage is calculated as: (covered Application Flow rules) / (total Application Flow rules) × 100. On-Program and Completion rules are excluded from this calculation entirely. This prevents false gaps for rules like SEVIS check-in enforcement, monthly check-ins, and exit surveys that are evaluated outside the application workflow.

### Regulatory Coverage Section

Rules attached by a regulatory bundle (rows in program_rule_configs whose bundle_id is set) are pulled out of the general coverage list and shown in their own collapsible group, one group per attached bundle (J-1 Standard v3, Camp Counselor Addendum v2, etc.). Each row in a bundle group renders the rule key, the regulatory citation (rule_definitions.regulatory_basis), the required fact keys, and an inline binding picker.

> **Why bundle grouping matters**
>
> Sponsor review treats "every rule attached by the J-1 Standard bundle" as a single auditable unit. Showing the rules grouped by bundle (with the bundle key, version, and citation per row) is what lets a Compliance Officer hand a Workflow Studio screenshot to a sponsor and say "here is exactly what satisfies the SEVIS check-in rule for this program" — not just "coverage = 87%".

### Step → Fact Contract

*Status: design spec, only partly built. In code, step fact producers are declared in the facts catalogue (`workflowStepProducer` on the fact definitions that a workflow step produces, in `shared/factsCatalog.definitions.*.ts`) and the system-process registry (`shared/systemProcessRegistry.ts`). The `produces_fact_keys` array on the step template, the `source_kind` column and the "step does not declare its fact contract" message described below are not in the code.*

Each step template in the Step Library declares the canonical fact keys it produces when its completion condition is satisfied. This is what makes a step bindable as the source for a rule’s fact requirement. The contract lives on the step template (not on the workflow node) so it travels with the step across every workflow that inserts it.

type StepLibraryEntry ;
// Example: passport upload + verify step
documents.passport.upload_and_verify / passport.verified / passport.expiry_date / verification
- A step may produce zero, one, or many fact keys. Hub and placeholder nodes never produce facts (a placeholder publishes as long as its routing is valid).
- System processes are non-step fact producers (e.g. fee_collector, sevis_checkin_engine). They are registered in the code-seeded system-process registry (`shared/systemProcessRegistry.ts`) under a fixed processKey, so bindings (source_type = system_process) can target them when no workflow step is the right answer.
- Adding a new fact_key to a published step is a minor version bump (the produced surface only widens). Removing one is a major version bump and triggers a coverage recheck on every program that bound to it.
- Migration: every existing seeded step in the library is backfilled with its produces_fact_keys array before this gate goes live; steps that have not been backfilled cannot be bound and will show as "step does not declare its fact contract" in the coverage panel.
### Step Access & Lock Contract

Step locking is an authority problem, not a portal feature. The structure is authored here in the builder, but whether a given applicant can open, edit, or submit a step is derived by Globus and shipped to the Applicant Portal as read-only truth. The portal renders that state and enforces it in its UI; it never computes its own locks. If the portal evaluated rules locally you would get split-brain the moment the portal and Globus disagree — and the portal would win on screen while Globus wins on enforcement.

> **Globus derives access; the portal renders it**
>
> The Applicant Portal must never ask “should this step be locked?” It asks only “what did Globus say this applicant can do with this step?” Access is always evaluated against the application’s pinned workflow version (see Per-Application Version Tracking ) — republishing a workflow never retroactively re-locks an in-flight applicant. Locks only change on a fresh derivation, surfaced via the step.lock_changed event and reconciled on application.lifecycle_reconciled.

Status and access are two orthogonal enums — do not mix them. A step can be completed and still view_only. Status describes how far the applicant has gotten; access describes what they may do right now.

#### status

- not_started
- in_progress
- submitted
- approved
- rejected
- completed
#### access

- editable — applicant can complete or update the step
- view_only — can view submitted data, cannot change it
- locked — cannot open or edit yet
- hidden — step is not shown for this applicant
For every step in an application’s instance Globus returns a resolved access record. The portal renders sidebar, stepper, cards, form access, and buttons directly from it — including the capability booleans, which spell out the four actions the portal must gate independently:

type StepAccess ;
The lock cause maps onto the existing rule failure modes ( Rules Engine : block, warn, route_to_queue) rather than a parallel vocabulary:

- block &rarr; access: locked with a lock_reason_code.
- route_to_queue &rarr; locked + a staff gate that surfaces in the queue system with SLA timers — not just a boolean.
- warn &rarr; not locked; the step stays editable and the portal renders a proceed-with-warning state. A binary editable/locked model would lose this case.
- Globus returns a stable lock_reason_code plus structured unlock_conditions — the portal owns the human copy and i18n. Presentation text stays in the experience layer.
> **Applicant edit-until-review window**
>
> A submitted step does not immediately become uneditable (Aug 11 2026 release capsule). An applicant can keep editing a submitted step until a staff member begins their review, and a step returned for revision reverts to editable the moment it is returned. Submitting a step no longer locks it; the step stays editable through the resolved access record until staff review begins. This removed the back-channel correction requests that previously had to be handled via support tickets.

> **Request Changes and resubmission**
>
> Staff "Request Changes" on a step is still stored as step status rejected (for compatibility), but the decision must carry a reason drawn from the governed workflow-evidence review reasons (reason purpose workflow_evidence_review; exact-step reasons sort ahead of general ones; listed by GET /api/applications/:id/step-review-reasons, and any other reason is refused with WORKFLOW_REVIEW_REASON_NOT_APPLICABLE). The applicant-facing message is prefilled from the reason, staff may edit it (up to 2,000 characters), and the reason label and message are snapshotted on the decision and review session, separate from staff-only review notes. All deterministic validation runs before any decision, audit or tracked-status write, so a rejected request leaves no partial state. For Skills & Hobbies item-level review, Approve requires every item reviewed and approved, while Request Changes needs only at least one item marked changes_requested with an applicant-visible comment; other items may stay not_reviewed, and a published item that is still unresolved can drive a repeat whole-step Request Changes. A step cannot be approved while any published item is changes_requested or applicant_resubmitted.
>
> The Portal submit route (POST /api/portal/steps/:stepKey/submit) is the final Submit/Continue boundary: the unsupported isFinalSubmission field was removed, and every successful call runs the resubmission transition after the snapshot is saved. A rejected step moves back to submitted (recorded as an applicant resubmission) and its open changes_requested items become applicant_resubmitted; other steps are unaffected. Incremental saves go through the separate integrations applicant-portal events endpoint and never trigger this transition. Unresolved resubmissions are surfaced to staff on the Applicant Profile, the Applicants list and Attention views.

> **Enforce server-side, fail closed**
>
> The rendered lock is a hint. The authoritative gate is Globus re-checking access on write: a step.submitted against a step whose can_applicant_submit is false returns 403 STEP_LOCKED, regardless of what the portal showed. Delivery is pull for truth on load, push for liveness via step.lock_changed. The event carries only the access delta — it is a liveness signal, not the source of truth; consumers read unlock_conditions and the capability booleans by refetching the StepAccess snapshot (on change and on application.lifecycle_reconciled). If access state is stale or unreachable, compliance-gating steps default to locked — staleness must never become a bypass. Locking is processing-area-aware: pre-SEVIS work runs across concurrent processing areas , so a naive “previous phase must complete” linearization is wrong before the SEVIS convergence gate.

### Rule → Fact Contract

Each rule definition declares the fact keys it consumes via the required_fact_keys column on rule_definitions. For a regulatory bundle rule the field is mandatory — a rule with an empty required_fact_keys cannot be attached to a published bundle. For business rules the field is optional; when present it powers the same coverage matcher, when absent the panel falls back to the legacy evidence-source matching.

> **Source of truth for required_fact_keys**
>
> For rules authored as a typed rule tree , the engine derives required_fact_keys automatically by walking every leaf, derived, and time_window node and collecting their fact_key references. The column is the materialized result, kept in sync on every rule version publish so the coverage matcher does not have to re-walk the tree on every panel render.

### Coverage Binding Model

The binding itself lives in program_rule_coverage_bindings — one row per (program, regulatory rule):

| Column | Meaning |
| --- | --- |
| program_id | FK → programs. Bindings are program-scoped (see binding scope rules below). |
| rule_version_id | FK → rule_versions. The effective rule version being satisfied (bundle-pinned, or the rule’s current published version). The rule definition is derived from the version. |
| source_type | workflow_step \| system_process. Constrained enum, no free text. Coverage validation only counts workflow_step rows. |
| source_id | When source_type = workflow_step, the immutable step_key. When source_type = system_process, the registered process key. |
| fact_key | The single fact key this row supplies for the rule version; a rule needing several facts has one row per fact. Rows with a null fact_key (whole-rule rows) remain readable but never participate in coverage diagnostics. |
| notes | Optional free-text justification recorded for the audit trail (e.g. "using consular interview as the SEVIS-readiness proxy this season"). |
| created_at | Audit column (no created_by, updated_by or updated_at). Bindings are versioned by audit, not by row — see history modal. |

Validation rules (server-side, on save):

- source_key must resolve in the step library (or the registered system-process catalog) and the resolved entry must not be deprecated or archived.
- satisfied_fact_keys must be a subset of the source’s declared produces_fact_keys — you cannot claim a step satisfies a fact it does not produce.
- satisfied_fact_keys must be a superset of the rule’s required_fact_keys — every fact the rule needs has to be covered by this binding.
- A fact with deterministic provenance — one produced by an authoritative system process or by workflow-step producer metadata in the shared facts catalogue — cannot receive a manual binding; that provenance is platform-owned and is credited automatically.
- When the binding is a workflow_step and the workflow being authored does not yet contain that step, the panel marks the binding pending step insertion and surfaces a one-click "insert this step here" affordance.
Binding scope:

- Bindings are program-scoped. Seasons inherit the program’s bindings unmodified by default.
- A season may narrow a binding (swap one workflow_step for another that produces the same fact_key set, or substitute a system_process for a workflow_step). It may not blank a binding out — the publish gate evaluates the resolved (program ⊕ season) binding and blocks if the result is empty.
- Auto-suggest is allowed; auto-commit is not. When more than one step in the current workflow produces the required fact_key, the panel ranks the candidates but the Compliance reviewer must click to accept one. Single-candidate bindings are also click-to-accept (never silently set on the user’s behalf).
### Bundle Publish Gate

Workflow publish and Publish Preview both call the same canonical V2 coverage validator (validateV2WorkflowCoverage). It depends only on published V2 bundles, their high-wins rule resolution and program_rule_coverage_bindings — never on legacy compliance_rule_definitions or V1 eligibility rule sets, so equivalent V2 configuration gives the same publish decision in every environment. The publish endpoint runs it inline and rejects with a validation error listing the blockers (details: v2CoverageErrors, uncoveredRegulatoryRules, coveredRegulatoryRules). POST /api/workflow-versions/:id/validate-coverage (body: programId; permission compliance.bundles.read) exposes the same validator and returns HTTP 422 if any error is found for any effective rule:

| Failure mode | Reason code | What the message names |
| --- | --- | --- |
| Effective rule definition is missing from the Rule Library | BUNDLE_RULE_DEFINITION_MISSING | rule definition id |
| Effective rule version is not backed by a published version (or a pinned published/archived version of that rule) | BUNDLE_RULE_VERSION_UNPUBLISHED | rule name and key, expected vs found rule version id |
| Rule tree is invalid and cannot be covered | BUNDLE_RULE_VERSION_INVALID | rule name and key |
| A required fact key has no coverage binding, system-process provenance or active workflow-step producer | BUNDLE_RULE_BINDING_FACT_GAP | rule name and key, the missing fact key, suggested workflow step keys |
| No coverage binding for the program points at the rule's effective version | BUNDLE_VERSION_MISMATCH | rule name and key, expected vs found rule version ids |
| Binding points at a step that is retired, deprecated, archived or missing from the step library | BUNDLE_RULE_BINDING_SOURCE_RETIRED | rule name and key, source step key and its status |

> **Failures are a list, not a toast**
>
> The validate-coverage 422 response carries errors[] (one entry per offending rule with code, rule_definition_id, rule_key, rule_name, classification, message, suggested_step_keys[] and details), a non-blocking business_rule_warnings[] list of { message }, plus workflow_version_id and program_id. The publish dialog renders the same blockers as a list of blocking issues rather than a toast, so a Compliance reviewer can hand the failure list to a workflow author verbatim. Errors classified as business rules are returned as warnings and never block publish; only compliance-classified rules block the publish and preview.

Legitimate human-evidence gaps (for example applicantAge, passportExpiryDate, passportDataComplete) remain blockers until they are connected to real workflow steps. A program with no effective published V2 compliance rule publishes with a warning that it will run without program-rule enforcement.

### Regulatory Coverage Report

*Status: design spec, not built. There is no Download Regulatory Coverage Report action in the Workflow Studio toolbar, and bindings carry no author or updated_at columns.*

The Workflow Studio toolbar exposes a Download Regulatory Coverage Report action available on any draft or published workflow version, scoped to the current program (and optionally a specific season). The report is generated using the same print-styling pattern as the existing audit exports and is downloadable as PDF or printable HTML.

Report Layout

- Header: program name, season (if scoped), workflow version, generated_at, generated_by, applied policy_bundle_version.
- One section per attached regulatory bundle (key + version + status pill).
- Within each bundle, one row per member rule with: rule key, name, regulatory citation, required_fact_keys, binding source_kind + source_key, satisfied_fact_keys, binding author, binding last-updated_at.
- Rules with no binding render as a single "Uncovered" row in red with a placeholder citation block so the document is still complete and printable.
- Footer: full publish-gate validation status (pass / fail) and the count of unbound rules at the time of generation.
> **Report is a snapshot, not a live view**
>
> The report is timestamped and embeds the workflow version id, the program rule binding rows as of the generation time, and the bundle versions in effect. It is intended to be archivable — sponsor reviews reference a fixed PDF, not a live URL.

## Testing & Preview

Before publishing a workflow, administrators can validate it using three complementary tools: Test Application Flow Mode, Portal Contract validation, and Admin Preview.

### Publish Preview

GET /api/workflow-studio/definitions/:id/publish-preview (draft definitions only) returns the diff against the current published version, the active-application count and a publishable flag with blockers[] and warnings[]. The blockers are computed with the same gates as the publish endpoint — structural validation, phase requirements, prerequisite-graph validation, the 7/6 caps, the linked-object gate for imported workflows, and the canonical V2 coverage validator — so the preview never reports a draft as safe when publish would reject it. The Publish dialog lists the server blockers with its own client checks and disables Publish while any blocker exists, publishable is false, or a regulatory rule is uncovered. Phase-milestone coverage gaps are warnings, not blockers.

### Test Application Flow Mode

Test Application Flow Mode creates a simulated applicant that walks through the workflow step by step. The builder shows which steps are active, which branches are triggered, and which phase transitions fire.

Capabilities

Configure sample applicant data (nationality, program, season, documents) / Step through the workflow one step at a time or auto-play / See which conditional branches activate for the test data / Verify phase completion detection fires correctly / Test visibility rules against different applicant profiles / Validate Rules Engine coverage with synthetic evidence / Export test results as a validation report
### Portal Contract

The portal contract defines how each workflow step renders in the Applicant Portal . The builder validates that every step has a valid portal contract before allowing publish.

> **Step mirror written synchronously at publish**
>
> The applicant-portal step mirror is written synchronously in-process at publish time (Aug 11 2026 release capsule). Previously the mirror was written asynchronously after publish, which created a race where applicants could see a stale step view immediately after a workflow went live. Writing the mirror in-band at publish time closes that race — the portal reflects the newly published definition the moment publish completes.

**Placeholder resolution.** `POST /api/workflow-studio/portal-contract/:programId/:workflowType/resolve-placeholder` (scope `workflows.read`, permission `programs.manage`) resolves a placeholder step of the published workflow against submitted answers. The body needs `placeholderStepKey` and `fieldData` (a map of field keys to values). The routing config reads `fieldData` at its source field and returns the mapped hub, or the default hub when no mapping matches. The response carries `placeholderId`, `resolvedHubId`, `resolvedSteps` (`stepKey`, `displayName`, `sortOrder`, `isRequired` for the hub's subtasks), `matchedValue` and `matchType` (`exact`, `default` or `none`; `none` returns a null hub and no steps). 404 when the program has no published definition of that type or the step key is not in it; 400 when the step is not a placeholder or has no valid routing configuration. The portal-facing `POST /api/portal/resolve-placeholder` takes `programId` and `workflowType` in the body and returns only `placeholderId`, `resolvedHubId` and `resolvedSteps`.

### Host Portal Contract

The Host Portal Contract defines how each workflow step’s completed output is displayed in the Host Portal . While the Applicant Portal Contract controls how the applicant fills in a step, the Host Portal Contract controls how the host views the submitted data. When a new step is added to a workflow, the Host Portal renders it automatically based on this contract — no Host Portal development required.

Dynamic rendering principle: When a workflow adds step 14, the Host Portal displays it without dev work. The published workflow version is the single source of truth for what the host sees on an applicant’s profile — phases, steps, completion status, and submitted data are all driven by the workflow definition.

> **Host interviews are host-facing behind-the-scenes steps**
>
> A host interview is a behind-the-scenes step whose audience is the host (not the applicant): it renders in the Host Portal and never on the applicant stepper. The host records its outcome through the Interview Result Capture surface, and the outcome keeps its job_order_id so a passed (accepted) host interview still feeds the placement match it was for.

### Admin Preview

Admin Preview renders the draft workflow as the applicant would see it in the portal. Unlike Test Application Flow (which simulates execution), Admin Preview focuses on the visual presentation — layout, labels, field ordering, help text, and step grouping.

### Showcase Yourself — Staff Review Rendering

Shipped Sep 1 2026. The portable form engine selects its injected **Showcase** renderer by *field* type, not by the parent step type. The served workflow deliberately keeps the Showcase step's `photo_gallery` field as `file_upload_advanced` so applicants collect photos with the ordinary uploader; when the same step is rendered in **staff** or **readonly** mode, the engine's normalisation step (`normalizeFieldsConfigForEngine`, keyed on `stepType === "showcase_yourself"`) retypes that field to `showcase_yourself` so reviewers see the submitted photos as a gallery instead of generic filename rows. This is a presentation-only normalisation — the stored step schema, the applicant-facing collection experience, and the submitted data are unchanged.

On the shared `showcase_yourself_v1_0` step, the **Video Link** (`video_introduction`) is optional: a migration sets `required` to false in both `step_library.schema_json` and `portal_step_library.fields_config`, and the field description now reads as optional. A supplied link is still validated (the engine accepts HTTP(S) YouTube, Vimeo or Google Drive links), and the minimum of three photos is unchanged.

## Step Configuration

Each step on the canvas has a rich configuration panel. These settings control how the step behaves at runtime, who is responsible for it, and what deadlines apply.

### Free-Text Length Bounds (minWords / maxWords / minLength)

Shipped Sep 2026. Declared free-text bounds in the step library are enforced server-side from the **same** field configuration the form renders, so a bound is live the moment it is declared on any step — previously only Skills & Hobbies had hand-written word enforcement and a `minWords`/`minLength` declared elsewhere was inert.

| Bound | Convention | Enforcement |
| --- | --- | --- |
| `minWords` / `maxWords` | Current word-limit contract | Server-side on submit; issue names the field, word count and limit. |
| `minLength` | Older character-minimum convention still used by a handful of fields (e.g. the **Personal Statement** box) | Server-side on submit against the **raw, untrimmed** character count (matching the renderer's `lengthBounds`, so a whitespace-padded value cannot pass one side and fail the other). Message: "*Label* must be at least N characters (currently M)." |
| `maxLength` | — | Deliberately **not** re-enforced server-side: the renderer already applies it as a hard input cap, and adding a server gate would be new, unreviewed behaviour for fields that never asked for it. |

Renderers can read `collectDeclaredMinLength(fieldsConfig)` to show a live "n of N characters" hint on a `minLength` field instead of a hardcoded number. Bounds apply to scalar fields and to repeated-item fields (per item).

### Role Assignments

Steps can be assigned to specific RBAC roles . The assignment determines which staff members see the step in their work queues and who receives SLA notifications. Role assignment is also what derives a step’s audience : a step assigned only to staff or host roles (no applicant role) is behind-the-scenes and never renders on the applicant portal — this is how sponsor and host interviews are kept off the applicant stepper.

Primary assignee: the role responsible for completing the step / Reviewer: the role that approves or verifies the step outcome / Escalation target: the role notified if SLA is breached / Scope: assignment respects brand and program scope boundaries / Audience: staff/host-only assignment (no applicant role) makes the step behind-the-scenes
### SLA Targets

Each step can define SLA targets that trigger notifications and escalations when deadlines approach or are breached. SLA configuration integrates with the Queue System’s SLA management .

SLA Fields

Response time target (hours/days) / Resolution time target (hours/days) / Warning threshold (% of target elapsed) / Escalation action (notify, reassign, flag)
Scope Hierarchy

Step-level SLA (most specific, highest priority) / Phase-level SLA (applies to all steps in phase) / Workflow-level SLA (global default) / System-level SLA (fallback from Queue System config)
### AI Configuration

Steps that involve document review or data validation can be configured with AI assistance. The AI configuration controls how automated review supplements human decision-making.

AI review enabled/disabled toggle per step / Confidence threshold: minimum AI confidence score to auto-approve / Fallback behavior: route to human reviewer below threshold / AI model selection: document classification, OCR extraction, compliance check / Training data reference: which verified documents train the model / Audit: all AI decisions logged with confidence scores and model version
> **AI as Assist, Not Authority**
>
> AI review is always configured as an assist layer. It can pre-screen, flag, and suggest — but compliance-critical decisions (SEVIS readiness, eligibility determinations) always require human confirmation unless the program administrator has explicitly enabled auto-approval above a configured confidence threshold.

### AI Capabilities

Each AI surface in Globus is a registered capability with its own prompt, model configuration, and invocation log. Steps reference capabilities by key when AI assist is enabled. The catalog below names the capabilities the doc and codebase share vocabulary on.

> **Planned capabilities are not yet wired**
>
> Translation Assist and Anomaly Detection are documented here as named capabilities so the doc and gap-analysis vocabulary line up. Neither is registered in the ai_capabilities table today; do not bind workflow steps or rule actions to them until they ship.

### Markdown Content & Attachments

Each step can include rich markdown content that is rendered to the applicant in the portal. The markdown editor supports standard formatting, embedded images, and document attachments.

`POST /api/workflow-studio/steps/:stepId/attachments` (API key scope `workflows.read` or `admin.write`, permission `programs.manage`) uploads one multipart `file` (10 MB limit) and appends `{ name, type, url, uploadedAt }` to the step's `attachments` in its `configJson`, returning that object. The file goes to GCS under `workflow-attachments/` when GCS is available, otherwise it is embedded as a `data:` URL. It returns `400` without a file, `404` for an unknown step or definition, and `409` when the definition is published or archived or another editor holds the draft lock.

## Per-Application Version Tracking

Each application record stores a workflowVersionId that binds it to a specific published workflow version. This is the mechanism that ensures in-flight applications are never affected by workflow changes.

Binding Rules

Set at application creation time / Points to the currently published version / Immutable once set (default behavior) / Migration available via admin action
Version Migration

Admin can migrate applications to new version / Migration maps old steps to new steps / Completed steps preserved (not re-required) / Migration audit trail records before/after / Reconciliation repins active applications to the published definition in a single transaction (see Content-Aware Reconciliation)
Reporting

Dashboard shows version distribution / Count of applications per workflow version / Alerts when old versions have active apps / Migration impact preview before execution
> **Stability Contract**
>
> The per-application version binding is the core stability guarantee of the Workflow Builder. An applicant who started their journey on workflow version 3 will complete that journey on version 3, even if the program administrator has since published versions 4 and 5. This prevents mid-process disruption and ensures auditability — the exact workflow an applicant experienced is always recoverable.

## Program Renewal Workflow

The Program Renewal Workflow is a specialised workflow type for participants who are returning for a subsequent season. It re-uses verified data from the original application (carry-forward) while collecting only the fields that must be refreshed.

> **Program Renewal vs Program Extension vs Reservation Extension**
>
> Program Renewal is a new application for a new season (new DS-2019). Program Extension extends the current program end date on the same DS-2019. Reservation Extension is unrelated — it is a partner extending a time-limited hold on a job position (see Partner Governance). Program Renewals create a new application record; Program Extensions modify the existing one. See Program Extension & Program Renewal Requests .

## J-2 Dependent Workflow

J-2 dependents (spouse or minor children of a J-1 participant) follow a parallel workflow that is linked to — but separate from — the primary participant’s workflow.

| Aspect | Primary (J-1) | Dependent (J-2) |
| --- | --- | --- |
| Workflow Trigger | Applicant starts application | Primary adds dependent during or after their own workflow |
| Required Documents | Full document set (passport, background check, etc.) | Passport, relationship proof, insurance only |
| SEVIS Record | Independent SEVIS record | Linked SEVIS record (references primary’s SEVIS ID) |
| DS-2019 | Own DS-2019 form | Separate DS-2019 tied to primary’s program |
| Phase Advancement | Independent phase progression | Cannot advance past primary’s current phase |
| Termination | Independent (but terminates all J-2s) | Auto-terminated if primary is terminated |

> **J-2 Phase Ceiling**
>
> A J-2 dependent can never be in a phase ahead of their primary J-1 participant. If the primary is in Phase 3 (Placement), the dependent cannot advance to Phase 4 (Visa) until the primary does. This is enforced by the workflow engine, not by manual staff action.

## Check-In Templates

Check-In Templates define the form structure for monthly participant check-ins. Templates are managed in Workflow Studio and can be customised per program and season.

Default Template Fields

Current Address / Employment Status / Hours/Week / Well-Being / Employer Confirm / Travel Plans / Concerns / Signature
## Phase Library

The Phase Library provides reusable phase templates that can be inserted into any workflow. Instead of building phases from scratch, workflow editors can browse pre-configured phase blueprints and customize them after insertion.

| Phase Template | Included Steps | Programs |
| --- | --- | --- |
| Standard Intake | Application form, Document uploads, Background check, Emergency contacts | All |
| Interview & Selection | Interview node (behind-the-scenes — staff/host-only, off the applicant stepper), Interview evaluation, Selection decision | Work & Travel, Intern |
| Placement & Housing | Job order match, Housing assignment, Placement confirmation | Work & Travel |
| Visa Processing | DS-2019 generation, SEVIS registration, Embassy interview prep | All |
| On-Program Monitoring | Monthly check-in, Site visit scheduling, Compliance review | All |
| Program Completion | Exit survey, Final check-in, SEVIS status update, Grace period tracking | All |

## Step Key Governance

Every step in a workflow has a unique step_key that serves as a stable identifier across versions. Step keys are critical for rules engine bindings, reporting queries, and portal contract mappings.

> **Step Key = Contract**
>
> Step keys are the contract between the workflow engine and every other system that references workflow steps. Changing a step key after publication would silently break rules, reports, and portal integrations. The system enforces immutability to prevent this class of errors.

> **Field-level companion: classification metadata**
>
> The step_key identifies the step. It does not describe the fields the step captures — whether a value is biographical (applicant-scope) or per-attempt (application-scope), whether downstream systems are allowed to bind to it, or which partner surfaces should render it. Today, that classification is decided implicitly by hardcoded knowledge inside biographicalSync. The required field-level metadata, the authoring decision tree, the worked examples, and a baseline audit of biographicalSync live on the Field Classification Contract page.

## Draft Lock & Auto-Save

Workflow Studio supports collaborative editing with draft locking and automatic save:

Only one editor holds a draft at a time. A lock (table `workflow_draft_locks`, one row per definition) records `locked_by`, `locked_by_name`, `acquired_at` and `last_active_at`, and expires 15 minutes after `last_active_at`. While another user's unexpired lock is held, writes to the draft (steps, conditions, prerequisites, phases) are refused with a message naming the holder; an expired lock, or your own, does not block. All routes below require `programs.manage`.

| Method | Path | Behavior |
| --- | --- | --- |
| GET | `/api/workflow-definitions/:id/lock` and `/lock/status` | Same handler. Returns `locked: false`, `locked: false` with `expired: true`, or `locked: true` with the `lock` row. |
| POST | `/api/workflow-definitions/:id/lock/acquire` | Draft definitions only. 409 with `lockHolder` (name and last active time) when another user's lock is unexpired; an expired or own lock is released and re-acquired. Returns `acquired: true` and the lock. |
| POST | `/api/workflow-definitions/:id/lock/refresh` | Refreshes `last_active_at`; returns `refreshed` true or false. |
| POST | `/api/workflow-definitions/:id/lock/release` | Releases the caller's lock; returns `released`. |
| GET | `/api/workflow-definitions/:id/checkpoints` | Draft change history, newest first, paged with `limit` (default 50, maximum 200) and `offset`; returns `items`, `limit` and `offset`. |
| GET, POST | `/api/workflow-definitions/:definitionId/phases` | List phases; create a phase (201). Published or archived definitions are refused, the draft lock applies, and a milestone status target that moves an applicant backwards or jumps a durable segment is rejected. |
| PATCH, DELETE | `/api/workflow-phases/:id` | Edit or delete a phase; same immutability and lock rules. |
| POST | `/api/workflow-definitions/:definitionId/phases/reorder` | Body `phaseIds`, the ordered list of phase ids. |

Draft edits record a checkpoint row in `workflow_draft_checkpoints` (for example adding a phase, reordering phases, or updating step conditions): `workflow_definition_id`, `user_id`, `user_name`, `change_type`, `entity_type`, `entity_id`, `summary`, an optional `snapshot_json` and `created_at`. A failure to record a checkpoint is logged and does not fail the edit.

## Coverage Gap Panel — Expanded

The Workflow-to-Rules Coverage Panel evaluates every step in the workflow against the rules engine to identify gaps. The expanded panel provides deeper analysis:

| Analysis | What It Checks | Gap Indicator |
| --- | --- | --- |
| Step-to-Rule Mapping | Every workflow step has at least one validation rule bound to it | Red badge on uncovered steps |
| Rule-to-Step Mapping | Every active rule references a valid (non-deprecated) step key | Warning on orphaned rules |
| Phase Gate Coverage | Every phase transition has at least one gate rule defined | Amber badge on ungated transitions |
| Document Completeness | Every required document type has a corresponding upload step | List of document types without steps |
| SLA Coverage | Every review or approval step has an SLA target defined | Grey badge on steps without SLA |
| Conditional Branch Reachability | Every conditional branch is reachable (no dead branches) | Strikethrough on unreachable branches |

## Workflow Import / Export

Staff can export a complete workflow definition as a self-contained, portable bundle and re-import it into any environment or program (Aug 11 2026 release capsule). This turns a workflow design into a transferable artifact instead of something that has to be rebuilt by hand each time.

The export bundle captures the entire definition — not just the canvas shape:

- Steps (with their configuration, role assignments, SLA targets, and markdown content)
- Conditional logic and branching conditions
- Sub-tasks and the hub structure that contains them
- Field configurations
- Agreements
- Compliance mappings
- Step prerequisite edges, carried by stable step key and re-validated on import (an edge that references a step not in the workflow is rejected)
- Brand legal policies — added to the transfer surface so brand-scoped legal terms travel with the workflow rather than being re-authored per environment
- Rule bundles owned by, or scoped to, the program or one of its seasons. Only each bundle's effective head is selected: the highest-numbered published, non-retired version, chosen before scope matching, so draft, archived and retired history, and an old program scope on a superseded version, are not selected

Import Engine

The import engine validates and applies every entity category individually, reporting errors per-entity so a single bad field never aborts the whole import. What can be applied is applied; what fails is reported against the specific entity that failed, so operators fix a narrow, named problem rather than re-running a blind all-or-nothing job.

- Per-entity-category validation with per-entity error reporting — one bad field never aborts the whole import.
- Dry-run mode lets operators verify exactly what would change before committing anything.
- A content-hash uniqueness guard prevents silent version drift — the same definition cannot be re-imported as a divergent duplicate without detection.
- Import targets the newest draft, or the newest published version when no draft exists. Archived versions are immutable history and are never an import target, even when their version number is higher.
- Replacing an existing draft needs an explicit yes. Without `acknowledgeDraftOverwrite: true`, `POST /api/workflow-studio/workflow-bundles/import` (permission `programs.manage`) returns `409 CONFLICT` with `details.code` `DRAFT_OVERWRITE_NOT_ACKNOWLEDGED` and the `targetId` and `targetVersion` of the draft that would be deleted and rebuilt.
- Steps that applicants are part-way through need per-step consent. `acknowledgeStepContentEdits` is a list of step keys taken from the dry run's `stepEdits`; any blocked step key missing from it returns `409 CONFLICT` with `details.code` `STEP_EDIT_NOT_ACKNOWLEDGED` and `details.steps` naming each step and its active applicants. Both acknowledgements are checked before a transaction opens and again inside the apply, and the audit entry records only the waivers that were actually applied.

Rule-coverage configuration is portable too. Coverage bindings travel in the configuration export only when they are operator-authored wiring for a carried rule version; bindings for facts with deterministic provenance (system process or workflow-step producer in the shared facts catalogue) are platform-owned, recreated in every environment, and omitted from the export.

> **Why portability matters**
>
> Workflow designs that previously had to be recreated by hand across brands or seasons can now be cloned in one operation. QA can promote a curated workflow from staging to production without a developer, and Operations can transfer a proven workflow template to a new partner program without rebuilding from scratch.

## Implementation Tickets — Step & Phase Caps

The two Asana tickets below are drafted verbatim in the house format documented in the Asana Ticket Standard . They cover Workflow Studio enforcement and the applicant portal scroll fallback. Paste each block as-is into Asana — this site is the source of truth for the ticket text until the tickets are filed. The companion workflow_cap_violation_detected notification event is documented as a design rule in the Workflow Studio Guide and is not filed as a separate engineering ticket.

Ticket A — Workflow Studio enforcement (lands in P11 / P11B)
Title: Workflow Studio: step & phase caps with soft authoring warning, hard publish-time validation, and legacy republish block

Body:

FOR PRODUCT OWNERS / STAKEHOLDERS
Sponsor admins can currently build workflows with unlimited steps per phase and unlimited phases. Long phases break the applicant portal "My Application" stepper because it renders steps horizontally. This ticket caps phases at 6 per workflow and steps at 7 per phase, warns admins as they author, blocks over-cap workflows from publishing, and refuses to re-publish any legacy workflow that already breaks the cap until it has been restructured.
----------------------------------------
BACKGROUND
Workflow Studio (\`workflow-builder.tsx\`) lets sponsor admins draft workflows on a drag-and-drop canvas with phases as swim lanes and steps inside each phase. The draft → published lifecycle (documented in the Workflow Studio Lifecycle / P11/P11B section) currently has no structural validation gate at publish time. Any number of steps and any number of phases will publish successfully. The published workflow is then read by the applicant portal "My Application" surface, which renders phases as top tabs and steps as a horizontal stepper row.
The applicant portal stepper has no overflow handling today — long phases wrap to multiple rows, breaking layout on the first screen applicants see.
WHAT THIS TICKET DOES
- Adds an authoring-time soft warning in Workflow Studio that fires when adding a step would push a phase past 7 steps, or when adding a phase would push the workflow past 6 phases. The warning is informational and does not block the draft save.
- Adds a publish-time hard validation gate in the Workflow Studio Lifecycle publish flow that rejects publish on any draft exceeding 7 steps per phase or 6 phases total. The publish endpoint returns a structured error naming each offending phase.
- Adds the same hard validation to the re-publish path for any currently-published workflow, so legacy workflows that already exceed the caps cannot be re-published until they have been manually restructured into compliant shape.
- Emits a single staff-targeted notification event "workflow_cap_violation_detected" to Karin Brettenny (email-only, single named recipient) on a one-time backfill sweep when this module is first deployed, and again whenever a newly-detected violation appears. The event itself is configured in the Notification & Communication Model catalog and does not require a separate engineering ticket.
- References companion ticket "Applicant Portal: horizontal-scroll fallback on My Application stepper for over-cap phases and phase tabs".
ACCEPTANCE CRITERIA (Gherkin)
Feature: Workflow Studio step and phase caps
Scenario: Authoring-time warning fires at the 8th step in a phase
Given a sponsor admin is editing a draft workflow in Workflow Studio
And the phase "Intake" already contains 7 steps
When the admin drags an 8th step into "Intake"
Then Workflow Studio displays a soft warning recommending the admin split "Intake" into a new phase
And the draft save still succeeds
Scenario: Authoring-time warning fires at the 7th phase
Given a sponsor admin is editing a draft workflow in Workflow Studio
And the workflow already contains 6 phases
When the admin adds a 7th phase
Then Workflow Studio displays a soft warning that the workflow exceeds the recommended 6-phase cap
And the draft save still succeeds
Scenario: Publish is rejected when any phase exceeds 7 steps
Given a sponsor admin has a draft workflow with 8 steps in the phase "Intake"
When the admin clicks "Publish"
Then publish is rejected with a validation error
And the error names "Intake" as the offending phase
And no new published version is created
Scenario: Publish is rejected when the workflow exceeds 6 phases
Given a sponsor admin has a draft workflow containing 7 phases
When the admin clicks "Publish"
Then publish is rejected with a validation error
And the error lists the workflow as exceeding the 6-phase cap
And no new published version is created
Scenario: Re-publish of a legacy over-cap workflow is blocked until restructured
Given a workflow was published before the cap was introduced
And one of its phases contains 12 steps
When a sponsor admin opens the workflow and clicks "Publish" on a new draft version
Then publish is rejected with a validation error naming the offending phase
And the workflow remains on its previous published version
And the error message instructs the admin to restructure the workflow before re-publishing
Scenario: Build-time guard prevents bypassing the validation
Given a developer attempts to add a new publish path that does not call the cap-validation module
When the test suite runs
Then a lint or test fails with a message that all publish entry points must invoke the cap-validation module 
Ticket B — Applicant Portal scroll fallback (lands in Applicant Portal / Applicant Lifecycle)
Title: Applicant Portal: horizontal-scroll fallback on My Application stepper for over-cap phases and phase tabs

Body:

FOR PRODUCT OWNERS / STAKEHOLDERS
The "My Application" screen in the applicant portal renders workflow phases as top tabs and steps as a horizontal row of numbered circles. Today, if a workflow has too many steps or phases, the row wraps and the screen looks broken. This ticket makes the portal degrade gracefully: when there are too many steps or phases to fit, the row scrolls horizontally instead of wrapping, so applicants always see a clean stepper even on legacy or over-cap data.
----------------------------------------
BACKGROUND
The applicant portal "My Application" surface receives published workflow data from Globus and renders it as a phase-tabs row at the top and a step-circles row below the active phase. The portal currently has no overflow handling on either row; long lists wrap to multiple lines and break layout. Companion ticket "Workflow Studio: step & phase caps with soft authoring warning, hard publish-time validation, and legacy republish block" introduces enforcement upstream, but the portal must still render legacy data, override paths, and edge cases defensively without ever wrapping or hiding steps.
WHAT THIS TICKET DOES
- Wraps the steps row in a horizontally-scrolling container that activates whenever the rendered step count exceeds what fits in the available width.
- Wraps the phase-tabs row in the same horizontally-scrolling container with the same activation rule.
- Preserves the active step / active phase as the scroll anchor on initial render so the applicant lands looking at where they are, not at step 1.
- Does not truncate, hide, condense, or wrap any steps or phases under any circumstance.
- The portal is not the enforcement point — it never rejects, warns about, or modifies workflow data based on cap violations.
- References companion ticket "Workflow Studio: step & phase caps with soft authoring warning, hard publish-time validation, and legacy republish block".
ACCEPTANCE CRITERIA (Gherkin)
Feature: My Application stepper horizontal-scroll fallback
Scenario: Steps row scrolls horizontally when over the cap
Given an applicant opens "My Application"
And the active phase contains 13 steps
When the page renders on a 1280px-wide viewport with the sidebar open
Then the step-circles row appears in a single horizontal line
And the row scrolls horizontally
And no step is hidden, wrapped, or truncated
Scenario: Phase tabs scroll horizontally when over the cap
Given an applicant opens "My Application"
And the workflow contains 8 phases
When the page renders
Then the phase-tabs row appears in a single horizontal line
And the row scrolls horizontally
And no phase tab is hidden, wrapped, or truncated
Scenario: Active step is the scroll anchor on initial render
Given an applicant is on step 9 of a phase containing 13 steps
When the page first renders
Then the step-circles row is scrolled so that step 9 is visible without manual scrolling
Scenario: Active phase is the scroll anchor on initial render
Given an applicant is on phase 6 of a workflow containing 8 phases
When the page first renders
Then the phase-tabs row is scrolled so that phase 6 is visible without manual scrolling
Scenario: Compliant workflow renders with no scroll
Given an applicant opens "My Application"
And the active phase contains 5 steps
And the workflow contains 4 phases
When the page renders on a 1280px-wide viewport
Then both rows fit without horizontal scroll
And no scroll affordance is shown
Scenario: Portal does not enforce caps
Given a published workflow with 13 steps in a phase reaches the applicant portal
When the page renders
Then the portal renders all 13 steps via horizontal scroll
And the portal does not log, warn, surface, or modify the workflow based on the cap violation
