# Workflow Scaffold

<!-- BEGIN GENERATED: guide-map -->
## Guide map

Read only the section needed for the next decision. Line ranges are exact
for this generated runtime target.

| Section | What it covers | Lines |
| --- | --- | --- |
| [Purpose](#purpose) | Scope, ownership, and the central operating question. | 28-34 |
| [A Play Is A Repo](#a-play-is-a-repo) | The scaffold exists because **plays must be re-runnable from a clean checkout**. | 35-62 |
| [Use This When](#use-this-when) | Tasks and conditions that belong in this guide. | 63-71 |
| [Do Not Use This For](#do-not-use-this-for) | Boundaries and the correct routes for excluded work. | 72-86 |
| [Mental Model](#mental-model) | Concepts and state boundaries needed for correct operation. | 87-103 |
| [Preflight](#preflight) | State, authority, inputs, and readiness to verify before execution. | 104-124 |
| [Risk And Side Effects](#risk-and-side-effects) | Effects, approvals, cost, privacy, and retry boundaries. | 125-134 |
| [Canonical Operating Pattern](#canonical-operating-pattern) | The normal end-to-end execution sequence. | 135-160 |
| [Core SDK Surfaces](#core-sdk-surfaces) | Exact source-backed callable contracts owned by this guide. | 161-168 |
| [Data Contracts](#data-contracts) | Required identities, fields, evidence, handles, and durable shapes. | 169-187 |
| [Examples](#examples) | Representative usage patterns and code. | 188-210 |
| [Inline scripts: bind the play first](#inline-scripts-bind-the-play-first) | Credit attribution depends on the calling Python process having the play's root in its cwd OR `VERBIFLOW_PLAY_ID` set in its environment. | 211-250 |
| [Verification](#verification) | Evidence required before reporting success. | 251-260 |
| [Troubleshooting](#troubleshooting) | Failure diagnosis and corrective routes. | 261-269 |
| [Related References](#related-references) | Adjacent boundaries and when to cross them. | 270-280 |
<!-- END GENERATED: guide-map -->

## Purpose

Use this guide when creating the generated Workflow Play root that later
gets operated, reported, and optionally synced to the workspace Play library.
It owns the scaffold boundary: stable root path selection, generated files,
`CHECKLIST.md`, project creation, and workspace registration.

## A Play Is A Repo

The scaffold exists because **plays must be re-runnable from a clean checkout**.
A play is not a sequence of inline `vf.*` calls you happen to type into the
chat — it is a checked-in directory of Python files that the user or a fresh
agent can re-execute with `python run.py --mode batch` weeks later, without
re-reading the conversation that created it. Server-owned schedules are a
separate workspace primitive in `workflow_schedules.md`; V1 scheduled Play runs
either create hosted agent runs or execute the latest synced Play code through
deterministic hosted workflow execution, not a local cron pretending to own
server execution.

The scaffold writes the canonical files (`workflow.py`, `adapters.py`,
`run.py`, `views/`, `tests/`, `README.md`, `CHECKLIST.md`, etc.) and
initializes git. Treat the generated root like any other repo: edit those
files, checkpoint them durably, and keep `README.md` current so the next
operator can pick up cold. If you find yourself writing play logic into the
chat instead of into `workflow.py`, you are not building a play — you are
running a one-shot script that will not survive a fresh session.
Commit or push only when an authenticated Git remote is explicitly part of the
user's task; Play durability itself comes from the canonical Play sync path.

This guide does not own the whole workflow lifecycle. After the root exists,
route to `workflow_composition.md` for step design, `workflow_operations.md` for
running and repairing, `workflow_schedules.md` for Play sync and scheduling,
`workflow_sources.md` for source checkpoint
semantics, and `workflow_reports.md` for final Play UI/report output.

## Use This When

- The user asks to create, initialize, scaffold, or start a new Workflow Play.
- The agent needs to decide where the play root should live before any
  provider, enrichment, qualification, personalization, staging, or report work.
- You need the source-backed contract for `vf.play_scaffold.init(...)`.
- You need to explain which generated files are created and which references
  own the next step after creation.

## Do Not Use This For

- Running, resuming, repairing, replaying, exporting, or reporting from an
  existing generated root. Use `workflow_operations.md`.
- Syncing a Play revision/output or scheduling it. Use `workflow_schedules.md`.
- Designing/refactoring the GTM pipeline steps before or after scaffolding. Use
  `workflow_composition.md`.
- Source windows, checkpointing, object memory, or stale-row behavior. Use
  `workflow_sources.md`.
- Final Play UI/report schema, sidecars, charts, row drawers, or custom views.
  Use `workflow_reports.md`.
- Sequence staging, publish/send, sender setup, domain/mailbox purchase, or
  billing. Use the sequence/sender/billing references and keep confirmation
  gates explicit.

## Mental Model

Scaffolding creates a project shape in the active runtime filesystem. It is not the same as running the
workflow, starting provider jobs, building the final Play UI, syncing the Play
to the workspace, staging a sequence, publishing, sending, or buying anything.

| Object | Created by scaffold | Owner after creation |
| --- | --- | --- |
| Stable play root | Yes | This guide for creation; `workflow_operations.md` after creation |
| `CHECKLIST.md` | Yes | This guide defines the creation contract; the file is the per-play state |
| `workflow.py`, generated scripts, directories | Yes | `workflow_composition.md` for design; `workflow_operations.md` for operation |
| `local_viz/pipeline.html` | Yes | This guide for bootstrap; `workflow_operations.md` while operating |
| `state/workflow.sqlite` path | Directory created; state is populated by operation | `workflow_operations.md` |
| Workspace Play id / `.env` registration | ALWAYS happens (no offline mode); the scaffold creates the workspace Play and writes `VERBIFLOW_PLAY_ID` to `.env` so credit spend is tagged from the first SDK call. Defaults to `visibility='local_only'` (registered + billable but hidden from /plays UI); pass `visibility='visible'` to surface the play to teammates. | `workflow_operations.md` and `auth.md` |

`vf.play_scaffold.init(...)` is the SDK entrypoint. `verbiflow init` is the terminal adapter for humans or environments that cannot call the SDK directly.

## Preflight

1. Choose a stable absolute root path under the user's home or a user-named
   project directory. Do not use `/tmp`, `/var/tmp`, scratchpads, the SDK
   install directory, or the current shell directory by default.
2. If an existing root may already exist, inspect `vf.workflows.list()` /
   `vf.workflows.recent()` before creating a duplicate.
3. **Verify workspace identity before scaffolding.** The scaffold ALWAYS
   registers (no offline mode) — it writes a workspace Play and stamps
   `VERBIFLOW_PLAY_ID` into `.env` on creation, regardless. Run
   `vf.auth.status()` first to confirm the active workspace; pass
   `workspace_id=<uuid_or_slug>` to register the play in a specific workspace
   (the scaffold pins the play root there before the workspace write).
   Defaults to `visibility='local_only'` (hidden from /plays UI); pass
   `visibility='visible'` to surface it to teammates.
4. Print the target root path, play name, template, `visibility`, and
   target organization identity before creating files.
5. After creation, preserve `root`, `state_path`, `workflow_id`, `written`,
   `next_actions`, `registration.play_id`, and `registration.workspace_pin`
   (when provided).

## Risk And Side Effects

| Action | Risk class | Rule |
| --- | --- | --- |
| `vf.play_scaffold.init(...)` (always registers; default `visibility='local_only'`) | Runtime filesystem write plus platform write | Preserve `play_id` and `.env` changes. The play is registered + billable but hidden from /plays UI. |
| `vf.play_scaffold.init(..., visibility='visible')` | Same as default, surfaced in /plays | Use when the play is ready to share with teammates. Promote a hidden play later with `vf.workflows.register(<root>, visibility='visible')`. |
| `vf.play_scaffold.init(..., workspace_id=<uuid_or_slug>)` | Same as default plus local workspace pin | Use when the active profile is wrong or ambiguous; the scaffold pins the play root to that workspace before registration. |
| `vf.play_scaffold.init(..., force=True)` | Local overwrite risk plus forced re-registration | Inspect existing files first; `force=True` also re-registers even when `.env` already has a play id. |
| Editing generated checklist/plan after scaffold | Local file write | The play's `CHECKLIST.md` is the state; do not rely on conversation memory. |

## Canonical Operating Pattern

1. Resolve the play root path and name.
2. Run `vf.auth.status()` and confirm the active workspace is the intended owner
   of this play. If it is wrong, either switch with `vf.auth.focus_workspace(...)`
   or pass `workspace_id=<uuid_or_slug>` to `init` in the next step.
3. Call `vf.play_scaffold.init(path=<absolute_root>, name=...)`. Always
   registers (no offline mode). Defaults to `visibility='local_only'` —
   registered + billable + tracked in `vf.billing.cost_by_play(...)`, hidden
   from /plays UI; pass `visibility='visible'` to surface it. The call also
   `os.chdir`s into the new root (set `chdir=False` to keep your original
   cwd), so any further SDK calls in the same Python process find
   `<root>/.env` and tag credit to this play. A later inline
   `"$VF_PY" -c "..."` starts in a fresh process — see the "Inline scripts:
   bind the play first" section below.
4. Open the generated `CHECKLIST.md` and `local_viz/pipeline.html`.
5. Write the play-specific steps into `CHECKLIST.md` before the main pipeline
   starts. Use `workflow_composition.md` to design those steps when the pipeline
   is not already obvious.
6. If the play ended up in the wrong workspace, move it with
   `vf.workflows.transfer(<play_id_or_root>, <target_workspace_id>)` — this
   moves the workspace Play record and every credit ledger row tagged with its
   play_id in a single transaction (see `workflow_operations.md`).
Finally, hand off to `workflow_operations.md` to run, resume, repair, report,
or sync the generated root.

## Core SDK Surfaces

<!-- BEGIN GENERATED: core-sdk-surfaces -->
| Function | Use When | Inputs | Output And Handles To Preserve | Side Effect | Source |
| --- | --- | --- | --- | --- | --- |
| `vf.play_scaffold.init` | Create the generated Workflow Play project shape and ALWAYS register it with the backend (so credit attribution works from the first SDK call). The play defaults to visibility='local_only' (registered + billable, hidden from the workspace UI); pass visibility='visible' to surface it in /plays. | <strong>Required</strong>: none<br><strong>Optional</strong>: `chdir` (bool; optional; default `True`): Change the calling Python process' working directory into the new play root before returning. Defaults to true so subsequent SDK calls in the same process resolve VERBIFLOW_PLAY_ID from the play's .env and credit spend is tagged correctly. Pass chdir=False to keep the original cwd.; `force` (bool; optional; default `False`): Overwrite scaffold files and force registration even when .env already has a play id.; `git` (bool; optional; default `True`): Initialize git in the target root when it is not already a repository.; `name` (str \| None; optional; default `None`): Play/workflow name shown in generated files and catalog registration.; `path` (str \| Path; optional; default `'.'`): Target directory for the generated Workflow Play root.; `template` (str; optional; default `'basic'`): Workflow scaffold template name.; `visibility` (visible \| local_only; optional; default `'local_only'`): Workspace UI visibility for the registered play. The play ALWAYS registers with the backend (so VERBIFLOW_PLAY_ID is written and credit spend is tagged to the play). Defaults to `local_only` — registered + billable but hidden from the workspace /plays UI, so scratch / in-progress plays do not clutter the team listing. Pass `visibility='visible'` to surface the play in /plays (typically once it is ready to share); switch a hidden play to visible later by re-calling `vf.workflows.register(root, visibility='visible')`.; `workspace_id` (str \| None; optional; default `None`): Workspace UUID or slug to register the play under. When provided, the play root is pinned to this workspace (writes a `workspace` file and switches the active profile) before registration, so registration lands in the right org even when the current session is logged in elsewhere. | `workflow_play_scaffold_result` payload returned by the SDK operation. Inspect the documented function reference and `raw`/dictionary fields when debugging provider-specific data.<br><strong>Common next calls</strong>: `vf.workflows.operate`, `vf.workflows.register`, `vf.workflows.sync` | platform_write | installed SDK: `verbiflow/play_scaffold.py:28-262` |
<!-- END GENERATED: core-sdk-surfaces -->

## Data Contracts

Preserve these fields from the scaffold result:

- `root` / `path`: the stable local play root.
- `workflow_id` and `workflow_name`: local identity used by workflow catalog
  operations.
- `state_path`: expected SQLite state path for later operation.
- `written`: generated files/directories, useful for verifying scaffold output.
- `next_actions`: source-backed hints for the next operation.
- `registration` / `play_id`: scaffold registers by default and writes the
  backend Play identity for attribution.

Generated roots normally include `CHECKLIST.md`, `workflow.py`, `config.json`,
`run.py`, `operate.py`, `approve.py`, `repair.py`, `replay.py`, `status.py`,
`export.py`, `monitor.py`, `build_report.py`, `serve_play.py`, `adapters.py`,
`README.md`, `inputs/`, `outputs/`, `runs/`, `reports/`, `views/`, `state/`,
`local_viz/`, and `tests/`.

## Examples

```python
import verbiflow as vf

result = vf.play_scaffold.init(
    path="/Users/me/verbiflow/plays/acme-prospecting",
    name="Acme Prospecting",
)
print(result["root"])
print(result["state_path"])
print(result["registration"]["play_id"])  # registered by default
print(result["next_actions"])

# Pin the play to a specific workspace before registration (useful when the
# current profile is logged in elsewhere):
result = vf.play_scaffold.init(
    path="/Users/me/verbiflow/plays/acme-prospecting",
    name="Acme Prospecting",
    workspace_id="<uuid_or_slug>",
)
```

## Inline scripts: bind the play first

Credit attribution depends on the calling Python process having the play's
root in its cwd OR `VERBIFLOW_PLAY_ID` set in its environment.
`vf.play_scaffold.init(...)` handles this for the scaffolding process by
`chdir`-ing into the new root and writing `<root>/.env`. But every subsequent
`"$VF_PY" -c "..."` is a fresh process whose cwd is back to the agent shell's
default — usually outside the play root. Without intervention, the SDK's
resolver walks up, finds no play root, sends no `x-verbiflow-play-id` header,
and the credit ledger row is written with `play_id = NULL`.

Two patterns; the first is the default.

1. **Save the script to `<play_root>/scripts/<name>.py` and run it as a file.**
   ```bash
   "$VF_PY" "$PLAY_ROOT/scripts/find_contacts.py"
   ```
   Cwd is the play root. Attribution Just Works. The script lives next to the
   play in git, so a later session (or human) can replay it. Use this for all
   non-trivial work.

2. **When inline is unavoidable, lead with `vf.workflows.open(...)`.** Pass the
   backend play_id or the play root path. `open()` is the single focus
   primitive — it resolves the play, returns the local handle, AND (when
   `bind=True`, the default) `os.chdir`s into the root and sets
   `os.environ["VERBIFLOW_PLAY_ID"]` so:
   - The SDK's cwd-walking resolver finds `<root>/.env`.
   - The env var is the highest-precedence override, so even if cwd later
     changes, headers still tag correctly.
   - Any subprocess this script spawns inherits the env var.

   ```python
   import verbiflow as vf
   vf.workflows.open("wfp_funded_saas_a3b1c9")   # or pass the root path
   pending = vf.sources.people.find_at_companies_start(...)
   ```

   To verify, call `vf.billing.cost_by_play(<play_id>)` after a small probe
   call and confirm the credits show up.

## Verification

- The root path exists and is not a temp/scratch path.
- `CHECKLIST.md` and `local_viz/pipeline.html` exist.
- The scaffold result lists the written files.
- `vf.auth.status()` was checked first and the returned workspace/play ids were
  printed, because scaffold registration is a workspace-scoped write.
- The next action is routed to the correct workflow child reference instead of
  continuing from memory.

## Troubleshooting

| Symptom | Check | Fix |
| --- | --- | --- |
| Scaffold would write into a temp/scratch path | Resolve `path` to an absolute path | Stop and choose a stable user-owned root. |
| Existing files would be overwritten | Inspect `written`/existing tree and `force` | Avoid `force=True` unless the user explicitly wants overwrite. |
| Agent wants to run provider work immediately | Is `CHECKLIST.md` populated with play-specific steps? | Write the plan first; then use `workflow_operations.md`. |
| Registration happened in the wrong workspace | Was `vf.auth.status()` checked before scaffolding? Was `workspace_id=...` passed? | Move it with `vf.workflows.transfer(<play_id_or_root>, <target_workspace_id>)` — this re-points the play and every credit ledger row tagged with its play_id in a single transaction. See `workflow_operations.md`. |

## Related References

| Need | Read | Why |
| --- | --- | --- |
| Classifying a broad workflow/play task | `workflows.md` | Parent router for workflow child references. |
| Designing the play's steps | `workflow_composition.md` | Owns GTM pipeline design across sources, enrichment, qualification, reports, and activation. |
| Running, repairing, or reporting from the root | `workflow_operations.md` | Owns immediate operation after the root exists. |
| Syncing the Play or managing schedules | `workflow_schedules.md` | Owns canonical revision/output sync and later/recurring execution. |
| Source windows and checkpoint semantics | `workflow_sources.md` | Owns freshness, object memory, and replay/reset assumptions for source steps. |
| Final Play UI/report output | `workflow_reports.md` | Owns final report bundle and review UI. |
| Workspace identity before registration/sync | `auth.md` | Registration and sync are workspace-scoped platform writes. |
