Passion Empathy Taste 5–7 hours Skills Workshop · 02 of 03

A Multi-Skill Pack for Someone You Love

Three Skills that call each other, share memory across a session, and are all designed for one specific person you can name. The hard part isn't the code. The hard part is choosing what should be one Skill, what should be three, and what each one should refuse.

Project 01 was about depth in a domain. Project 02 is about orchestration — making three Skills cooperate well enough that the user doesn't notice there are three. The hard part isn't the code. The hard part is choosing the seams.

This is also the project where empathy enters Skills Workshop. The user is one named person — not "users" — and you'll know you've succeeded when you can say "I built this because I wanted to make Wednesday afternoons easier for Aunt Mei."

Why three Skills?

Because real software has shape. One huge Skill that does everything is unmaintainable, untestable, and impossible to reason about. Three Skills with clean boundaries are how a builder actually thinks. You're not learning Skills — you're learning systems.

Step by step

  1. Pick the person. Name them. Watch them.

    Pick one specific person — a grandparent, a sibling, an aunt, a teacher. Watch what they actually struggle with for a few days. Take notes. You're looking for the gap between what they need and what current tools give them. Example: Mei is 78. She loves cooking. The recipes she learned from her mother were never written down. She gives up on apps after two attempts. That's the brief.

  2. Decompose into three Skills with clean seams.

    Not three random Skills. Three Skills with one clear boundary each. The trick is finding seams a beginner wouldn't see. The decomposition rule: if you can't say in one sentence what each Skill does and doesn't do, you haven't decomposed yet — you've drawn lines around a single big Skill.

  3. Design the shared memory schema first.

    Before any Skill code: write the JSON schema all three Skills will read and write. Make it explicit. Version it. Treat it like a database schema — because that's exactly what it is. Without this step, your three Skills will silently disagree on the shape of the data, and the bugs will be impossible to track down.

  4. Build the Skills in order: write → check → coach.

    Build them in the order data flows through them. recipe-recall writes new recipes. ingredient-checker reads recipes and returns shopping lists. kitchen-coach reads recipes and walks Mei through them. Build write before read; you can't test reads against an empty store.

  5. Wire up the orchestration handoffs.

    The pack feels like one tool only if the handoffs between Skills feel natural. recipe-recall, when finished, writes to memory and tells the user "I've saved it; would you like a shopping list?" — which triggers ingredient-checker. Design the handoff language as carefully as the Skills themselves. "I've saved it. Would you like a shopping list?" is a designed sentence.

  6. Run a real session with the named person.

    Sit with the actual person. Not your imagination of them. Watch what they actually do. Note three things you guessed wrong. (You will guess wrong about three things.) Edit the Skills. You're done when she finishes a recipe end-to-end without asking you for help.

A complete worked example, every file

Mei's recipe pack — every file, ready to copy. Three Skills, one shared schema, two example sessions.

mei-pack/brief.md · the design doc that came before any Skill
## the user

name: Aunt Mei
age: 78
languages: native Cantonese, basic English reading, no typing
context: lives alone, cooks daily, has an iPad she rarely opens
the pain (her words): "我记得我妈妈做的菜,但是没有写下来,
                       现在做的时候总是忘记顺序。"
                       (I remember my mother's dishes, but I never
                        wrote them down, and now I forget the order.)

## what other apps fail at, that I observed

- All recipe apps assume the user knows the dish name first.
  Mei doesn't. She remembers the smell, the season, a feeling.
- All apps default to 16pt text. She can't read it without glasses
  she keeps losing.
- All apps assume the user can scroll. She can't — she taps too hard
  and triggers links.
- All apps want her to "create an account". She won't.

## the design constraints (non-negotiable)

- 28pt minimum text
- read-aloud on every screen, calm female voice, slow speed
- no scrolling — one screen at a time, with a "next" button big
  enough for one finger
- no accounts, no login, no sync
- never panic her. no pop-ups. no loading spinners she might mistake
  for an error.

## the three Skills

  recipe-recall      → asks her one question at a time, builds up
                       a recipe over 5–7 turns, saves in her own words
  ingredient-checker → reads a saved recipe, lists ingredients with
                       Cantonese first, suggests substitutions
  kitchen-coach      → reads any saved recipe back, one step at a
                       time, with read-aloud + confirm-to-advance
