diff --git a/.gitignore b/.gitignore index bd2460d..bd713ff 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ data.db # file as well, but since this is for a workshop # we're going to keep them around. # .env + +# ignored stuff +*.ignored.* +*.ignored/ diff --git a/README.md b/README.md index af8824a..154b14c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,19 @@ [![Code of Conduct][coc-badge]][coc] +## Creating Workshops with AI Agents + +This template includes comprehensive documentation for creating workshops using +AI agents. See the [`instructor/`](./instructor/) directory for: + +- **`workflow/`** - Step-by-step agent workflow (start here) +- Reference docs on planning, exercises, MDX, testing, and best practices + +To create a new workshop, tell an AI agent: + +> "I want to create an Epic Workshop about [YOUR TOPIC]. Please read +> `/instructor/workflow/01-discovery.md` and help me build it." + ## Prerequisites - TODO: add prerequisites diff --git a/instructor/01-workshop-overview.md b/instructor/01-workshop-overview.md new file mode 100644 index 0000000..3bf0660 --- /dev/null +++ b/instructor/01-workshop-overview.md @@ -0,0 +1,147 @@ +# Epic Workshop Overview + +## What is an Epic Workshop? + +An Epic Workshop is a hands-on, interactive learning experience where students learn by writing code. The Epic Workshop App provides the infrastructure to run these workshops locally, presenting exercises with problems to solve and solutions to compare against. + +## Core Terminology + +Understanding these terms is essential: + +### Workshop +The entire learning experience. A workshop has a title, subtitle, and contains multiple exercises. Example: "React Fundamentals" or "Mocking Techniques in Vitest" + +### Exercise +A major learning topic within a workshop. Each exercise has: +- An introduction (`README.mdx`) explaining the concept +- Multiple steps (problem/solution pairs) +- A summary (`FINISHED.mdx`) + +Example exercises: "Hello World in JS", "Functions", "Form Validation" + +### Step +A focused, atomic task within an exercise. Each step has: +- A **Problem**: The starting state where students implement something +- A **Solution**: The expected final state students should arrive at + +### Problem +The initial code state. Contains: +- Starter code with TODO comments and emoji markers +- A `README.mdx` with instructions +- All necessary configuration files + +### Solution +The completed code state. Contains: +- Fully implemented code +- A `README.mdx` explaining the solution +- Often includes tests to verify the solution + +### Playground +A sandbox area where learners can experiment. When they click "Set to Playground" in the UI, the app copies the contents of any exercise step to the playground. + +### Example +Standalone, runnable code samples in the `examples/` directory that demonstrate concepts without being exercises. + +## Workshop Structure Hierarchy + +``` +Workshop +├── exercises/ +│ ├── README.mdx (Workshop Intro) +│ ├── 01.exercise-name/ +│ │ ├── README.mdx (Exercise Intro) +│ │ ├── 01.problem.step-name/ +│ │ │ ├── README.mdx (Problem Instructions) +│ │ │ └── [code files] +│ │ ├── 01.solution.step-name/ +│ │ │ ├── README.mdx (Solution Explanation) +│ │ │ └── [code files] +│ │ ├── 02.problem.next-step/ +│ │ ├── 02.solution.next-step/ +│ │ └── FINISHED.mdx (Exercise Summary) +│ ├── 02.next-exercise/ +│ └── FINISHED.mdx (Workshop Wrap-up) +├── examples/ +├── playground/ +└── package.json +``` + +## App Types + +Epic Workshops support two types of apps: + +### Simple Apps +No `package.json` with a `dev` script. The workshop app serves files directly. + +**Use for:** +- HTML/CSS/JS exercises +- Single-file React exercises +- Quick demonstrations + +**Files:** +- `index.tsx` or `index.html` (required) +- `index.css` (optional, auto-included) +- `api.server.ts` (optional, for server-side logic) + +### Project Apps +Has a `package.json` with a `dev` script. Runs as a separate server. + +**Use for:** +- Full applications (Remix, Vite, etc.) +- Exercises requiring build tools +- Complex multi-file projects + +**Requirements:** +- `package.json` with `"scripts": { "dev": "..." }` +- Dev server must use the `PORT` environment variable + +## Learning Flow + +The typical learner experience: + +1. **Read the Exercise Intro** - Understand the concept being taught +2. **Read the Problem Instructions** - Understand what to implement +3. **Implement the Solution** - Write code in the problem directory +4. **Compare with Solution** - Use the diff tab to see differences +5. **Run Tests** (if available) - Verify the implementation +6. **Read the Summary** - Reflect on what was learned +7. **Move to Next Step** - Continue the learning journey + +## Key Design Principles + +### 1. Incremental Complexity +Start with the simplest possible example and build complexity gradually. Each step should introduce ONE new concept. + +### 2. Problem-Solution Pairs +Every problem must have a corresponding solution. The solution should be the minimal change needed from the problem to complete the task. + +### 3. Clear Instructions +Use emoji characters to guide learners: +- 👨‍💼 Peter the Product Manager - Context and requirements +- 🐨 Kody the Koala - Specific instructions +- 🧝‍♀️ Kellie the Co-worker - Pre-done work context +- 🦺 Lily the Life Jacket - TypeScript guidance +- 💰 Marty the Money Bag - Tips and hints +- 📜 Dominic the Document - Documentation links +- 📝 Nancy the Notepad - Note-taking prompts +- 🦉 Olivia the Owl - Best practices +- 💣 Barry the Bomb - Code to delete +- 🚨 Alfred the Alert - Test failure help + +### 4. Real-World Relevance +Exercises should mirror actual development tasks. Use realistic examples, not contrived scenarios. + +### 5. Self-Contained Steps +Each step should be completable independently. Don't require knowledge from later steps to complete earlier ones. + +## What Makes a Great Workshop + +Based on analysis of successful Epic Workshops: + +1. **Clear Learning Objectives** - Each exercise has a specific goal +2. **Appropriate Scope** - 6-10 exercises, 2-6 steps per exercise +3. **Consistent Difficulty Progression** - Gets harder gradually +4. **Rich Context** - Explains "why" not just "what" +5. **Quality Code** - Production-grade patterns and practices +6. **Helpful Diffs** - Problem and solution differ meaningfully but not overwhelmingly +7. **Engaging Content** - Uses emoji characters, videos, and callouts effectively diff --git a/instructor/02-workshop-planning.md b/instructor/02-workshop-planning.md new file mode 100644 index 0000000..c33d391 --- /dev/null +++ b/instructor/02-workshop-planning.md @@ -0,0 +1,235 @@ +# Workshop Planning Guide + +## Phase 1: Topic Research + +Before creating any exercises, thoroughly understand the topic: + +### Research Checklist +- [ ] Identify core concepts that must be covered +- [ ] Find official documentation and authoritative sources +- [ ] Review existing tutorials and courses on the topic +- [ ] Understand common pain points and misconceptions +- [ ] Identify prerequisites learners need +- [ ] Determine the target audience skill level + +### Questions to Answer +1. What does a beginner need to know? +2. What does an expert need that beginners don't? +3. What are the most common mistakes people make? +4. What's the "aha moment" for this topic? +5. How does this topic connect to real-world applications? + +## Phase 2: Scoping + +Define clear boundaries for your workshop: + +### Workshop Length Guidelines + +| Workshop Type | Exercises | Steps per Exercise | Total Duration | +|--------------|-----------|-------------------|----------------| +| Short | 4-6 | 2-4 | 2-4 hours | +| Standard | 6-10 | 3-5 | 4-8 hours | +| Comprehensive | 8-12 | 4-6 | 8-16 hours | + +### Scoping Questions +1. What's the ONE big thing learners will be able to do after? +2. What topics are explicitly OUT of scope? +3. What's the minimum viable workshop? +4. What advanced topics could be bonus exercises? + +### Red Flags - Workshop is Too Broad +- More than 12 exercises planned +- Exercises covering unrelated concepts +- Need for extensive prerequisite explanation +- Multiple target audiences with different needs + +### Red Flags - Workshop is Too Narrow +- Fewer than 4 exercises +- Exercises that are all variations of the same thing +- No natural progression of difficulty +- Could be covered in a blog post + +## Phase 3: Exercise Design + +### Creating an Exercise Outline + +For each exercise, define: + +```markdown +## Exercise: [Name] + +**Learning Objective:** [One sentence describing what learners will understand] + +**Prerequisites:** [Concepts learners should already know] + +**Steps:** +1. [First concept/task] - Introduces [specific concept] +2. [Second concept/task] - Builds on step 1 by [specific addition] +3. [Third concept/task] - Adds [specific feature/concept] + +**Key Takeaway:** [Main insight learners should have] +``` + +### Example: Exercise Outline for "Mocking Functions" + +```markdown +## Exercise: Functions + +**Learning Objective:** Learners will understand how to mock, spy on, and +replace function implementations in tests. + +**Prerequisites:** Basic Vitest knowledge, JavaScript functions + +**Steps:** +1. mock-functions - Create mock functions with vi.fn() +2. spies - Spy on existing functions without replacing them +3. mock-implementation - Replace function implementations dynamically + +**Key Takeaway:** Mock functions record calls while spies observe them. +``` + +## Phase 4: Flow Design + +### Linear vs Non-Linear Flow + +**Linear Flow (Recommended)** +Each exercise builds directly on the previous one. Best for: +- Teaching a framework or library +- Building a complete application +- Topics with strong dependencies + +**Non-Linear Flow** +Exercises are relatively independent. Best for: +- Technique collections (like Mocking Techniques) +- Reference-style workshops +- Topics where order matters less + +### Ordering Principles + +1. **Fundamentals First** - Start with the basics even if they seem obvious +2. **Build on Prior Knowledge** - Reference earlier exercises +3. **Difficulty Progression** - Easy → Medium → Hard +4. **Conceptual Grouping** - Related topics should be adjacent +5. **Energy Management** - Place challenging exercises in the middle, not at the end + +## Phase 5: Creating the Exercise Plan + +### Template for Complete Workshop Plan + +```markdown +# Workshop: [Title] + +## Target Audience +[Who is this for? What do they already know?] + +## Learning Outcomes +By the end of this workshop, learners will be able to: +1. [Outcome 1] +2. [Outcome 2] +3. [Outcome 3] + +## Prerequisites +- [Prerequisite 1] +- [Prerequisite 2] + +## Exercise Plan + +### 01. [Exercise Name] +**Concept:** [What this teaches] +**Steps:** +- 01.problem.[step-name]: [Description] +- 02.problem.[step-name]: [Description] + +### 02. [Exercise Name] +**Concept:** [What this teaches] +**Steps:** +- 01.problem.[step-name]: [Description] +- 02.problem.[step-name]: [Description] +- 03.problem.[step-name]: [Description] + +[Continue for all exercises...] + +## Out of Scope +- [Topic not covered] +- [Topic not covered] + +## Potential Bonus Content +- [Extra exercise idea] +- [Extra exercise idea] +``` + +## Phase 6: Iteration + +### Review Your Plan + +Before implementation, verify: + +1. **Coverage** - Does every learning outcome have exercises? +2. **Progression** - Does difficulty increase smoothly? +3. **Balance** - Are exercises roughly similar in length? +4. **Coherence** - Does the workshop tell a story? +5. **Practicality** - Can each exercise be completed in 15-30 minutes? + +### Get Feedback + +Consider: +- Reviewing the plan with domain experts +- Comparing to existing successful workshops +- Validating with potential learners +- Testing the flow with a prototype exercise + +## Example: Complete Workshop Plan + +```markdown +# Workshop: React Server Components + +## Target Audience +Experienced React developers who want to understand RSC + +## Learning Outcomes +1. Understand the mental model of server vs client components +2. Know when and how to use 'use client' and 'use server' +3. Build applications that efficiently blend server and client rendering + +## Prerequisites +- React fundamentals (components, hooks, state) +- Basic understanding of client/server architecture +- Node.js familiarity + +## Exercise Plan + +### 01. Mental Model +**Concept:** Understanding what runs where +**Steps:** +- 01.problem.server-components: Create a simple server component +- 02.problem.client-components: Mark a component as client-side +- 03.problem.composition: Compose server and client components + +### 02. Data Fetching +**Concept:** Fetching data in server components +**Steps:** +- 01.problem.async-components: Fetch data in async server components +- 02.problem.streaming: Stream data to the client +- 03.problem.loading-states: Handle loading states + +### 03. Server Actions +**Concept:** Calling server code from client components +**Steps:** +- 01.problem.use-server: Create a server action +- 02.problem.forms: Use server actions with forms +- 03.problem.optimistic: Add optimistic updates + +[etc...] +``` + +## Planning Anti-Patterns + +### Avoid These Mistakes + +1. **Kitchen Sink** - Trying to cover everything +2. **No Flow** - Random exercise order +3. **Cliff Drops** - Sudden difficulty spikes +4. **Too Abstract** - No practical applications +5. **Too Long** - Exercises that take > 30 minutes +6. **Missing Context** - No explanation of "why" +7. **Unclear Goals** - Exercises without clear outcomes diff --git a/instructor/03-directory-structure.md b/instructor/03-directory-structure.md new file mode 100644 index 0000000..fb0bca8 --- /dev/null +++ b/instructor/03-directory-structure.md @@ -0,0 +1,315 @@ +# Directory Structure Guide + +## Overview + +Epic Workshops have a precise directory structure that the workshop app expects. Following this structure exactly is critical for the workshop to function. + +## Root Directory Structure + +``` +workshop-name/ +├── .github/ +│ └── workflows/ +│ └── validate.yml # CI validation workflow +├── exercises/ # All exercises live here +│ ├── README.mdx # Workshop introduction +│ ├── 01.exercise-name/ # First exercise +│ ├── 02.exercise-name/ # Second exercise +│ └── FINISHED.mdx # Workshop wrap-up +├── examples/ # Optional standalone examples +│ └── example-name/ +├── epicshop/ # Workshop app configuration +│ ├── package.json +│ ├── setup.js +│ └── setup-custom.js +├── playground/ # Auto-generated playground +├── public/ # Static assets +│ └── images/ +│ └── instructor.png # Instructor avatar +├── package.json # Root package.json with epicshop config +├── tsconfig.json # TypeScript configuration +├── README.md # GitHub readme +└── LICENSE.md # License file +``` + +## Naming Conventions + +### File Names +- Use **lower-kebab-case** for all file names +- Example: `my-component.tsx`, `user-service.ts` + +### Exercise Directory Names +Format: `XX.exercise-name` + +- `XX` is a zero-padded number (01, 02, 03...) +- `exercise-name` is lowercase with hyphens +- Examples: + - `01.hello-world` + - `02.raw-react` + - `03.using-jsx` + +### Step Directory Names +Format: `XX.type.step-name` + +- `XX` is a zero-padded number matching within the exercise +- `type` is either `problem` or `solution` +- `step-name` is lowercase with hyphens +- Examples: + - `01.problem.hello` + - `01.solution.hello` + - `02.problem.root` + - `02.solution.root` + +### Important Rules +- Problem and solution pairs MUST have matching numbers and names +- `01.problem.hello` must have `01.solution.hello` +- Numbers must be sequential (01, 02, 03... not 01, 03, 05) + +## Exercise Directory Structure + +### Simple App Exercise + +For exercises without a separate dev server: + +``` +01.exercise-name/ +├── README.mdx # Exercise introduction +├── 01.problem.step-name/ +│ ├── README.mdx # Problem instructions +│ ├── index.tsx # Main code file (or index.html) +│ ├── index.css # Optional styles +│ ├── api.server.ts # Optional server-side code +│ └── tsconfig.json # TypeScript config +├── 01.solution.step-name/ +│ ├── README.mdx # Solution explanation +│ ├── index.tsx # Completed code +│ ├── index.css +│ ├── index.test.ts # Optional tests +│ └── tsconfig.json +├── 02.problem.next-step/ +│ └── ... +├── 02.solution.next-step/ +│ └── ... +└── FINISHED.mdx # Exercise summary +``` + +### Project App Exercise + +For exercises with their own dev server: + +``` +01.exercise-name/ +├── README.mdx +├── 01.problem.step-name/ +│ ├── README.mdx +│ ├── package.json # Has "dev" script +│ ├── tsconfig.json +│ ├── vite.config.ts # Or similar build config +│ ├── index.html +│ └── src/ +│ ├── index.tsx +│ └── components/ +│ └── my-component.tsx +├── 01.solution.step-name/ +│ ├── README.mdx +│ ├── package.json +│ ├── tsconfig.json +│ ├── vite.config.ts +│ ├── index.html +│ ├── src/ +│ │ └── ... +│ └── tests/ +│ └── my.test.ts +└── FINISHED.mdx +``` + +### Full Application Exercise (Remix, etc.) + +For complex full-stack exercises: + +``` +01.exercise-name/ +├── README.mdx +├── 01.problem.step-name/ +│ ├── README.mdx +│ ├── package.json +│ ├── tsconfig.json +│ ├── remix.config.js +│ ├── app/ +│ │ ├── entry.client.tsx +│ │ ├── entry.server.tsx +│ │ ├── root.tsx +│ │ ├── routes/ +│ │ │ └── index.tsx +│ │ ├── components/ +│ │ │ └── ui/ +│ │ │ └── button.tsx +│ │ └── utils/ +│ │ └── db.server.ts +│ ├── public/ +│ └── tests/ +│ └── e2e/ +│ └── smoke.test.ts +├── 01.solution.step-name/ +│ └── [same structure with completed code] +└── FINISHED.mdx +``` + +## Examples Directory + +Optional directory for standalone demonstrations: + +``` +examples/ +├── example-name/ +│ ├── README.mdx # Example explanation +│ ├── index.tsx # Simple app example +│ └── tsconfig.json +└── another-example/ + ├── README.mdx + ├── package.json # Project app example + └── src/ + └── index.ts +``` + +## Public Directory + +Static assets accessible at `/`: + +``` +public/ +├── images/ +│ └── instructor.png # Required: instructor avatar (112x112px min) +├── favicon.ico # Optional: custom favicon +├── favicon.svg # Optional: SVG favicon +├── logo.svg # Optional: custom logo with theme support +└── og/ + ├── background.png # Optional: OG image background + └── logo.svg # Optional: OG image logo +``` + +## Epicshop Directory + +Workshop app configuration: + +``` +epicshop/ +├── package.json # Workshop app dependencies +├── package-lock.json +├── setup.js # Standard setup script +├── setup-custom.js # Custom setup logic +├── tsconfig.json # TypeScript config +└── [optional custom directories] +``` + +## Diff Ignore Files + +Control what appears in diff comparisons: + +### Root Level +``` +epicshop/ +└── .diffignore # Global diff ignores +``` + +### Exercise Level +``` +exercises/ +└── 01.exercise-name/ + └── epicshop/ + └── .diffignore # Exercise-specific ignores +``` + +### .diffignore Syntax +``` +# Ignore all README files +README.mdx + +# Ignore specific directory +node_modules/ + +# Include something that would be ignored +!package.json +``` + +## Workspace Configuration + +For workshops using npm workspaces: + +```json +// package.json +{ + "workspaces": [ + "exercises/*/*", + "examples/*" + ] +} +``` + +This allows: +- Shared dependencies at root level +- Exercise-specific dependencies in exercise package.json +- Running commands across all exercises + +## Common Patterns + +### Pattern: Shared Code Across Steps + +When multiple steps share code, each step still has complete copies: + +``` +01.exercise-name/ +├── 01.problem.step-1/ +│ ├── shared-util.ts # Copy 1 +│ └── index.tsx +├── 01.solution.step-1/ +│ ├── shared-util.ts # Copy 2 (identical) +│ └── index.tsx +├── 02.problem.step-2/ +│ ├── shared-util.ts # Copy 3 (identical) +│ └── index.tsx +└── 02.solution.step-2/ + ├── shared-util.ts # Copy 4 (identical) + └── index.tsx +``` + +### Pattern: Progressive Enhancement + +Building on code from previous steps: + +``` +01.problem.basic/ +└── component.tsx # Basic implementation + +02.problem.enhanced/ +└── component.tsx # Starts from 01.solution + TODOs + +03.problem.complete/ +└── component.tsx # Starts from 02.solution + TODOs +``` + +### Pattern: Test Files + +Tests typically only in solution directories: + +``` +01.problem.feature/ +└── index.tsx # No tests + +01.solution.feature/ +├── index.tsx +└── index.test.ts # Tests verify solution +``` + +## Validation Checklist + +Before running the workshop, verify: + +- [ ] All exercise directories have `README.mdx` +- [ ] All problem/solution pairs have matching names +- [ ] Numbers are sequential (no gaps) +- [ ] Each step has `README.mdx` +- [ ] `FINISHED.mdx` exists for each exercise and at exercises root +- [ ] `package.json` has valid `epicshop` configuration +- [ ] Instructor image exists at `public/images/instructor.png` +- [ ] All code files follow naming conventions diff --git a/instructor/04-writing-exercises.md b/instructor/04-writing-exercises.md new file mode 100644 index 0000000..fe29103 --- /dev/null +++ b/instructor/04-writing-exercises.md @@ -0,0 +1,375 @@ +# Writing Effective Exercises + +## The Problem-Solution Paradigm + +Every step in an Epic Workshop consists of a problem and a solution. The problem presents incomplete code with clear instructions, and the solution shows the completed implementation. + +## Creating Problem Files + +### Structure of Problem Code + +Problem code should include: + +1. **Working starter code** - Enough context to understand the task +2. **TODO markers** - Emoji-marked comments guiding the learner +3. **Clear placeholders** - Where code needs to be added +4. **Helpful hints** - Tips and documentation links + +### Example: Problem Code + +```tsx +// 01.problem.props/index.tsx + +import { createRoot } from 'react-dom/client' + +const operations = { + '+': (left: number, right: number): number => left + right, + '-': (left: number, right: number): number => left - right, +} + +// 🦺 Create a type called CalculatorProps with: +// - left: number +// - operator: string +// - right: number + +// 🦺 Add the type annotation to this props argument +// @ts-expect-error 💣 Remove this when you add the type +function Calculator({ left, operator, right }) { + // 🐨 Use the operator to calculate the result + // 💰 const result = operations[operator](left, right) + + return ( +
+ {/* 🐨 Display the result here */} +
+ ) +} + +function App() { + return +} + +createRoot(document.getElementById('root')!).render() +``` + +### TODO Comment Format + +Use this pattern for instructions: + +```javascript +// 🐨 [What to do] +// 💰 [Hint or code snippet] +// 📜 [Documentation link] +``` + +### Making Instructions Clear + +**Good Instructions:** +```javascript +// 🐨 Create a useState hook to track the count +// 💰 const [count, setCount] = useState(0) +// 📜 https://react.dev/reference/react/useState +``` + +**Bad Instructions:** +```javascript +// 🐨 Add state +``` + +## Creating Solution Files + +### Structure of Solution Code + +Solution code should: + +1. **Be complete and working** - No TODOs or placeholders +2. **Be minimal** - Only add what was asked +3. **Be clean** - Remove any development artifacts +4. **Include tests** (when appropriate) + +### Example: Solution Code + +```tsx +// 01.solution.props/index.tsx + +import { createRoot } from 'react-dom/client' + +const operations = { + '+': (left: number, right: number): number => left + right, + '-': (left: number, right: number): number => left - right, +} + +type CalculatorProps = { + left: number + operator: string + right: number +} + +function Calculator({ left, operator, right }: CalculatorProps) { + const result = operations[operator](left, right) + + return ( +
+ {result} +
+ ) +} + +function App() { + return +} + +createRoot(document.getElementById('root')!).render() +``` + +## The Diff Should Tell a Story + +The difference between problem and solution should be: + +1. **Focused** - Only changes related to the learning objective +2. **Readable** - Easy to understand at a glance +3. **Minimal** - Smallest possible change to complete the task +4. **Educational** - Demonstrates the concept clearly + +### Good Diff Example + +```diff +- // 🐨 Create a type called CalculatorProps ++ type CalculatorProps = { ++ left: number ++ operator: string ++ right: number ++ } + +- // @ts-expect-error 💣 Remove this when you add the type +- function Calculator({ left, operator, right }) { ++ function Calculator({ left, operator, right }: CalculatorProps) { +``` + +### Bad Diff Example (Too Many Changes) + +```diff +- // lots of commented code +- // more commented code ++ import { useState, useEffect, useCallback } from 'react' ++ import { validateInput } from './utils' ++ import { logger } from './logger' +// 50 more lines of unrelated changes +``` + +## Writing README.mdx Files + +### Exercise Introduction (exercises/XX.exercise-name/README.mdx) + +Purpose: Explain the concept being taught + +Structure: +```mdx +# Exercise Title + + + +[2-4 paragraphs explaining the concept] + +[Code examples if helpful] + +[Callouts for important points] + +[What the learner will do in this exercise] +``` + +Example: +```mdx +# Form Validation + + + +HTML has built-in support for form validation. You can use attributes like +`required`, `minlength`, and `pattern` to validate inputs before submission. + +```html + +``` + + +Client-side validation is for UX only. Always validate on the server! + + +In this exercise, you'll add validation to a note-editing form. +``` + +### Problem README (exercises/XX.exercise-name/XX.problem.step-name/README.mdx) + +Purpose: Tell the learner exactly what to do + +Structure: +```mdx +# Step Title + + + +👨‍💼 [Context from the product manager] + +🐨 [Specific instructions] + +[Additional guidance, links, tips] +``` + +Example: +```mdx +# Required Field Validation + + + +👨‍💼 Our users are submitting empty forms! We need to add validation +so that both the title and content fields are required. + +🐨 Open and add the +`required` attribute to both the title input and the content textarea. + +Requirements: +- `title` is required, maximum length of 100 +- `content` is required, maximum length of 10000 + +📜 [Form Validation on MDN](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) +``` + +### Solution README (exercises/XX.exercise-name/XX.solution.step-name/README.mdx) + +Purpose: Briefly confirm what was done and transition forward + +Structure: +```mdx +# Step Title + + + +👨‍💼 [Brief confirmation or insight] + +[Optional: Transition to next concept] +``` + +Example: +```mdx +# Required Field Validation + + + +👨‍💼 Great job! The form now validates that both fields have content +before submission. + +But we can do even better. Let's look at server-side validation next. +``` + +### Exercise Summary (exercises/XX.exercise-name/FINISHED.mdx) + +Purpose: Help learners reflect on what they learned + +Structure: +```mdx +# Exercise Title + + + +👨‍💼 [Congratulations and summary] + +[Key takeaways as bullet points or prose] + +[Optional: What's next or how this connects to future learning] +``` + +### Workshop Summary (exercises/FINISHED.mdx) + +Purpose: Celebrate completion and provide next steps + +Structure: +```mdx +# Workshop Title + + + +[Celebration] + +[Summary of journey] + +[Call to action or next steps] +``` + +## Exercise Patterns + +### Pattern: Concept Introduction + +First step introduces a new concept simply: + +``` +01.problem.basic/ # Minimal example of the concept +02.problem.realistic/ # More realistic usage +03.problem.edge-cases/ # Handling edge cases +``` + +### Pattern: Building Features + +Steps build on each other to create a feature: + +``` +01.problem.setup/ # Set up the foundation +02.problem.core/ # Implement core functionality +03.problem.polish/ # Add polish and error handling +``` + +### Pattern: Technique Variations + +Steps show different approaches to the same problem: + +``` +01.problem.approach-a/ # First approach +02.problem.approach-b/ # Alternative approach +03.problem.when-to-use/ # Comparing approaches +``` + +### Pattern: Bug Fix + +Present broken code and have learners fix it: + +``` +01.problem.identify/ # Identify the bug +02.problem.fix/ # Fix the bug +03.problem.prevent/ # Add tests/guards to prevent regression +``` + +## Quality Checklist + +For each exercise step: + +- [ ] Problem has clear, numbered TODO comments +- [ ] Each TODO uses appropriate emoji characters +- [ ] Problem code runs (even if incomplete) +- [ ] Solution is minimal (only required changes) +- [ ] Solution code runs and works correctly +- [ ] README.mdx explains what to do (problem) or what was done (solution) +- [ ] Diff between problem and solution is readable +- [ ] Tests exist and pass (if testing is part of the exercise) +- [ ] No console errors or warnings +- [ ] Code follows project conventions + +## Common Mistakes + +### 1. Too Many Changes Per Step +**Problem:** Diff is overwhelming +**Solution:** Break into multiple steps + +### 2. Unclear Instructions +**Problem:** Learner doesn't know what to do +**Solution:** Be specific, use code hints + +### 3. Non-Functional Problem Code +**Problem:** App crashes before learner can work +**Solution:** Ensure problem code runs + +### 4. Solution Has Extra Changes +**Problem:** Diff includes unrelated improvements +**Solution:** Only change what's required + +### 5. Missing Context +**Problem:** Learner doesn't understand why +**Solution:** Add context in exercise README diff --git a/instructor/05-mdx-and-content.md b/instructor/05-mdx-and-content.md new file mode 100644 index 0000000..ba81f20 --- /dev/null +++ b/instructor/05-mdx-and-content.md @@ -0,0 +1,398 @@ +# MDX and Content Guide + +## MDX Basics + +MDX files combine Markdown with JSX components. All workshop content uses MDX (`.mdx` extension). + +## Code Blocks + +### Basic Code Block + +````mdx +```tsx +function Hello() { + return
Hello World
+} +``` +```` + +### Code Block Options + +Code blocks support several options from `@kentcdodds/md-temp`: + +````mdx +```tsx filename=app/components/hello.tsx nocopy nonumber remove=1,3-5 add=2,6-8 lines=3,9-12 +// This line will be marked for removal (line 1) +// This line will be marked for addition (line 2) +// This line will be highlighted (line 3) +``` +```` + +### Options Reference + +| Option | Purpose | Example | +|--------|---------|---------| +| `filename` | Show filename header | `filename=app/root.tsx` | +| `nocopy` | Hide copy button | `nocopy` | +| `nonumber` | Hide line numbers | `nonumber` | +| `lines` | Highlight specific lines | `lines=3,9-12` | +| `add` | Mark lines as added (green) | `add=2,6-8` | +| `remove` | Mark lines as removed (red) | `remove=1,3-5` | + +### Shell Commands + +Shell commands with `sh` language don't get line numbers by default: + +````mdx +```sh nonumber +npm install react react-dom +``` +```` + +## Callouts + +Callouts draw attention to important information: + +### Callout Types + +```mdx +Gray - less important notes + +Blue - helpful information + +Yellow - things to be careful about + +Red - critical warnings + +Green - positive reinforcement +``` + +### Callout Classes + +```mdx + +Smaller text for tangential information + + + +Larger, bold text for critical information + + + +
Warning Title
+Notification style with title (warning/danger only) +
+``` + +## Built-in Components + +### InlineFile + +Link to open a file in the editor: + +```mdx + +``` + +With custom text: + +```mdx +Open the root file +``` + +### LinkToApp + +Link to a page in the running app: + +```mdx + +``` + +With custom text: + +```mdx +Go to the dashboard +``` + +### DiffLink + +Link to show a diff between two exercise steps: + +```mdx + + + See the diff + + + + + View changes + + + + + See what changes + +``` + +### NextDiffLink and PrevDiffLink + +Shortcuts for adjacent diffs. Often used with Kellie (🧝‍♀️) to explain work: + +```mdx +🧝‍♀️ I've already set up the form structure for you. +Check what I did +``` + +```mdx +🧝‍♀️ In the next step, I'll apply the same pattern to the rest of the fields. +Feel free to try it yourself first for more practice! +See the upcoming changes +``` + +Use these when: +- Explaining work done between steps (PrevDiffLink) +- Showing repetitive work learners can skip or try (NextDiffLink) + +### EpicVideo + +Embed an EpicWeb/EpicReact video (added after videos are recorded): + +```mdx + +``` + + +**Note for new workshops:** Don't include `` components when first creating a workshop. Videos are recorded and added later. Just write the content without video embeds initially. + + +### VideoEmbed + +Embed any video (YouTube, etc.): + +```mdx + +``` + +## Mermaid Diagrams + +Create diagrams using Mermaid: + +````mdx +```mermaid +flowchart TB + A[Start] --> B{Decision} + B -->|Yes| C[Do Something] + B -->|No| D[Do Something Else] + C --> E[End] + D --> E +``` +```` + +### Useful Diagram Types + +**Flowchart:** +````mdx +```mermaid +flowchart LR + Client --> Server --> Database +``` +```` + +**Sequence Diagram:** +````mdx +```mermaid +sequenceDiagram + User->>App: Click button + App->>API: Fetch data + API-->>App: Return data + App-->>User: Display result +``` +```` + +## Content Writing Guidelines + +### Voice and Tone + +1. **Conversational** - Write like you're talking to a colleague +2. **Encouraging** - Celebrate progress, don't criticize mistakes +3. **Clear** - Use simple language, avoid jargon +4. **Active** - Use active voice ("Click the button" not "The button should be clicked") + +### Using Emoji Characters + +The workshop has established characters: + +| Emoji | Character | Purpose | +|-------|-----------|---------| +| 👨‍💼 | Peter the Product Manager | Requirements, context, user needs | +| 🐨 | Kody the Koala | Specific instructions, what to do | +| 🧝‍♀️ | Kellie the Co-worker | Pre-done work, setup context | +| 🦺 | Lily the Life Jacket | TypeScript-specific guidance | +| 💰 | Marty the Money Bag | Tips and code hints | +| 📜 | Dominic the Document | Documentation links | +| 📝 | Nancy the Notepad | Encourages note-taking | +| 🦉 | Olivia the Owl | Best practices, insights | +| 💣 | Barry the Bomb | Code to delete | +| 🚨 | Alfred the Alert | Test failure explanations | + +### Formatting Best Practices + +**Use code formatting for:** +- File names: `app/root.tsx` +- Function names: `useState` +- Variable names: `count` +- Commands: `npm install` +- Key names: `Enter` + +**Use bold for:** +- Important terms on first use +- Emphasis in instructions + +**Use italics for:** +- Introducing concepts +- Subtle emphasis + +### Common Patterns + +**Introducing a concept:** +```mdx +# Using useState + +The `useState` hook lets you add state to functional components. When state +changes, React re-renders the component with the new value. + +```tsx +const [count, setCount] = useState(0) +``` + +In this exercise, you'll add state to track... +``` + +**Giving instructions:** +```mdx +👨‍💼 Users want to see their profile information on the dashboard. + +🐨 Open and: + +1. Import the `useUser` hook from `~/utils/user` +2. Call `useUser()` at the top of the component +3. Display the user's name in the header + +💰 Here's how to import the hook: +```tsx +import { useUser } from '~/utils/user' +``` +``` + +**Providing context:** +```mdx + +You might wonder why we use `useState` instead of a regular variable. The key +difference is that `useState` tells React to re-render when the value changes. +A regular variable would change, but React wouldn't know to update the UI. + +``` + +**Warning about common mistakes:** +```mdx + +Don't call hooks inside conditions or loops! React relies on hooks being +called in the same order every render. Putting them in conditions breaks this. + +``` + +## Complete README.mdx Examples + +These examples show complete content. Note: `` is added later after videos are recorded - omit it when first creating a workshop. + +### Workshop Introduction (exercises/README.mdx) + +```mdx +# React Fundamentals ⚛ + +👨‍💼 Hello! I'm Peter the Product Manager and I'm here to help you understand +what users need so you can build great React applications! + +In this workshop, we'll cover the fundamentals you need to build React apps: + +1. Creating elements with JavaScript +2. Using JSX for cleaner syntax +3. Building reusable components +4. Adding TypeScript for type safety +5. Styling components +6. Handling forms +7. Managing errors +8. Rendering lists + + +The first few exercises use plain HTML files to keep things simple. Later +exercises use TypeScript for a more realistic development experience. + + +Let's get started! +``` + +### Problem Instruction (XX.problem.step-name/README.mdx) + +```mdx +# Creating a Custom Hook + +👨‍💼 We have logic for managing a counter that's duplicated in several +components. Let's extract it into a reusable custom hook! + +🐨 Open and create a +`useCounter` hook that: + +1. Accepts an optional `initialValue` parameter (default: 0) +2. Returns an object with: + - `count` - the current count + - `increment` - function to add 1 + - `decrement` - function to subtract 1 + - `reset` - function to reset to initial value + +💰 Here's the function signature to get you started: + +```ts +export function useCounter(initialValue = 0) { + // Your implementation here +} +``` + +📜 [React Custom Hooks Documentation](https://react.dev/learn/reusing-logic-with-custom-hooks) +``` + +### Solution Confirmation (XX.solution.step-name/README.mdx) + +```mdx +# Creating a Custom Hook + +👨‍💼 Excellent! Now we have a reusable `useCounter` hook that any component +can use. Notice how the hook encapsulates all the counter logic, making our +components simpler. + +Key insights: +- Custom hooks are just functions that use other hooks +- They must start with `use` to follow React's rules +- They can accept parameters and return anything + +Let's see how to test this hook next. +``` + +### Exercise Summary (FINISHED.mdx) + +```mdx +# Custom Hooks + +👨‍💼 Great work! You've learned how to extract reusable logic into custom hooks. + +Remember: +- 🎯 Hooks help share stateful logic between components +- 📏 Custom hooks follow the same rules as built-in hooks +- 🧪 Hooks are easy to test in isolation + +Now you're ready to tackle more advanced patterns! +``` diff --git a/instructor/06-package-configuration.md b/instructor/06-package-configuration.md new file mode 100644 index 0000000..d285d1a --- /dev/null +++ b/instructor/06-package-configuration.md @@ -0,0 +1,414 @@ +# Package Configuration Guide + +## Root package.json + +The root `package.json` configures the entire workshop. + +### Minimal Configuration + +```json +{ + "name": "my-workshop", + "private": true, + "epicshop": { + "title": "Workshop Title", + "githubRepo": "https://github.com/username/repo", + "instructor": { + "name": "Your Name", + "avatar": "/images/instructor.png" + } + }, + "type": "module", + "scripts": { + "start": "npx --prefix ./epicshop epicshop start", + "dev": "npx --prefix ./epicshop epicshop start", + "setup": "node ./epicshop/setup.js" + } +} +``` + +### Full Configuration Example + +```json +{ + "name": "react-fundamentals", + "private": true, + "epicshop": { + "title": "React Fundamentals ⚛", + "subtitle": "Learn the foundational concepts for building React applications", + "subdomain": "react-fundamentals", + "githubRepo": "https://github.com/epicweb-dev/react-fundamentals", + "instructor": { + "name": "Kent C. Dodds", + "avatar": "/images/instructor.png", + "𝕏": "kentcdodds" + }, + "product": { + "host": "www.epicreact.dev", + "slug": "react-fundamentals", + "displayName": "EpicReact.dev", + "displayNameShort": "Epic React", + "logo": "/logo.svg", + "discordChannelId": "1285244676286189569", + "discordTags": ["1285246046498328627"] + }, + "stackBlitzConfig": { + "view": "editor", + "file": "src/App.tsx" + }, + "forms": { + "workshop": "https://docs.google.com/forms/...", + "exercise": "https://docs.google.com/forms/..." + }, + "testTab": { + "enabled": true + }, + "scripts": { + "postupdate": "npm run build" + }, + "initialRoute": "/", + "onboardingVideo": "https://www.epicweb.dev/tips/get-started" + }, + "type": "module", + "scripts": { + "postinstall": "cd ./epicshop && npm install", + "start": "npx --prefix ./epicshop epicshop start", + "dev": "npx --prefix ./epicshop epicshop start", + "setup": "node ./epicshop/setup.js", + "setup:custom": "node ./epicshop/setup-custom.js", + "lint": "eslint .", + "format": "prettier --write .", + "typecheck": "tsc -b" + }, + "workspaces": [ + "exercises/*/*", + "examples/*" + ], + "devDependencies": { + "@epic-web/config": "^1.21.3", + "eslint": "^9.39.0", + "prettier": "^3.7.0", + "typescript": "^5.9.0" + }, + "prettier": "@epic-web/config/prettier", + "engines": { + "node": ">=20", + "npm": ">=8.16.0" + } +} +``` + +## epicshop Configuration Options + +### Required Options + +| Option | Type | Description | +|--------|------|-------------| +| `title` | string | Workshop title (shown in UI) | +| `githubRepo` | string | GitHub repository URL | +| `instructor.name` | string | Instructor's name | +| `instructor.avatar` | string | Path to avatar image | + +### Core Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `subtitle` | string | - | Workshop subtitle | +| `subdomain` | string | package name | Subdomain for local URL | +| `githubRoot` | string | - | Root URL for GitHub links | +| `initialRoute` | string | "/" | Initial route when starting app | + +### Instructor Options + +| Option | Type | Description | +|--------|------|-------------| +| `instructor.name` | string | Full name | +| `instructor.avatar` | string | Path to avatar (min 112x112px) | +| `instructor.𝕏` | string | X (Twitter) handle | +| `instructor.xHandle` | string | Alternative to 𝕏 | + +### Product Options + +For workshops associated with a product (EpicWeb, EpicReact, etc.): + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `product.host` | string | "www.epicweb.dev" | Host domain | +| `product.slug` | string | - | Product slug for API | +| `product.displayName` | string | "EpicWeb.dev" | Full display name | +| `product.displayNameShort` | string | "Epic Web" | Short display name | +| `product.logo` | string | "/logo.svg" | Logo path | +| `product.discordChannelId` | string | - | Discord channel ID | +| `product.discordTags` | string[] | - | Discord tag IDs | + +### StackBlitz Options + +Configure the StackBlitz embed for deployed workshops: + +| Option | Type | Description | +|--------|------|-------------| +| `stackBlitzConfig.title` | string | Project title | +| `stackBlitzConfig.startScript` | string | Script to run | +| `stackBlitzConfig.view` | "editor" \| "preview" \| "both" | Initial view | +| `stackBlitzConfig.file` | string | Initial file to open | + +Set to `null` to disable StackBlitz: +```json +{ + "epicshop": { + "stackBlitzConfig": null + } +} +``` + +### Forms Options + +Configure feedback forms: + +```json +{ + "epicshop": { + "forms": { + "workshop": "https://docs.google.com/forms/...?entry.123={workshopTitle}", + "exercise": "https://docs.google.com/forms/...?entry.123={workshopTitle}&entry.456={exerciseTitle}" + } + } +} +``` + +Available tokens: +- `{workshopTitle}` - Workshop title +- `{exerciseTitle}` - Current exercise title + +### Test Tab Options + +Control the test tab visibility: + +```json +{ + "epicshop": { + "testTab": { + "enabled": true + } + } +} +``` + +### Sidecar Processes + +Run additional processes alongside the workshop: + +```json +{ + "epicshop": { + "sidecarProcesses": { + "BackendAPI": "npm run dev --prefix ./backend", + "Database": "docker run --rm -p 5432:5432 postgres:15" + } + } +} +``` + +### Post-Update Script + +Run commands after workshop updates: + +```json +{ + "epicshop": { + "scripts": { + "postupdate": "npm run build" + } + } +} +``` + +### Notifications + +Add workshop-specific notifications: + +```json +{ + "epicshop": { + "notifications": [ + { + "id": "welcome", + "title": "Welcome!", + "message": "Check out the resources tab for additional materials.", + "type": "info" + }, + { + "id": "breaking-change", + "title": "Breaking Change", + "message": "React 19 changed how refs work. See updated exercises.", + "type": "warning", + "link": "https://react.dev/blog/...", + "expiresAt": "2025-12-31" + } + ] + } +} +``` + +Notification types: `info`, `warning`, `danger` + +## Exercise-Level Configuration + +Individual exercises can override global settings in their `package.json`: + +```json +{ + "name": "01.problem.step-name", + "epicshop": { + "testTab": { + "enabled": false + }, + "initialRoute": "/admin", + "stackBlitzConfig": null + }, + "scripts": { + "dev": "vite" + } +} +``` + +### Common Overrides + +**Disable tests for an exercise:** +```json +{ + "epicshop": { + "testTab": { + "enabled": false + } + } +} +``` + +**Set custom initial route:** +```json +{ + "epicshop": { + "initialRoute": "/dashboard" + } +} +``` + +**Disable StackBlitz:** +```json +{ + "epicshop": { + "stackBlitzConfig": null + } +} +``` + +## Scripts Configuration + +### Essential Scripts + +```json +{ + "scripts": { + "postinstall": "cd ./epicshop && npm install", + "start": "npx --prefix ./epicshop epicshop start", + "dev": "npx --prefix ./epicshop epicshop start", + "setup": "node ./epicshop/setup.js", + "setup:custom": "node ./epicshop/setup-custom.js" + } +} +``` + +### Optional Scripts + +```json +{ + "scripts": { + "lint": "eslint .", + "format": "prettier --write .", + "typecheck": "tsc -b", + "test": "npm run test --silent --prefix playground", + "validate:all": "npm-run-all --parallel lint typecheck" + } +} +``` + +## Workspaces Configuration + +For workshops with multiple package apps: + +```json +{ + "workspaces": [ + "exercises/*/*", + "examples/*" + ] +} +``` + +This allows: +- Running `npm install` once at root +- Sharing dependencies across exercises +- Using workspace protocols (`workspace:*`) + +## Engine Requirements + +Specify required versions: + +```json +{ + "engines": { + "node": ">=20", + "npm": ">=8.16.0", + "git": ">=2.18.0" + } +} +``` + +## Dependencies + +### Root Dependencies + +Common root-level dev dependencies: + +```json +{ + "devDependencies": { + "@epic-web/config": "^1.21.3", + "@epic-web/workshop-utils": "^6.49.3", + "eslint": "^9.39.0", + "prettier": "^3.7.0", + "typescript": "^5.9.0" + } +} +``` + +### Exercise Dependencies + +Exercises can have their own dependencies: + +```json +{ + "name": "01.problem.api-calls", + "dependencies": { + "axios": "^1.6.0" + }, + "devDependencies": { + "msw": "^2.0.0", + "vitest": "^1.0.0" + } +} +``` + +## Configuration Checklist + +Before publishing, verify: + +- [ ] `title` is set and descriptive +- [ ] `githubRepo` or `githubRoot` is set +- [ ] `instructor.name` and `instructor.avatar` are set +- [ ] Avatar image exists at specified path +- [ ] All required scripts are defined +- [ ] `engines` specifies minimum versions +- [ ] `private: true` is set (workshops shouldn't publish to npm) +- [ ] `workspaces` configured if using workspace structure diff --git a/instructor/07-testing-and-validation.md b/instructor/07-testing-and-validation.md new file mode 100644 index 0000000..442251c --- /dev/null +++ b/instructor/07-testing-and-validation.md @@ -0,0 +1,414 @@ +# Testing and Validation Guide + +## Types of Testing in Epic Workshops + +### 1. In-Browser Tests +Tests that run directly in the browser, testing the rendered UI. + +### 2. Package Tests +Tests that run via `npm test`, typically using Vitest or Jest. + +### 3. E2E Tests +End-to-end tests using Playwright that test the full application. + +## In-Browser Tests + +### When to Use +- Simple apps without a build system +- Testing rendered DOM +- Quick feedback for learners + +### Setting Up + +Create a test file in the exercise directory: + +```typescript +// index.test.ts +import { testStep, expect, dtl } from '@epic-web/workshop-utils/test' + +// Import your app code +import './index.tsx' + +await testStep('Button should be rendered', async () => { + const button = await dtl.screen.findByRole('button', { name: /click me/i }) + expect(button).toBeInTheDocument() +}) + +await testStep('Clicking button should increment count', async () => { + const button = await dtl.screen.findByRole('button', { name: /click me/i }) + await dtl.userEvent.click(button) + expect(button).toHaveTextContent('Count: 1') +}) +``` + +### Test Utilities + +The `@epic-web/workshop-utils/test` module provides: + +| Export | Description | +|--------|-------------| +| `testStep` | Define a test step with name and async function | +| `expect` | Vitest expect with DOM matchers | +| `dtl` | @testing-library/dom utilities | +| `dtl.screen` | Screen queries | +| `dtl.userEvent` | User event simulation | + +### Example: Testing a Counter + +```typescript +import { testStep, expect, dtl } from '@epic-web/workshop-utils/test' +import './index.tsx' + +await testStep('Counter starts at 0', async () => { + const count = await dtl.screen.findByText(/count: 0/i) + expect(count).toBeInTheDocument() +}) + +await testStep('Increment button increases count', async () => { + const incrementBtn = await dtl.screen.findByRole('button', { name: /increment/i }) + await dtl.userEvent.click(incrementBtn) + expect(await dtl.screen.findByText(/count: 1/i)).toBeInTheDocument() +}) + +await testStep('Decrement button decreases count', async () => { + const decrementBtn = await dtl.screen.findByRole('button', { name: /decrement/i }) + await dtl.userEvent.click(decrementBtn) + expect(await dtl.screen.findByText(/count: 0/i)).toBeInTheDocument() +}) +``` + +## Package Tests (Vitest) + +### When to Use +- Project apps with build systems +- Testing logic in isolation +- More complex test scenarios + +### Setting Up + +Add Vitest to the exercise: + +```json +// package.json +{ + "scripts": { + "test": "vitest" + }, + "devDependencies": { + "vitest": "^1.0.0" + } +} +``` + +Create a test file: + +```typescript +// my-function.test.ts +import { describe, it, expect } from 'vitest' +import { myFunction } from './my-function' + +describe('myFunction', () => { + it('should return expected value', () => { + expect(myFunction('input')).toBe('expected output') + }) +}) +``` + +### Vitest Configuration + +```typescript +// vitest.config.ts +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'jsdom', // or 'node' for non-DOM tests + globals: true, + }, +}) +``` + +### Example: Testing a React Component + +```typescript +// counter.test.tsx +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { expect, test } from 'vitest' +import { Counter } from './counter' + +test('Counter increments when clicked', async () => { + const user = userEvent.setup() + render() + + const button = screen.getByRole('button', { name: /increment/i }) + expect(screen.getByText('Count: 0')).toBeInTheDocument() + + await user.click(button) + expect(screen.getByText('Count: 1')).toBeInTheDocument() +}) +``` + +## E2E Tests (Playwright) + +### When to Use +- Testing full user flows +- Complex applications +- Integration testing + +### Setting Up + +Add Playwright to the epicshop directory: + +```json +// epicshop/package.json +{ + "devDependencies": { + "@playwright/test": "^1.40.0" + } +} +``` + +Create test file: + +```typescript +// epicshop/tests/exercise.spec.ts +import { test, expect } from '@playwright/test' + +test('user can complete exercise', async ({ page }) => { + await page.goto('/') + await expect(page.getByRole('heading')).toContainText('Welcome') +}) +``` + +### Playwright Configuration + +The workshop app runs tests with a `PORT` environment variable. Your config must use it: + +```typescript +// playwright.config.ts +import { defineConfig, devices } from '@playwright/test' + +const PORT = process.env.PORT || '3000' + +export default defineConfig({ + testDir: './tests/e2e', + timeout: 15 * 1000, + expect: { + timeout: 5 * 1000, + }, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: `http://localhost:${PORT}/`, + trace: 'on-first-retry', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + + webServer: { + command: process.env.CI ? 'npm run start' : 'npm run dev', + port: Number(PORT), + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + stderr: 'pipe', + env: { PORT }, + }, +}) +``` + +Key points: +- Read `PORT` from environment with fallback: `process.env.PORT || '3000'` +- Use `PORT` in `baseURL`: `` `http://localhost:${PORT}/` `` +- Pass `PORT` to `webServer.port` and `webServer.env` + +## Testing Best Practices + +### 1. Test the Solution, Not the Problem + +Tests should be in solution directories, not problem directories: + +``` +01.problem.feature/ +└── index.tsx # No tests + +01.solution.feature/ +├── index.tsx +└── index.test.ts # Tests here +``` + +### 2. Test Behavior, Not Implementation + +```typescript +// ✅ Good - tests behavior +test('form shows error when email is invalid', async () => { + render(
) + await user.type(screen.getByLabelText('Email'), 'invalid') + await user.click(screen.getByRole('button', { name: /submit/i })) + expect(screen.getByText(/invalid email/i)).toBeInTheDocument() +}) + +// ❌ Bad - tests implementation +test('useState is called with empty string', () => { + // Don't test React internals +}) +``` + +### 3. Use Accessible Queries + +```typescript +// ✅ Preferred - uses accessible queries +screen.getByRole('button', { name: /submit/i }) +screen.getByLabelText('Email') +screen.getByText('Welcome') + +// ❌ Avoid - brittle queries +document.querySelector('.submit-btn') +document.getElementById('email-input') +``` + +### 4. Make Tests Deterministic + +```typescript +// ✅ Good - deterministic +const fixedDate = new Date('2024-01-15') +vi.setSystemTime(fixedDate) + +// ❌ Bad - flaky +const now = new Date() // Changes every run +``` + +### 5. Keep Tests Fast + +- Mock expensive operations +- Use minimal setup +- Run tests in parallel when possible + +## Disabling Tests + +### Disable for Entire Workshop + +```json +// package.json +{ + "epicshop": { + "testTab": { + "enabled": false + } + } +} +``` + +### Disable for Specific Exercise + +```json +// exercises/01.exercise/01.problem.step/package.json +{ + "epicshop": { + "testTab": { + "enabled": false + } + } +} +``` + +## Validating the Workshop + +### Pre-Flight Checklist + +Before sharing the workshop, verify: + +#### Structure +- [ ] All directories follow naming conventions +- [ ] Every problem has a matching solution +- [ ] README.mdx files exist for all levels +- [ ] FINISHED.mdx files exist for all exercises + +#### Code +- [ ] All problem code runs without errors +- [ ] All solution code runs without errors +- [ ] Diffs between problem/solution are reasonable +- [ ] No TODO comments remain in solutions + +#### Tests +- [ ] All tests pass +- [ ] Tests verify the learning objective +- [ ] No flaky tests + +#### Content +- [ ] Video URLs are valid (if applicable) +- [ ] Documentation links work +- [ ] Emoji markers are correct +- [ ] Instructions are clear and complete + +### Validation Commands + +```bash +# Start the workshop app +npm run dev + +# Run linting +npm run lint + +# Run type checking +npm run typecheck + +# Run tests +npm test + +# Run all validations +npm run validate:all +``` + +### Using the Epic Workshop MCP Server + +If you have access to the Epic Workshop MCP server, you can use it to: + +1. **List exercises**: Check workshop structure +2. **Get file contents**: Verify file content +3. **Run exercises**: Test that apps start correctly +4. **Run tests**: Verify tests pass + +### Manual Validation + +Walk through the workshop as a learner: + +1. Start at the workshop introduction +2. Read each exercise introduction +3. Attempt each problem WITHOUT looking at the solution +4. Use the diff tab to compare your solution +5. Read each summary + +This helps identify: +- Unclear instructions +- Missing context +- Difficulty spikes +- Broken code + +## Common Issues + +### Tests Fail in CI but Pass Locally + +- Check for environment differences +- Ensure no hardcoded paths +- Mock system dependencies (date, random, etc.) + +### Flaky Tests + +- Use waitFor/findBy instead of getBy +- Increase timeouts for slow operations +- Mock network requests + +### Tests Too Slow + +- Mock expensive operations +- Use test.concurrent where possible +- Split large test files diff --git a/instructor/08-best-practices.md b/instructor/08-best-practices.md new file mode 100644 index 0000000..c4034f3 --- /dev/null +++ b/instructor/08-best-practices.md @@ -0,0 +1,314 @@ +# Best Practices from Successful Workshops + +This guide captures patterns and practices from analyzing successful Epic Workshops including React Fundamentals, Mocking Techniques, Web Forms, React Performance, MCP Auth, and others. + +## Exercise Design Patterns + +### Pattern 1: Concept → Application → Edge Cases + +The most successful exercises follow this progression: + +``` +01.problem.basic # Introduce the concept simply +02.problem.realistic # Apply to a real scenario +03.problem.edge-cases # Handle edge cases +``` + +**Example from React Fundamentals - Error Boundaries:** +``` +01.problem.composition # Create an ErrorBoundary component +02.problem.show-boundary # Display the error UI +03.problem.reset # Add reset functionality +``` + +### Pattern 2: Build a Feature Incrementally + +Each step adds one piece to a complete feature: + +``` +01.problem.setup # Foundation +02.problem.core # Main functionality +03.problem.polish # Error handling, UX +``` + +**Example from Mocking Techniques - Network:** +``` +01.problem.setup # Set up MSW +02.problem.handlers # Add request handlers +03.problem.error # Mock error responses +04.problem.network-error # Mock network failures +05.problem.timing # Control response timing +``` + +### Pattern 3: Before and After + +Show the "wrong" way, then the "right" way: + +``` +01.problem.naive # Initial approach +02.problem.problems # Show issues with naive approach +03.problem.better # Introduce better approach +``` + +## Code Patterns + +### Minimal Problem Code + +Problem code should be minimal but functional: + +```tsx +// ✅ Good - minimal, clear where to add code +function Counter() { + // 🐨 Add useState hook here + + return ( + + ) +} +``` + +```tsx +// ❌ Bad - too much noise, unclear what to change +function Counter() { + const [count, setCount] = useState(0) // Is this done? + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + // ... 50 more lines +} +``` + +### Progressive Enhancement of Code + +Each step should add to the previous solution: + +```tsx +// Step 1 Problem: Basic counter +function Counter() { + // 🐨 Add useState + return +} + +// Step 1 Solution → Step 2 Problem: Add increment +function Counter() { + const [count, setCount] = useState(0) + return +} + +// Step 2 Solution → Step 3 Problem: Add decrement +function Counter() { + const [count, setCount] = useState(0) + return ( +
+ + Count: {count} + {/* 🐨 Add increment button */} +
+ ) +} +``` + +### Realistic File Organization + +Use file structures that mirror real projects: + +``` +# ✅ Good - realistic structure +app/ +├── components/ +│ └── counter.tsx +├── hooks/ +│ └── use-counter.ts +└── routes/ + └── index.tsx + +# ❌ Bad - everything in one file +index.tsx # 500 lines of code +``` + +## Content Patterns + +### Exercise Introductions + +Strong introductions have: + +1. **Hook** - Why this matters +2. **Context** - Background information +3. **Example** - Code showing the concept +4. **Preview** - What learner will do + +```mdx +# Code Splitting + +Modern apps can grow large, causing slow initial loads. Code splitting lets you +load code only when needed. + +```tsx +// Instead of importing everything upfront... +import { HugeComponent } from './huge-component' + +// You can load on demand +const HugeComponent = lazy(() => import('./huge-component')) +``` + +In this exercise, you'll add code splitting to a globe component that's slowing +down the initial page load. +``` + +### Problem Instructions + +Effective instructions: + +1. **Set the scene** (👨‍💼) +2. **Give specific tasks** (🐨) +3. **Provide hints** (💰) +4. **Link documentation** (📜) + +```mdx +👨‍💼 Users are complaining that the page loads slowly because we're loading +a heavy globe component that most users never see. + +🐨 Open and: + +1. Import `lazy` from React +2. Replace the direct import of `Globe` with a lazy import +3. Wrap the `Globe` usage in a `Suspense` boundary + +💰 Here's the lazy import syntax: +```tsx +const Globe = lazy(() => import('./globe')) +``` + +📜 [React lazy documentation](https://react.dev/reference/react/lazy) +``` + +### Solution Explanations + +Keep solutions brief but insightful: + +```mdx +👨‍💼 Perfect! Now the Globe component only loads when the user navigates to +that section. The initial bundle is 200KB smaller. + +Notice how `Suspense` handles the loading state automatically. Without it, +React would throw an error when the lazy component isn't ready yet. +``` + +## Difficulty Progression + +### Within an Exercise + +Each step should be slightly harder: + +| Step | Complexity | Time | +|------|------------|------| +| 01 | Simple | 5-10 min | +| 02 | Medium | 10-15 min | +| 03 | Challenging | 15-20 min | +| 04+ | Advanced | 15-25 min | + +### Across Exercises + +Exercises should build in complexity: + +| Exercise | Focus | +|----------|-------| +| 01-02 | Fundamentals, simple concepts | +| 03-05 | Core skills, realistic scenarios | +| 06-08 | Advanced patterns, edge cases | +| 09-10 | Complex integrations, real-world challenges | + +## Common Anti-Patterns + +### 1. Kitchen Sink Exercise + +**Problem:** Exercise tries to teach too many concepts. + +**Solution:** Split into multiple exercises, each focused on one concept. + +### 2. Magic Code + +**Problem:** Code appears without explanation. + +**Solution:** Every new concept should be introduced in the exercise README. + +### 3. Overwhelming Diff + +**Problem:** Diff between problem and solution is 100+ lines. + +**Solution:** Break into smaller steps; each step should have a <30 line diff. + +### 4. Abandoned Learner + +**Problem:** Instructions say "implement X" without guidance. + +**Solution:** Provide specific steps, hints, and documentation links. + +### 5. Copy-Paste Solution + +**Problem:** Learner can copy-paste without understanding. + +**Solution:** Add "why" explanations; require adaptation, not just copying. + +### 6. Dead-End Errors + +**Problem:** Learner gets stuck with unhelpful error messages. + +**Solution:** Test problem code runs; provide troubleshooting hints. + +## Quality Markers + +### High-Quality Exercises Have: + +- [ ] Clear learning objectives stated upfront +- [ ] Realistic, relatable context +- [ ] Appropriate difficulty for position in workshop +- [ ] Specific, actionable instructions +- [ ] Helpful hints without giving away solutions +- [ ] Clean, readable diffs +- [ ] Tests that verify the learning objective +- [ ] Summaries that reinforce key learnings + +### High-Quality Workshops Have: + +- [ ] Logical exercise order with clear progression +- [ ] Consistent difficulty curve (no sudden spikes) +- [ ] Balance of theory and practice +- [ ] Real-world applicability +- [ ] Appropriate length (not too long, not too short) +- [ ] Engaging, conversational tone +- [ ] Complete coverage of stated objectives + +## Learner Experience Tips + +### 1. Respect Time + +- Don't add unnecessary complexity +- Provide "fast path" for experienced learners +- Mark optional/bonus content clearly + +### 2. Build Confidence + +- Start with wins (easy first steps) +- Celebrate progress in summaries +- Provide escape hatches (hints, solutions) + +### 3. Provide Context + +- Explain "why" not just "what" +- Connect to real-world scenarios +- Reference prior exercises + +### 4. Support Different Learning Styles + +- Written instructions for readers +- Video content for watchers +- Hands-on coding for doers +- Tests for verifiers + +### 5. Handle Failure Gracefully + +- Problem code should never crash +- Error messages should be helpful +- Diff tab provides ultimate escape hatch diff --git a/instructor/09-emoji-guide.md b/instructor/09-emoji-guide.md new file mode 100644 index 0000000..d8a6c29 --- /dev/null +++ b/instructor/09-emoji-guide.md @@ -0,0 +1,171 @@ +# Emoji Character Guide + +Epic Workshops use emoji characters to guide learners. Each emoji represents a specific role. + +## Character Reference + +| Emoji | Name | Role | +|-------|------|------| +| 👨‍💼 | Peter the Product Manager | Helps us know what our users want | +| 🧝‍♀️ | Kellie the Co-worker | Does work ahead of your exercises | +| 🐨 | Kody the Koala | Tells you something specific to do | +| 🦺 | Lily the Life Jacket | TypeScript-specific help | +| 💰 | Marty the Money Bag | Specific tips and code hints | +| 📝 | Nancy the Notepad | Encourages note-taking | +| 🦉 | Olivia the Owl | Best practice notes | +| 📜 | Dominic the Document | Links to documentation | +| 💣 | Barry the Bomb | Code to delete | +| 🚨 | Alfred the Alert | Test failure explanations | + +## Usage in Code Comments + +```tsx +// 🐨 [What to do] +// 💰 [Hint showing how] +// 📜 [Link to documentation] + +function MyComponent() { + // 🐨 Add useState hook here + // 💰 const [state, setState] = useState(initialValue) + + // 🦺 Add type annotation + // @ts-expect-error 💣 Remove when typed + + return ( +
+ {/* 🐨 Render the state here */} +
+ ) +} +``` + +### HTML Comments + +```html + + + + + + + +``` + +## Usage in README.mdx Files + +### Problem Instructions (XX.problem.step-name/README.mdx) + +```mdx +# Step Title + +👨‍💼 [Context and user need] + +🧝‍♀️ [What's already been set up, if applicable] + +🐨 [Main instructions with file reference] + +💰 [Helpful hints] + +📜 [Documentation links] + +📝 [Reflection prompt, if appropriate] +``` + +### Solution Explanation (XX.solution.step-name/README.mdx) + +```mdx +# Step Title + +👨‍💼 [Brief confirmation of completion] + +🦉 [Key insight or best practice] +``` + +### Exercise Summary (FINISHED.mdx) + +```mdx +# Exercise Title + +👨‍💼 [Celebration and summary] + +📝 [Encourage notes on key takeaways] +``` + +## Test Failure Messages + +Use Alfred (🚨) in test assertion messages: + +```tsx +expect( + myFunction, + `🚨 Make sure you exported the function as a named export, not default` +).toBeDefined() +``` + +## Character Guidelines + +### 👨‍💼 Peter +- Friendly and encouraging +- Thinks about users and business needs +- Celebrates success ("Great job!", "Perfect!") + +### 🐨 Kody +- Direct and helpful +- Uses action verbs ("Open", "Add", "Create") +- Breaks tasks into clear steps + +### 🧝‍♀️ Kellie +- Helpful colleague vibe +- Explains work that's been done or will be done +- Uses diff links to show changes + +**Kellie explains past work** (with ``): +```mdx +🧝‍♀️ I've already set up the form validation for the email field. +You can check what I did here. +``` + +**Kellie explains upcoming work** (with ``): +```mdx +🧝‍♀️ In the next step, I'll apply the same pattern to the remaining +form fields. If you'd like more practice, feel free to do it yourself +before moving on! +Check the upcoming changes +``` + +Use Kellie when: +- Work was done between steps that learners didn't see +- Repetitive work will be done that learners can skip or try themselves +- Setting up boilerplate that isn't the focus of the exercise + +### 🦺 Lily +- Technical and precise +- Focused on type safety +- Explains TypeScript-specific concepts + +### 💰 Marty +- Generous with hints +- Provides code snippets +- Helps when stuck + +### 📜 Dominic +- Points to authoritative sources +- Links to official docs + +### 📝 Nancy +- Encourages active learning +- Prompts reflection + +### 🦉 Olivia +- Wise and insightful +- Shares best practices +- Explains the "why" + +### 💣 Barry +- Marks code for deletion +- Indicates temporary code + +### 🚨 Alfred +- Helps debug test failures +- Explains common mistakes +- Appears in test assertion messages diff --git a/instructor/readme.md b/instructor/readme.md new file mode 100644 index 0000000..22e3fb8 --- /dev/null +++ b/instructor/readme.md @@ -0,0 +1,98 @@ +# Epic Workshop Instructor Guide 📚 + +This directory contains documentation for creating Epic Workshops using AI agents. + +## Quick Start for Agents + +**To create a workshop, start here:** + +``` +Read /instructor/workflow/01-discovery.md and begin the discovery phase. +``` + +The workflow will guide you through each phase, referencing additional documentation as needed. + +## Directory Structure + +``` +instructor/ +├── workflow/ # Step-by-step agent workflow +│ ├── readme.md # Workflow overview +│ ├── 01-discovery.md # Phase 1: Understand topic and scope +│ ├── 02-planning.md # Phase 2: Design exercise structure +│ ├── 03-setup.md # Phase 3: Configure workshop +│ ├── 04-implementation.md # Phase 4: Create exercise code +│ ├── 05-content.md # Phase 5: Write README.mdx files +│ ├── 06-validation.md # Phase 6: Test and verify +│ └── 07-verification.md # Verify diffs and use MCP tools +├── 01-workshop-overview.md # Core concepts and terminology +├── 02-workshop-planning.md # Planning best practices +├── 03-directory-structure.md # File/folder organization +├── 04-writing-exercises.md # Problem/solution creation +├── 05-mdx-and-content.md # MDX formatting and components +├── 06-package-configuration.md # package.json options +├── 07-testing-and-validation.md # Testing exercises +├── 08-best-practices.md # Patterns from successful workshops +└── 09-emoji-guide.md # Character emoji reference +``` + +## How It Works + +1. **Start with the workflow** - Begin at `workflow/01-discovery.md` +2. **Progress through phases** - Each phase references relevant docs +3. **Load context as needed** - Only read detailed docs when required +4. **Get checkpoints** - Confirm with instructor at key points + +## Creating a Workshop + +Tell an AI agent: + +> "I want to create an Epic Workshop about [YOUR TOPIC]. Please read `/instructor/workflow/01-discovery.md` and help me build it." + +The agent will: +1. Ask discovery questions about your topic +2. Design an exercise structure +3. Create the workshop files +4. Write all content +5. Validate everything works + +## Reference Documentation + +These files provide detailed information when needed: + +| File | When to Reference | +|------|-------------------| +| `01-workshop-overview.md` | Understanding Epic Workshop concepts | +| `02-workshop-planning.md` | Designing exercise flow | +| `03-directory-structure.md` | Setting up files and folders | +| `04-writing-exercises.md` | Creating problem/solution pairs | +| `05-mdx-and-content.md` | Formatting MDX content | +| `06-package-configuration.md` | Configuring package.json | +| `07-testing-and-validation.md` | Adding and running tests | +| `08-best-practices.md` | Learning from successful workshops | +| `09-emoji-guide.md` | Using emoji characters correctly | + +## Efficiency Tips + +### Copy, Don't Create +For linear exercises, **copy the previous step** and modify rather than creating files from scratch. This saves tokens and ensures minimal diffs. + +### Use Helper Scripts +```bash +node ./epicshop/fix.js # Auto-fix tsconfig and package.json +npm run format # Format all files +npm run lint -- --fix # Fix lint issues +``` + +### Verify with MCP Tools +The epicshop MCP server provides `get_diff_between_apps` to verify diffs are focused. Use it after each step. + +## Workshop Philosophy + +Epic Workshops follow these principles: + +1. **Learn by Doing** - Students write real code +2. **Incremental Progress** - Build knowledge step by step +3. **Immediate Feedback** - Problems and solutions side by side +4. **Real-World Context** - Exercises mirror actual tasks +5. **Self-Paced** - Works for live and async learning diff --git a/instructor/workflow/01-discovery.md b/instructor/workflow/01-discovery.md new file mode 100644 index 0000000..31cdb66 --- /dev/null +++ b/instructor/workflow/01-discovery.md @@ -0,0 +1,67 @@ +# Phase 1: Discovery + +**Goal:** Understand the topic, audience, and scope before designing exercises. + +## Step 1.1: Understand the Topic + +Ask the instructor these questions: + +``` +To create a great workshop, I need to understand: + +1. What is the main topic? (e.g., "React Server Components", "Vitest Mocking") +2. Who is the target audience? (beginners, intermediate, advanced) +3. What should learners be able to do after completing the workshop? +4. How long should the workshop be? (2-4 hours, 4-8 hours, full day) +5. Are there specific technologies/frameworks required? +``` + +## Step 1.2: Research the Topic + +Before designing exercises: + +1. Review official documentation for the topic +2. Identify core concepts that must be covered +3. Find common pain points and misconceptions +4. Understand best practices and anti-patterns +5. Note any prerequisites learners need + +## Step 1.3: Define Boundaries + +Create a scope document: + +```markdown +## In Scope +- [Core concept 1] +- [Core concept 2] +- [Core concept 3] + +## Out of Scope +- [Related but not essential topic] +- [Advanced topic for future workshop] + +## Prerequisites +- [What learners must already know] +``` + +## Checkpoint + +Before proceeding, confirm with the instructor: + +``` +Based on my research, here's what I understand: + +**Topic:** [Summary] +**Audience:** [Who this is for] +**Duration:** [Expected length] +**Core concepts:** [List] +**Prerequisites:** [List] + +Does this match your vision? Should I proceed to planning? +``` + +## Next Step + +Once approved, proceed to: `02-planning.md` + +For background on Epic Workshop concepts, see: `../01-workshop-overview.md` diff --git a/instructor/workflow/02-planning.md b/instructor/workflow/02-planning.md new file mode 100644 index 0000000..6ffe700 --- /dev/null +++ b/instructor/workflow/02-planning.md @@ -0,0 +1,86 @@ +# Phase 2: Planning + +**Goal:** Design the exercise structure and flow before implementation. + +**Reference:** For planning best practices, see `../02-workshop-planning.md` + +## Step 2.1: Draft Exercise List + +Create a high-level exercise outline: + +```markdown +# Workshop: [Title] + +## Exercise Plan + +### 01. [First Exercise Name] +- Core concept to teach +- Why this comes first + +### 02. [Second Exercise Name] +- Concept this teaches +- How it builds on 01 + +### 03. [Third Exercise Name] +- Concept this teaches +- How it builds on previous +``` + +## Step 2.2: Design Each Exercise + +For each exercise, define the steps: + +```markdown +## Exercise: [Name] + +**Learning Objective:** [One sentence] + +**Concepts Covered:** +- [Concept 1] +- [Concept 2] + +**Steps:** +1. [step-name] - [What this teaches] +2. [step-name] - [What this adds] +3. [step-name] - [What this completes] + +**Key Takeaway:** [Main insight] +``` + +## Step 2.3: Verify Flow + +Check the exercise plan: + +- [ ] Does difficulty progress smoothly? +- [ ] Does each exercise build on previous knowledge? +- [ ] Are there any gaps in coverage? +- [ ] Can each step be completed in 15-30 minutes? +- [ ] Is the total length appropriate for target duration? + +## Workshop Length Guidelines + +| Workshop Type | Exercises | Steps per Exercise | Total Duration | +|--------------|-----------|-------------------|----------------| +| Short | 4-6 | 2-4 | 2-4 hours | +| Standard | 6-10 | 3-5 | 4-8 hours | +| Comprehensive | 8-12 | 4-6 | 8-16 hours | + +## Checkpoint + +Present the plan to the instructor: + +``` +Here's my proposed workshop structure: + +[Full exercise plan with steps] + +Questions: +1. Does this cover what you wanted? +2. Is the order logical? +3. Should anything be added or removed? +4. Is the difficulty progression appropriate? +``` + +## Next Step + +Once the plan is approved, proceed to: `03-setup.md` diff --git a/instructor/workflow/03-setup.md b/instructor/workflow/03-setup.md new file mode 100644 index 0000000..9874dcc --- /dev/null +++ b/instructor/workflow/03-setup.md @@ -0,0 +1,84 @@ +# Phase 3: Setup + +**Goal:** Configure the workshop and create the directory structure. + +**Reference:** For full configuration options, see `../06-package-configuration.md` +**Reference:** For directory structure details, see `../03-directory-structure.md` + +## Step 3.1: Configure package.json + +Update the root `package.json` with workshop metadata: + +```json +{ + "name": "workshop-name", + "private": true, + "epicshop": { + "title": "Workshop Title 🎯", + "subtitle": "Clear description of what learners will learn", + "githubRepo": "https://github.com/org/repo", + "instructor": { + "name": "Instructor Name", + "avatar": "/images/instructor.png", + "𝕏": "handle" + }, + "product": { + "host": "www.epicweb.dev", + "displayName": "EpicWeb.dev", + "displayNameShort": "Epic Web" + } + } +} +``` + +## Step 3.2: Create Directory Structure + +Create directories for each exercise and step from your plan: + +```bash +# Exercise 1 +mkdir -p exercises/01.exercise-name/01.problem.step-name +mkdir -p exercises/01.exercise-name/01.solution.step-name +mkdir -p exercises/01.exercise-name/02.problem.step-name +mkdir -p exercises/01.exercise-name/02.solution.step-name + +# Continue for all exercises and steps... +``` + +### Naming Conventions + +- Exercise directories: `XX.exercise-name` (e.g., `01.hello-world`) +- Step directories: `XX.problem.step-name` and `XX.solution.step-name` +- Use lowercase with hyphens +- Numbers must be zero-padded and sequential + +## Step 3.3: Create Required Files + +Create placeholder README.mdx files: + +```bash +# Workshop level +touch exercises/README.mdx +touch exercises/FINISHED.mdx + +# For each exercise +touch exercises/01.exercise-name/README.mdx +touch exercises/01.exercise-name/FINISHED.mdx +touch exercises/01.exercise-name/01.problem.step-name/README.mdx +touch exercises/01.exercise-name/01.solution.step-name/README.mdx +``` + +## Step 3.4: Verify Structure + +Checklist: + +- [ ] `exercises/README.mdx` exists (workshop intro) +- [ ] `exercises/FINISHED.mdx` exists (workshop wrap-up) +- [ ] `public/images/instructor.png` exists (instructor avatar) +- [ ] Each exercise has `README.mdx` and `FINISHED.mdx` +- [ ] Each step has `README.mdx` in both problem and solution +- [ ] All problem/solution pairs have matching numbers and names + +## Next Step + +Once the structure is created, proceed to: `04-implementation.md` diff --git a/instructor/workflow/04-implementation.md b/instructor/workflow/04-implementation.md new file mode 100644 index 0000000..0dc725c --- /dev/null +++ b/instructor/workflow/04-implementation.md @@ -0,0 +1,171 @@ +# Phase 4: Implementation + +**Goal:** Create the code for each exercise step efficiently with minimal diffs. + +**Reference:** For exercise writing patterns, see `../04-writing-exercises.md` +**Reference:** For emoji usage in code, see `../09-emoji-guide.md` + +## Key Principle: Copy and Modify + +**Don't create exercise files from scratch.** Instead: + +1. Copy the previous step's solution to the new problem +2. Modify only what's needed for the new learning objective +3. This ensures minimal, focused diffs + +This approach: +- Saves tokens (no duplicating boilerplate) +- Ensures consistency between steps +- Keeps diffs focused on learning objectives +- Prevents accidental differences + +## Step 4.1: Create First Exercise Step + +For the **first step** of an exercise: + +1. Create the solution with complete, working code +2. Copy solution to problem directory +3. Remove the parts learners need to implement +4. Add TODO comments with emoji markers + +## Step 4.2: Create Subsequent Steps (Linear Exercises) + +For **subsequent steps** in a linear exercise: + +``` +Previous solution → Copy → New problem → Modify → New solution +``` + +1. **Copy previous solution** to new problem directory +2. **Add new TODOs** for what learners will implement +3. **Copy new problem** to new solution directory +4. **Implement the solution** for this step's objective + +### Example Flow + +``` +01.solution.basic/ → copy to → 02.problem.enhanced/ + ↓ + add TODOs + ↓ +02.problem.enhanced/ → copy to → 02.solution.enhanced/ + ↓ + implement solution +``` + +## Step 4.3: Run the Fix Script + +After creating new directories, run: + +```bash +node ./epicshop/fix.js +``` + +This automatically updates: +- `tsconfig.json` references +- `package.json` name properties + +**Always run this** after creating exercise directories to ensure consistent configuration. This prevents config differences from appearing in diffs. + +## Step 4.4: Format and Lint + +Before verifying diffs, ensure consistent formatting: + +```bash +npm run format +npm run lint -- --fix +``` + +This prevents formatting-only changes from cluttering diffs. + +## Emoji Markers for Problem Code + +Use these emoji characters in TODO comments: + +| Emoji | Character | Purpose | +|-------|-----------|---------| +| 🐨 | Kody | Specific instruction - what to do | +| 💰 | Marty | Hint or code snippet | +| 📜 | Dominic | Link to documentation | +| 🦺 | Lily | TypeScript-specific guidance | +| 💣 | Barry | Code to remove | + +### Problem Code Template + +```tsx +// 🐨 [What to do] +// 💰 [Hint showing how] +// 📜 [Link to documentation] + +function MyComponent() { + // 🐨 Add useState hook here + // 💰 const [state, setState] = useState(initialValue) + + // 🦺 Add type annotation + // @ts-expect-error 💣 Remove when typed + + return ( +
+ {/* 🐨 Render the state here */} +
+ ) +} +``` + +## Step 4.5: Verify the Diff + +Use the epicshop MCP server to check diffs: + +``` +get_diff_between_apps( + workshopDirectory: "/path/to/workshop", + app1: "01.01.problem", + app2: "01.01.solution" +) +``` + +Verify: +- [ ] Only intended changes appear +- [ ] No formatting-only changes +- [ ] Diff is readable (ideally < 30 lines) +- [ ] No config file changes + +**See `07-verification.md` for detailed verification workflow.** + +## Step 4.6: Add Tests (Optional) + +If the exercise should have tests, add them to the solution directory: + +```tsx +expect( + myFunction, + `🚨 Make sure you exported the function as a named export, not default` +).toBeDefined() +``` + +## Non-Linear Exercises + +If exercises are **not linear** (each step is independent): + +1. Create a base template with shared code +2. Copy the template to each problem/solution +3. Customize each step independently + +This is less efficient but necessary when steps don't build on each other. + +## Checkpoint + +After implementing each exercise: + +- [ ] Ran `node ./epicshop/fix.js` +- [ ] Ran `npm run format` +- [ ] Problem → Solution diff is focused +- [ ] Step → Step diff is minimal (for linear) +- [ ] All code runs without errors +- [ ] Tests pass (if applicable) + +## Next Step + +Once code is implemented and verified, proceed to: `05-content.md` + +For detailed verification workflow, see: `07-verification.md` diff --git a/instructor/workflow/05-content.md b/instructor/workflow/05-content.md new file mode 100644 index 0000000..9e9357d --- /dev/null +++ b/instructor/workflow/05-content.md @@ -0,0 +1,139 @@ +# Phase 5: Content + +**Goal:** Write the README.mdx files for all exercises and steps. + +**Reference:** For MDX formatting and components, see `../05-mdx-and-content.md` +**Reference:** For emoji character usage, see `../09-emoji-guide.md` + +## Emoji Characters for Content + +| Emoji | Character | Use for | +|-------|-----------|---------| +| 👨‍💼 | Peter | Context, requirements, celebrating completion | +| 🐨 | Kody | Specific instructions | +| 🧝‍♀️ | Kellie | Explaining work done or upcoming (with diff links) | +| 🦺 | Lily | TypeScript guidance | +| 💰 | Marty | Tips and hints | +| 📜 | Dominic | Documentation links | +| 📝 | Nancy | Note-taking prompts | +| 🦉 | Olivia | Best practices | + +### Using Kellie with Diff Links + +Kellie (🧝‍♀️) explains work that was done between steps or will be done next: + +**Explaining past work** (learner hasn't seen it): +```mdx +🧝‍♀️ I've already set up the database connection and created the schema. +Check what I did +``` + +**Explaining upcoming work** (repetitive/optional practice): +```mdx +🧝‍♀️ In the next step, I'll apply validation to the remaining fields. +If you'd like more practice, try doing it yourself first! +See the upcoming changes +``` + +Use Kellie when: +- Setup work was done that learners didn't see +- Repetitive work is coming that learners can skip or try themselves +- Boilerplate needs explanation but isn't the learning focus + +## Step 5.1: Workshop Introduction (exercises/README.mdx) + +```mdx +# Workshop Title + +👨‍💼 [Greeting and introduction to the workshop] + +[Explain what learners will learn - use a list] + +[Set expectations and prerequisites] + +[Encouraging call to action to start] +``` + +## Step 5.2: Exercise Introduction (exercises/XX.exercise-name/README.mdx) + +```mdx +# Exercise Title + +[Explain the concept being taught - 2-4 paragraphs] + +[Show code examples if helpful] + +[Use callouts for important points] + +[Preview what learner will do] +``` + +## Step 5.3: Problem Instructions (XX.problem.step-name/README.mdx) + +```mdx +# Step Title + +👨‍💼 [Set the scene - what's the context/requirement] + +🧝‍♀️ [What's already been set up, if applicable] + +🐨 [Specific instructions - tell them exactly what to do] + +[If needed, numbered list of subtasks] + +💰 [Provide helpful hints] + +📜 [Link to relevant documentation] +``` + +## Step 5.4: Solution Explanation (XX.solution.step-name/README.mdx) + +```mdx +# Step Title + +👨‍💼 [Brief confirmation of what was accomplished] + +🦉 [Optional: Key insight or best practice] + +[Optional: Transition to what's next] +``` + +## Step 5.5: Exercise Summary (exercises/XX.exercise-name/FINISHED.mdx) + +```mdx +# Exercise Title + +👨‍💼 [Celebrate completion] + +📝 [Encourage notes on key takeaways] + +[How this connects to the next exercise or real-world use] +``` + +## Step 5.6: Workshop Wrap-up (exercises/FINISHED.mdx) + +```mdx +# Workshop Title + +👨‍💼 [Celebrate completion of the entire workshop] + +[Summary of the journey] + +[What they can do now] + +[Call to action - what to learn next or how to practice] +``` + +## Content Checklist + +For each README.mdx: + +- [ ] Uses appropriate emoji characters +- [ ] Instructions are specific and actionable +- [ ] Code examples are correct +- [ ] Links are valid +- [ ] Follows the template for its type + +## Next Step + +Once all content is written, proceed to: `06-validation.md` diff --git a/instructor/workflow/06-validation.md b/instructor/workflow/06-validation.md new file mode 100644 index 0000000..ada35be --- /dev/null +++ b/instructor/workflow/06-validation.md @@ -0,0 +1,176 @@ +# Phase 6: Validation + +**Goal:** Final testing and verification of the complete workshop. + +**Reference:** For testing details, see `../07-testing-and-validation.md` +**Reference:** For ongoing verification, see `07-verification.md` + +## Pre-Validation: Ensure Clean State + +Before final validation, ensure all fixes have been applied: + +```bash +# Auto-fix configs +node ./epicshop/fix.js + +# Format all files +npm run format + +# Fix lint issues +npm run lint -- --fix +``` + +## Step 6.1: Structural Validation + +Verify the directory structure: + +- [ ] All directories follow naming conventions (`XX.name`) +- [ ] All problem/solution pairs have matching numbers and names +- [ ] All README.mdx files exist +- [ ] All FINISHED.mdx files exist +- [ ] No missing or extra files + +Use the MCP server to get an overview: + +``` +get_workshop_context(workshopDirectory: "/path/to/workshop") +``` + +## Step 6.2: Diff Validation + +For each problem/solution pair, verify focused diffs: + +``` +get_diff_between_apps( + workshopDirectory: "/path/to/workshop", + app1: "01.01.problem", + app2: "01.01.solution" +) +``` + +Check: +- [ ] Only intended changes appear +- [ ] No formatting differences +- [ ] No config file changes +- [ ] Diff is readable (< 30 lines ideal) + +For linear exercises, also check step-to-step diffs: + +``` +get_diff_between_apps( + app1: "01.01.solution", + app2: "01.02.problem" +) +``` + +## Step 6.3: Code Validation + +Run validation commands: + +```bash +npm run typecheck +npm run lint +npm test # if tests exist +``` + +For each step verify: +- [ ] Problem code runs without crashing +- [ ] Solution code runs without errors +- [ ] Tests pass (if tests exist) + +## Step 6.4: Content Validation + +For each README.mdx: + +- [ ] Uses correct emoji characters +- [ ] Instructions are clear and complete +- [ ] Code examples are correct +- [ ] File references are valid + +Use MCP to review content: + +``` +get_exercise_context( + workshopDirectory: "/path/to/workshop", + exerciseNumber: 1 +) +``` + +## Step 6.5: Run the Workshop + +Start the workshop app and walk through as a learner: + +```bash +npm run dev +``` + +1. Navigate through all exercises +2. Attempt each problem WITHOUT looking at the solution +3. Use the diff tab to compare your work +4. Run tests if available +5. Verify everything functions correctly + +This helps identify: +- Unclear instructions +- Missing context +- Difficulty spikes +- Broken code + +## Final Checklist + +### Structure +- [ ] All directories correctly named +- [ ] All required files present +- [ ] package.json properly configured + +### Code +- [ ] All problem code runs +- [ ] All solution code runs +- [ ] All tests pass +- [ ] No TypeScript errors +- [ ] No linting errors + +### Diffs +- [ ] All problem→solution diffs are focused +- [ ] All step→step diffs are minimal +- [ ] No formatting-only changes +- [ ] No config changes in diffs + +### Content +- [ ] All READMEs written +- [ ] Emoji characters used correctly +- [ ] Instructions are clear +- [ ] Links work + +### Experience +- [ ] Difficulty progresses smoothly +- [ ] Each step is completable in 15-30 min +- [ ] Workshop can be completed in target time + +## Completion + +Once validation passes, report to the instructor: + +``` +The workshop is complete and validated: + +- [X] exercises with [Y] total steps +- All code runs without errors +- All tests pass +- All diffs are focused and minimal +- Walked through as a learner - everything works + +Ready for review! +``` + +## Common Issues and Solutions + +| Issue | Solution | +|-------|----------| +| Formatting in diffs | Run `npm run format` on all files | +| Config files in diffs | Run `node ./epicshop/fix.js` | +| Exercise too long | Split into multiple exercises or reduce steps | +| Diff too large | Break step into smaller steps | +| Instructions unclear | Add more guidance, hints, and examples | +| Code doesn't work | Test solution first, then create problem from it | +| Difficulty spike | Add intermediate step or move content later | diff --git a/instructor/workflow/07-verification.md b/instructor/workflow/07-verification.md new file mode 100644 index 0000000..d6b13ee --- /dev/null +++ b/instructor/workflow/07-verification.md @@ -0,0 +1,197 @@ +# Verification Guide + +**Goal:** Verify your work as you go to catch issues early and ensure minimal diffs between steps. + +## Key Principle: Minimal Diffs + +A core goal of Epic Workshops is **minimal, focused diffs** between problem and solution. Learners should see only the changes relevant to the learning objective—nothing extra. + +## Helper Scripts + +Always run these after creating/modifying exercises: + +```bash +# Auto-fix tsconfig.json and package.json name properties +node ./epicshop/fix.js + +# Format all files consistently +npm run format + +# Fix lint issues +npm run lint -- --fix +``` + +Run these **before** checking diffs to prevent formatting/config noise. + +## Using the epicshop MCP Server + +The epicshop MCP server provides tools to verify your work. These are the most useful for workshop creation: + +### `get_diff_between_apps` + +**Primary verification tool.** Shows the git diff between any two apps. + +``` +App ID format: {exerciseNumber}.{stepNumber}.{type} +Examples: 01.01.problem, 01.01.solution, 02.03.problem +``` + +Use this to verify: +- Diff between problem and solution is focused +- Diff between consecutive steps is minimal +- No unintended changes crept in + +**Example usage:** +``` +get_diff_between_apps( + workshopDirectory: "/path/to/workshop", + app1: "01.02.problem", + app2: "01.02.solution" +) +``` + +### `get_workshop_context` + +Get an overview of all exercises and steps. Useful for verifying the structure matches your plan. + +``` +get_workshop_context(workshopDirectory: "/path/to/workshop") +``` + +### `get_exercise_context` + +Get detailed context for a specific exercise including all step instructions. Verify your README content is complete. + +``` +get_exercise_context( + workshopDirectory: "/path/to/workshop", + exerciseNumber: 1 +) +``` + +### `get_exercise_step_context` + +Get instructions for a specific step. Useful for reviewing individual problem/solution content. + +``` +get_exercise_step_context( + workshopDirectory: "/path/to/workshop", + exerciseNumber: 1, + stepNumber: 2 +) +``` + +## Verification Checklist + +After creating each exercise step: + +### 1. Check the Problem → Solution Diff + +``` +get_diff_between_apps(app1: "XX.YY.problem", app2: "XX.YY.solution") +``` + +Verify: +- [ ] Only intended changes appear +- [ ] No formatting-only changes (run formatter first) +- [ ] No unrelated file changes +- [ ] Diff is readable (ideally < 30 lines of actual changes) + +### 2. Check Step → Step Diff (for linear exercises) + +``` +get_diff_between_apps(app1: "XX.01.solution", app2: "XX.02.problem") +``` + +Verify: +- [ ] Minimal changes between steps +- [ ] Only new TODO comments and setup for next task +- [ ] Previous solution code is preserved + +### 3. Run Formatting and Linting + +Before checking diffs, ensure consistent formatting: + +```bash +npm run format +npm run lint -- --fix +``` + +This prevents formatting differences from cluttering diffs. + +### 4. Run the Fix Script + +The `epicshop/fix.js` script auto-updates: +- `tsconfig.json` references +- `package.json` name properties + +```bash +node ./epicshop/fix.js +``` + +Run this after creating new exercise directories to ensure consistent configuration. + +### 5. Verify Code Runs + +For each step: +- [ ] Problem code runs without crashing +- [ ] Solution code runs without errors +- [ ] Tests pass (if applicable) + +```bash +npm run typecheck +npm run lint +npm test # if tests exist +``` + +## Common Diff Problems + +### Problem: Formatting differences in diff + +**Cause:** Inconsistent formatting between problem and solution. + +**Fix:** Run `npm run format` on both before comparing. + +### Problem: Config file changes in diff + +**Cause:** Different tsconfig or package.json settings. + +**Fix:** Run `node ./epicshop/fix.js` to normalize configs. + +### Problem: Too many files changed + +**Cause:** Created files from scratch instead of copying. + +**Fix:** Copy from previous step and modify only what's needed. + +### Problem: Unrelated code changes + +**Cause:** "Improving" code while creating the step. + +**Fix:** Only change what the learning objective requires. Save improvements for a dedicated refactoring step if needed. + +## Verification Workflow + +For each exercise step: + +1. **Create the step** (copy from previous, modify) +2. **Run fix script** (`node ./epicshop/fix.js`) +3. **Run formatter** (`npm run format`) +4. **Check diff** (`get_diff_between_apps`) +5. **Fix any issues** found in diff +6. **Verify code runs** (typecheck, lint, test) +7. **Move to next step** + +## Using Diffs to Improve + +If a diff is too large: + +1. **Split the step** - Break into multiple smaller steps +2. **Move setup earlier** - Put boilerplate in a previous step +3. **Use Kellie** - Have "co-worker" do setup work that learners don't need to see + +If a diff has unrelated changes: + +1. **Revert unrelated changes** - Keep focus on the objective +2. **Create separate step** - If changes are valuable, make them their own step +3. **Check your source** - Make sure you copied from the right previous step diff --git a/instructor/workflow/readme.md b/instructor/workflow/readme.md new file mode 100644 index 0000000..1bd3cba --- /dev/null +++ b/instructor/workflow/readme.md @@ -0,0 +1,70 @@ +# Epic Workshop Creation Workflow + +This directory contains a step-by-step workflow for AI agents creating Epic Workshops. Each phase is in its own file to allow progressive context loading. + +## Workflow Phases + +Follow these phases in order: + +| Phase | File | Purpose | +|-------|------|---------| +| 1 | `01-discovery.md` | Understand the topic and scope | +| 2 | `02-planning.md` | Design the exercise structure | +| 3 | `03-setup.md` | Configure the workshop | +| 4 | `04-implementation.md` | Create exercise code | +| 5 | `05-content.md` | Write README.mdx files | +| 6 | `06-validation.md` | Final testing and verification | + +## Supporting Guides + +| File | Purpose | +|------|---------| +| `07-verification.md` | Verify work as you go (diffs, MCP tools) | + +## How to Use + +1. Start with `01-discovery.md` +2. Complete each phase before moving to the next +3. Each file references the relevant documentation you need +4. **Use `07-verification.md` throughout** to verify diffs +5. Get instructor approval at key checkpoints + +## Quick Start + +Tell the agent: + +> "I want to create an Epic Workshop about [TOPIC]. Please read `/instructor/workflow/01-discovery.md` and begin the discovery phase." + +The agent will progressively load additional context as needed. + +## Key Efficiency Tips + +### Copy, Don't Create + +For linear exercises, **copy the previous step and modify** rather than creating files from scratch. This: +- Saves tokens +- Ensures minimal diffs +- Prevents accidental differences + +### Use the Fix Script + +After creating directories, run: + +```bash +node ./epicshop/fix.js +``` + +This auto-updates tsconfig and package.json files. + +### Verify Diffs Early + +Use `get_diff_between_apps` from the epicshop MCP server to check diffs after each step. Fix issues before moving on. + +### Format Before Comparing + +Always run formatting before checking diffs: + +```bash +npm run format +npm run lint -- --fix +```