The skill arc
Every kid-facing thing in Sprout runs through a skill. Three beats: author, activate, deliver.
Every kid-facing thing in Sprout runs through a skill. Three beats: author, activate, deliver. Do not conflate them.
Three beats, distinct
Author writes the skill. Activate flips it live (records intent). Deliver makes the kid see it. Each beat is a different call. None of them imply the next.
| Beat | Call | Kid sees? |
|---|---|---|
| 1. Author | canvas.create then skill.write | No |
| 2. Activate | skill.activate | No (records intent) |
| 3. Deliver | task.create or routine.create | Yes (finally) |
The one rule
A skill is the first-class, trackable unit of agent behavior: the thing that is authored, activated, audited, and (later) shared. A kid never sees a raw canvas; they only ever see the output of an activated, delivered skill.
Artifact vs. vehicle
A canvas (also program, itinerary) is an artifact. It is inert on its own. canvas.create persists it but delivers nothing: a fresh canvas reaches no kid.
A skill is the vehicle. It links the artifact and is what gets delivered. One skill can both author/modify artifacts and assign them; these are non-exclusive jobs. You do not need a separate skill per job.
The three beats: do not conflate
1. Author
skill.write (or skill.update addCanvasIds) creates the skill and links the canvas(es). Author the canvas first via canvas.create (read sprout://canvas/sdk before writing canvas HTML).
2. Activate
skill.activate {skillId} flips the skill live for the family. This records INTENT. It is idempotent: re-activating an already-active skill is a safe no-op (status: 'already_active'). Do not treat that as an error and do not loop. Activation is NOT delivery: after this the kid still sees nothing.
3. Deliver
The only beat the kid actually sees. Do exactly one of:
task.createwithsourceSkillId: a one-off (or scheduled) task the kid sees. Put the matchingcanvasIdon each canvas-bearing step.routine.createwith theskillId: a recurring cadence the kid sees.
skill.invoke is NOT kid delivery: it only renders the skill body into the agent conversation. The kid never sees an invoke.
kid-callable does not mean surfaced
kidCallable: true means the kid app can launch the skill; it does NOT mean the kid has been given it. An active, kid-callable skill with no task and no routine is still invisible to the kid. Do not report an activity as live / assigned / "the kid can play it" until the delivering task or routine exists.
Reading the nextStep copy
Tool responses and invariant errors are directional: they name the exact next call. canvas.create returns a nextStep; skill.activate returns a nextStep; a canvas-bearing task.create against an inactive or unlinked skill returns a BAD_INPUT whose message names the fix. Follow them literally rather than guessing.
Interaction discipline: checkpoint, do not barrel
You are collaborating with a parent, not executing a batch job. Every step here mutates the family's world; the parent stays in the loop. Pause and confirm before:
- Authoring the skill: once the canvas exists, offer the preview and confirm the design.
- Delivery:
task.create/routine.createis the consequential, kid-facing step. Confirm shape first: one-off vs recurring, schedule, gems, which kid(s).
The only time you may barrel the whole pipeline is when the parent's request makes fully-autonomous execution UNAMBIGUOUS ("just build it and assign it", "YOLO it"). A bare "make a math game for Jay" is NOT that signal.
Recovery / partial state
Execution is best-effort; partial state is normal. The idempotency story differs per step:
- Authoring:
canvas_create/skill_writeusedryRun+specHash. A repeated commit either echoes the same id or rejects on hash mismatch. - Activation:
skill_activatereturnsalready_activerather than failing, so re-running is a safe no-op. - Delivery:
task_create/routine_createdo not yet auto-dedupe via the skill_id-entity backstop (that linkage on invoke is a roadmap item). Your agent should check state withtask_list/routine_listbefore retrying.
Recovery is re-invocation that self-heals at the authoring/activation layers and agent-checked at the delivery layer. Never manual rollback.
Naming heads-up: activated vs triggered
The shipped enum value is activated. The honest meaning is closer to triggered: the flip fires, the execution that follows is best-effort. A rename to triggered plus a companion retry_friendly advisory (read by the agent before manual re-trigger; not a runtime lock) is in flight. Docs will track when it lands.
Try it
# Canonical sequence (canvas-backed activity)
canvas_create({name: "...", html: "<...>", dryRun: true, ...})
# returns {specHash}
canvas_create({..., dryRun: false, specHash: "<echo>"})
# returns {canvasId}
skill_write({canvasIds: ["<canvasId>"], kidCallable: true, ageRange: "10-12", ...})
# returns {skillId}
skill_activate({skillId: "<skillId>"})
# kid still sees nothing
task_create({
sourceSkillId: "<skillId>",
steps: [{canvasId: "<canvasId>", ...}],
...
})
# kid sees the card on next iPad sync