mei-pack/memory.schema.json · the shared contract
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "mei.recipes shared memory v1.0",
  "type": "object",
  "required": ["version", "preferences", "recipes"],
  "properties": {
    "version": { "const": "1.0" },
    "preferences": {
      "type": "object",
      "required": ["font_size_pt", "language_ratio", "voice", "speed"],
      "properties": {
        "font_size_pt": { "type": "integer", "minimum": 24, "maximum": 48 },
        "language_ratio": {
          "type": "object",
          "properties": {
            "zh": { "type": "number", "minimum": 0, "maximum": 1 },
            "en": { "type": "number", "minimum": 0, "maximum": 1 }
          }
        },
        "voice": { "enum": ["calm female", "calm male"] },
        "speed": { "enum": ["slow", "normal"] }
      }
    },
    "recipes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "saved_at", "in_her_words", "ingredients", "steps"],
        "properties": {
          "id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "saved_at": { "type": "string", "format": "date" },
          "in_her_words": { "type": "string", "minLength": 10 },
          "ingredients": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["item_zh", "qty"],
              "properties": {
                "item_zh": { "type": "string" },
                "item_en": { "type": "string" },
                "qty":     { "type": "string" },
                "substitute_zh": { "type": "string" }
              }
            }
          },
          "steps": {
            "type": "array",
            "minItems": 2,
            "items": { "type": "string" }
          }
        }
      }
    }
  }
}
recipe-recall/SKILL.md
---
name: recipe-recall
version: 0.1
description: |
  Asks Aunt Mei one question at a time about a dish she remembers
  from childhood. Builds the recipe over 5–7 turns. Writes it into
  shared memory in her own Cantonese words.

when_to_use: |
  Triggered when Mei says she "想做" (wants to make) a dish she
  hasn't yet recorded, or "想起来" (just remembered) one. Refuses
  to recall a recipe she's already saved (suggests kitchen-coach).

response_style: |
  ONE question per turn. Always Cantonese-first. No more than 18 words
  per question. Never assume the next ingredient — always ask.
  Always confirm before writing to memory: "我这样写下来对吗?"

does:
  - ask single questions in Mei's pace
  - write the saved recipe to mei.recipes
  - hand off to ingredient-checker when done

does_not:
  - skip ahead
  - guess ingredients she didn't name
  - rewrite recipes that already exist (refuses, redirects to coach)
  - use English unless she does first

writes_to: mei.recipes
hands_off_to:
  - condition: "recipe is saved AND user hasn't said no"
    target: ingredient-checker
    prompt_template: "我帮你把方子存好了。要我帮你列张买菜清单吗?"
ingredient-checker/SKILL.md
---
name: ingredient-checker
version: 0.1
description: |
  Given a saved recipe, lists the ingredients Mei will need, with
  Cantonese first, English second. Suggests substitutions for
  hard-to-find items. Writes nothing to recipes — read-only.

