Tutorial Workflow Runs
choco intermediate 4 min read
ELI5
Tutorial runs are pretend workflow runs — they look real, they run real handlers, but they are scoped to one user as a learning toy. They never graduate into the user’s actual workflows; once finished, they just stop.
Technical Deep Dive
ADR-002 (architecture/decisions/ADR-002-tutorial-workflow-runs.md) is the first cross-resource application of ADR-001’s platform-hosted pattern. Reuses the dual-discriminator (hosting_mode + lifecycle_stage) on workflow_runs.
Schema Shape
classDiagram class WorkflowRun { run_id user_id tutorial_slug hosting_mode "user_git OR platform_template" lifecycle_stage "playground OR live" status "pending, running, complete, failed" }Differences from Sites
ADR-002 explicitly enumerates lessons folded back into the pattern doc (ADR-002:33-41):
- No
adoptedlifecycle stage. Tutorial runs don’t graduate. The 3-stage lifecycle in ADR-001 was site-specific. user_idon the resource table. Sites have onlyworkspace_id; workflow runs neededuser_idbecause two members of a workspace can independently run tutorials.- No adoption saga. TASKSET 7’s adopt machinery does not apply; the pattern’s adoption step is optional.
One-Active-Per-Slug Enforcement
A partial unique index, not an app-layer guard:
CREATE UNIQUE INDEX ON workflow_runs (user_id, tutorial_slug)WHERE hosting_mode = 'platform_template' AND status IN ('pending', 'running');Source: ADR-002:24. The DB rejects a second active tutorial run for the same (user_id, tutorial_slug). App-layer enforcement was rejected because it can race; the partial index gives a hard guarantee.
Tutorial Catalog
Three v1 tutorials, each reusing an existing choco-consumers handler (ADR-002:27-30):
| Tutorial slug | Reused handler |
|---|---|
check-links | broken_links |
audit-typos | typo_check |
style-guide | style_guide |
Catalog location: services/choco-gateway/internal/tutorial/catalog.go — kept in code rather than R2 because tutorial scenarios change with handler code (ADR-002:31-32).
3-Concurrent Cap
ADR-002 alternative B was rejected (ADR-002:48-50): “clicking the chips pops three spinners” creates an unexpected load profile. The 3-concurrent cap is both a fairness guarantee and an invariant the Prometheus alert keys on.
Key Terms
- platform_template hosting_mode → tutorial runs are platform-owned; the user’s workspace is read-only against them.
- partial unique index → Postgres index applied only where the WHERE clause holds; gives conditional uniqueness without a separate table.
- catalog in code → catalog.go is checked in, not loaded from R2; tutorial code and handler code change together.
Q&A
Q: Can a tutorial run survive lifecycle_stage = adopted?
A: No — the lifecycle enum for workflow runs is {playground, live} only (ADR-002:21). Tutorials don’t graduate.
Q: What stops a user starting four tutorials at once?
A: The partial unique index on (user_id, tutorial_slug) WHERE platform_template AND status IN ('pending','running') blocks duplicates of the same slug; the 3-concurrent cap is the additional separate enforcement against starting all three different slugs simultaneously (ADR-002:24, 48-50).
Q: Why are tutorials not in a separate table?
A: ADR-002 alternative A (ADR-002:45-46): a separate tutorial_runs table doubles the activity-feed query paths and re-introduces the bimodal data model the pattern exists to eliminate.
Examples
Adding a “broken-images” tutorial: append the entry to services/choco-gateway/internal/tutorial/catalog.go (slug, reused handler ref, demo content seed), wire the slug through the gallery UI in cho-co-web. The partial unique index already covers it; no migration needed unless adding a new column to workflow_runs.
neighbors on the map
- Site Hosting Modes & Lifecycle Stages adding a new fork branching on platform vs user_git
- Prompt-DAG Scheduler designing a graph.json for a new repo
- End-to-End Chain Execution Request Flow tracing a chain execution through the entire system