when_to_use: |
  Triggered after recipe-recall completes, OR when Mei opens an
  existing recipe and asks "I need to buy what?". Refuses if
  asked to MODIFY a recipe (that's recall's job, not checker's).

response_style: |
  Bilingual list, Cantonese first, English in parentheses. Quantity
  in the unit Mei understood it (catty, half a bowl, "a handful").
  When suggesting a substitute, explain why in one short sentence.

does:
  - read mei.recipes
  - format ingredient lists bilingually
  - suggest substitutes for items hard to find in Mei's neighborhood
  - hand off to kitchen-coach when she says she's ready to cook

does_not:
  - add or change steps in any recipe (refuses → recall)
  - write to mei.recipes (read-only by design)
  - assume which substitute she'd want (always asks)

reads_from: mei.recipes
writes_to: (none — read-only)
hands_off_to:
  - condition: "user says ready to cook OR uses 'now' / '现在'"
    target: kitchen-coach
kitchen-coach/SKILL.md
---
name: kitchen-coach
version: 0.1
description: |
  Reads any saved recipe back to Mei, one step at a time, with
  large text and read-aloud. Pauses for confirmation between steps.
  Refuses to skip ahead — pacing is the whole feature.

when_to_use: |
  Triggered when Mei says she's "now" cooking, OR opens a recipe
  with the kitchen-coach button. Refuses if no recipe exists in
  memory (redirects to recall).

response_style: |
  ONE step per screen. 28pt minimum. Read-aloud automatically on
  show. After speaking the step: "準備好了告訴我,我等你。" Wait.
  Never play more than one step until the user confirms.

does:
  - read steps from mei.recipes
  - speak them with calm female voice, slow speed
  - wait for confirmation between steps
  - allow the user to say "再说一次" / "say it again"

does_not:
  - skip ahead even if asked (politely refuses)
  - reorder steps
  - speed up
  - quit if interrupted (resumes from the step it last spoke)

reads_from: mei.recipes
writes_to: (none — read-only)
respects: mei.preferences (font_size_pt, voice, speed)
mei-pack/orchestration.yaml · how the three coordinate
## handoff graph

flow:
  - from: (entry)
    to: recipe-recall
    when: "user mentions a dish not yet in mei.recipes"

  - from: (entry)
    to: ingredient-checker
    when: "user asks 'I need what to buy?' AND a recipe exists"

  - from: (entry)
    to: kitchen-coach
    when: "user says she is cooking now AND a recipe exists"

  - from: recipe-recall
    to: ingredient-checker
    when: "recipe-recall completed AND user did not decline"
    bridge: "我帮你把方子存好了。要我帮你列張買菜清單嗎?"

  - from: ingredient-checker
    to: kitchen-coach
    when: "user says 'ready' / '現在' / 'I'm cooking'"
    bridge: "好。我帮你一步一步念出来。準備好了告訴我。"

  - from: kitchen-coach
    to: (end)
    when: "all steps confirmed OR user says 'done'"

## refusals

refuse_routes:
  - if: "kitchen-coach asked but no recipe exists"
    redirect_to: recipe-recall
    message: "我们還沒有寫下這個菜的方子。要我先問你,再幫你寫好嗎?"

  - if: "ingredient-checker asked to MODIFY a recipe"
    redirect_to: recipe-recall
    message: "我只能讀方子,不能改。要找方子的人是 recipe-recall。"

## shared memory contract

writers: [recipe-recall]
readers: [recipe-recall, ingredient-checker, kitchen-coach]
schema:  memory.schema.json (v1.0)

Live demo 1: watch the three Skills hand off

A simulated session. Click "next turn" to advance the conversation and watch shared memory update on the right.

Mei's recipe pack · simulated session


    

  

Live demo 2: does your shared schema actually hold up?

Paste a JSON record into the validator. The check runs against Mei's memory.schema.json v1.0 and tells you exactly which field is wrong. This is the most boring widget on the site, and the most useful — schema bugs are 70% of multi-Skill failures.

JSON schema validator

Live demo 3: does your handoff feel like one experience?

The seam between Skills shows up in language. A bad handoff: "Skill A has finished. Switching to Skill B." A good handoff: "我帮你把方子存好了。要我列張買菜清單嗎?" The user shouldn't notice there's a switch. Type your handoff sentence — the checker flags machine-speak.

Handoff-language checker

What makes this hard

The hardest part is not three Skills. It's three Skills that feel like one experience. The seams will show if you don't think about handoffs explicitly. The user will feel a "switch" between Skills — and the switch will break the trust the pack has earned. The cure is to design the handoff language as carefully as the Skills themselves. "I've saved it. Would you like a shopping list?" is a designed sentence. So is the next one.

The second hardest part is restraint. You will be tempted to add a fourth Skill, a fifth feature. Don't. Three Skills, used by Mei, beats six Skills, used by no-one.

Self-check before you ship v0.1

  • I can name the user out loud and describe one specific thing they struggle with.
  • The brief.md exists and includes 4+ design constraints I observed (not invented).
  • The three Skills have clean seams — what each does AND does not do, in one sentence each.
  • The shared memory schema is explicit, versioned, and validates clean against the validator above.
  • Each handoff sentence passes the handoff-language checker.
  • I sat with the real person and ran a session. I noticed three things I had guessed wrong.
  • The pack feels like one tool. The handoffs use language I designed.

Push further · for the harder end of 15+

Three Skills that cooperate is the minimum. Here's where it stops feeling like a school project.

  1. Add an undo Skill as the fourth. Mei makes mistakes. She says "no, that wasn't right" after recipe-recall has already saved a turn. Right now, there's no way back. Add a fourth Skill, recipe-undo, that snapshots before each write and restores on command. Watch how this changes the orchestration. Hint: it has to come before any writer, not after.
  2. Build the conflict-resolution layer. Two Skills now want to read or write the same field at the same time. Design the locking strategy — optimistic? Last-writer-wins? Append-only log + replay? Pick one, write down why, document the failure modes. Real production multi-agent systems live or die on this question.
  3. Internationalize the schema, not just the surface. Mei's preferences include a language_ratio field. Right now the Skill responses respect it on output. But what about input? Mei sometimes types in pinyin, sometimes Cantonese characters, sometimes a mix with English brand names. Add an input_normalizer stage so all three Skills see the same canonical form regardless of how Mei wrote it. Bonus: write the normalizer so it could be swapped for any other language pair without changing the three Skills.