@elevasis/sdk 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/cli.cjs +2325 -124
  2. package/dist/index.d.ts +882 -794
  3. package/dist/index.js +170 -46
  4. package/dist/node/index.d.ts +69 -0
  5. package/dist/node/index.js +273 -0
  6. package/dist/test-utils/index.d.ts +857 -711
  7. package/dist/test-utils/index.js +2 -0
  8. package/dist/types/worker/adapters/lead.d.ts +1 -1
  9. package/dist/types/worker/platform.d.ts +2 -9
  10. package/dist/worker/index.js +1 -0
  11. package/package.json +12 -3
  12. package/reference/_navigation.md +23 -1
  13. package/reference/_reference-manifest.json +98 -0
  14. package/reference/claude-config/rules/agent-start-here.md +13 -0
  15. package/reference/claude-config/rules/organization-model.md +40 -40
  16. package/reference/claude-config/rules/organization-os.md +16 -16
  17. package/reference/claude-config/rules/ui.md +2 -6
  18. package/reference/claude-config/rules/vibe.md +13 -13
  19. package/reference/claude-config/skills/knowledge/SKILL.md +253 -0
  20. package/reference/claude-config/skills/{configure → knowledge}/operations/codify-level-a.md +100 -100
  21. package/reference/claude-config/skills/{configure → knowledge}/operations/codify-level-b.md +158 -158
  22. package/reference/claude-config/skills/knowledge/operations/customers.md +109 -0
  23. package/reference/claude-config/skills/knowledge/operations/features.md +113 -0
  24. package/reference/claude-config/skills/knowledge/operations/goals.md +118 -0
  25. package/reference/claude-config/skills/knowledge/operations/identity.md +93 -0
  26. package/reference/claude-config/skills/knowledge/operations/labels.md +89 -0
  27. package/reference/claude-config/skills/knowledge/operations/offerings.md +109 -0
  28. package/reference/claude-config/skills/knowledge/operations/roles.md +99 -0
  29. package/reference/claude-config/skills/knowledge/operations/techStack.md +102 -0
  30. package/reference/claude-config/skills/run-ui/SKILL.md +73 -0
  31. package/reference/claude-config/skills/setup/SKILL.md +270 -270
  32. package/reference/claude-config/skills/tutorial/SKILL.md +249 -0
  33. package/reference/claude-config/skills/tutorial/progress-template.md +74 -0
  34. package/reference/claude-config/skills/tutorial/technical.md +1309 -0
  35. package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -0
  36. package/reference/claude-config/sync-notes/2026-05-02-crm-ownership-next-action.md +58 -0
  37. package/reference/claude-config/sync-notes/2026-05-02-template-hardcode-workos-config.md +56 -0
  38. package/reference/claude-config/sync-notes/2026-05-04-elevasis-workspace.md +71 -0
  39. package/reference/claude-config/sync-notes/2026-05-04-template-skills-run-ui-and-tutorial.md +59 -0
  40. package/reference/deployment/index.mdx +5 -5
  41. package/reference/examples/organization-model.ts +40 -0
  42. package/reference/framework/index.mdx +1 -1
  43. package/reference/framework/tutorial-system.mdx +86 -173
  44. package/reference/packages/core/src/knowledge/README.md +32 -0
  45. package/reference/packages/ui/src/knowledge/README.md +31 -0
  46. package/reference/packages/ui/src/theme/presets/README.md +19 -0
  47. package/reference/scaffold/core/organization-model.mdx +1 -1
  48. package/reference/scaffold/recipes/add-a-feature.md +1 -1
  49. package/reference/scaffold/recipes/customize-crm-actions.md +3 -3
  50. package/reference/scaffold/recipes/customize-organization-model.md +3 -3
  51. package/reference/scaffold/recipes/extend-crm.md +12 -8
  52. package/reference/scaffold/recipes/extend-lead-gen.md +129 -20
  53. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +1 -1
  54. package/reference/scaffold/recipes/index.md +6 -0
  55. package/reference/scaffold/reference/contracts.md +829 -595
  56. package/reference/scaffold/reference/feature-registry.md +2 -1
  57. package/reference/scaffold/ui/composition-extensibility.mdx +17 -0
  58. package/reference/claude-config/skills/configure/SKILL.md +0 -98
  59. package/reference/claude-config/skills/configure/operations/customers.md +0 -150
  60. package/reference/claude-config/skills/configure/operations/features.md +0 -162
  61. package/reference/claude-config/skills/configure/operations/goals.md +0 -147
  62. package/reference/claude-config/skills/configure/operations/identity.md +0 -133
  63. package/reference/claude-config/skills/configure/operations/labels.md +0 -128
  64. package/reference/claude-config/skills/configure/operations/offerings.md +0 -159
  65. package/reference/claude-config/skills/configure/operations/roles.md +0 -153
  66. package/reference/claude-config/skills/configure/operations/techStack.md +0 -139
@@ -0,0 +1,1309 @@
1
+ # Tutorial: Technical Track
2
+
3
+ This file is loaded by `.claude/skills/tutorial/SKILL.md` when the user selects the technical
4
+ track. Do not invoke this file directly -- the router reads it and dispatches to the lesson the
5
+ user picks.
6
+
7
+ Each lesson is a script for the teaching agent. Instructions in plain text address the agent
8
+ ("Read X", "Walk the user through Y"). User-facing strings are indented as block quotes.
9
+
10
+ After each lesson, update `.claude/memory/tutorial-progress.md` by changing `[ ]` to
11
+ `[x] YYYY-MM-DD` for that item. After completing any full section, redisplay the menu with
12
+ updated markers.
13
+
14
+ ---
15
+
16
+ ## Menu
17
+
18
+ ```
19
+ SECTION A -- The workspace (3 items)
20
+ 1 What is this workspace [ ]
21
+ 2 The skill layer -- slash commands you'll use [ ]
22
+ 3 The vibe layer -- ambient intent classification [ ]
23
+
24
+ SECTION B -- Build your first thing (5 items)
25
+ 4 Echo workflow tour [ ]
26
+ 5 Custom workflow with schemas [ ]
27
+ 6 Platform tools and credentials [ ]
28
+ 7 Multi-step and conditionals [ ]
29
+ 8 Going to production [ ]
30
+
31
+ SECTION C -- The Organization Model (3 items)
32
+ 9 /knowledge ceremony -- identity, customers, [ ]
33
+ offerings via the layered flow
34
+ 10 Features and labels [ ]
35
+ 11 Entity extensions -- BaseProject, BaseDeal [ ]
36
+
37
+ SECTION D -- Modules (load on demand) (6 items)
38
+ 12 HITL [ ]
39
+ 13 Schedules [ ]
40
+ 14 Notifications + integrations [ ]
41
+ 15 Error handling [ ]
42
+ 16 LLM and agents [ ]
43
+ 17 Composition (execution.trigger) [ ]
44
+
45
+ SECTION E -- Power user (2 items)
46
+ 18 Rules, memory, scaffold registry [ ]
47
+ 19 Template lifecycle and /git-sync [ ]
48
+ ```
49
+
50
+ ---
51
+
52
+ ## SECTION A -- The Workspace
53
+
54
+ ### Item 1: What is this workspace
55
+
56
+ **Goal:** The user understands the three-layer project structure, the standalone repo model, and
57
+ why the scaffold is described as an agent operating environment, not just an app starter.
58
+
59
+ **Estimated time:** 15 min
60
+
61
+ **Files referenced:** `.claude/rules/agent-start-here.md`, `CLAUDE.md`,
62
+ `node_modules/@elevasis/sdk/reference/scaffold/index.mdx`
63
+
64
+ **Flow:**
65
+
66
+ Open by reading `.claude/rules/agent-start-here.md` (the canonical first-read) and `CLAUDE.md`
67
+ (the project bootstrap surface). Use both to anchor the explanation below.
68
+
69
+ Open with the skip affordance:
70
+
71
+ > "Item 1 covers what this workspace is and how it is structured. If you already know the
72
+ > three-layer layout (ui / operations / core), the standalone repo model, and why the scaffold
73
+ > is an agent operating environment, type 'skip' and I'll mark it complete."
74
+
75
+ If the user skips, mark `[x] YYYY-MM-DD` in `.claude/memory/tutorial-progress.md` for item 1
76
+ and move on.
77
+
78
+ Otherwise, walk through the following:
79
+
80
+ **Three runtime layers.** The template is divided into three directories with hard runtime
81
+ boundaries:
82
+
83
+ - `ui/` -- React 19 frontend (Vite, TanStack Router, Mantine). Browser runtime. Imports from
84
+ `core/` only.
85
+ - `operations/` -- Platform workflows and agents (Elevasis SDK). Node target. Imports from
86
+ `core/` only.
87
+ - `core/` -- Runtime-agnostic shared contracts: Zod schemas, TypeScript types, organization
88
+ model configuration. No React, no Node APIs, no SDK worker imports.
89
+
90
+ Emphasize: `ui/` and `operations/` are separate runtimes. They communicate through the platform
91
+ API at runtime. At build time they share types only via `core/`.
92
+
93
+ **Standalone repo.** This project is NOT part of the monorepo pnpm workspace. It has its own
94
+ `.git/`, own `pnpm-lock.yaml`, own `node_modules/`. The monorepo boundary hook does not apply
95
+ here. Git, gh, and CLI commands work directly.
96
+
97
+ **Agent operating environment.** Read the "Template Surfaces" section from `agent-start-here.md`
98
+ aloud to the user. Key point: the `.claude/` directory is a first-class surface -- rules (always
99
+ loaded), skills (invocable via slash commands), hooks (run on tool calls), and memory (persists
100
+ across sessions). The scaffold is not just an app starter; the agent reads `.claude/rules/` on
101
+ every session and uses it to route task classes, resolve boundaries, and choose the right tool.
102
+
103
+ **SDK reference scaffold.** After `pnpm install`, the entry point
104
+ `node_modules/@elevasis/sdk/reference/scaffold/index.mdx` gives access to canonical recipes, UI
105
+ patterns, the gating model, contracts, and a glossary. Whenever the user wants the authoritative
106
+ answer on a scaffold surface (how to add a feature, extend an entity, wire a workflow), that
107
+ index is the starting point.
108
+
109
+ **Verification:** Ask the user: "Where does the agent look first when entering a session?" (Answer:
110
+ `agent-start-here.md` via the always-loaded rules.) If they get it right, continue. If not, point
111
+ them to the "First Action: Check Active Projects" and "Discovery Order" sections in
112
+ `agent-start-here.md`.
113
+
114
+ **Completion signal:** Mark `[ ] 1 What is this workspace` as `[x] YYYY-MM-DD` in
115
+ `.claude/memory/tutorial-progress.md`.
116
+
117
+ ---
118
+
119
+ ### Item 2: The skill layer -- slash commands you'll use
120
+
121
+ **Goal:** The user knows all 12 slash commands, how to invoke them, where they live, and can
122
+ demonstrate `/status` and `/project`.
123
+
124
+ **Estimated time:** 15 min
125
+
126
+ **Files referenced:** `.claude/skills/*/SKILL.md` (skim), `CLAUDE.md` slash command table
127
+
128
+ **Flow:**
129
+
130
+ Open with the skip affordance:
131
+
132
+ > "Item 2 walks through the 12 slash commands in this scaffold. If you've already used them and
133
+ > know where they live, type 'skip' and I'll mark it complete."
134
+
135
+ If the user skips, mark complete and move on.
136
+
137
+ Otherwise, read the Slash Commands table in `CLAUDE.md` aloud. Then explain the skill system:
138
+
139
+ **Invocation pattern.** Every slash command is a skill. Type `/<name>` (optionally with args,
140
+ e.g., `/knowledge features`). The agent reads `.claude/skills/<name>/SKILL.md` and follows its
141
+ instructions. Skills may be context-forked (they run in a subagent) or inline. The frontmatter
142
+ `context: fork` means a subagent handles it.
143
+
144
+ **Where they live.** Each skill lives in `.claude/skills/<name>/SKILL.md`. Multi-step operations
145
+ that belong to a skill (but are too large for `SKILL.md`) live in
146
+ `.claude/skills/<name>/operations/<op>.md`.
147
+
148
+ **How the agent knows to use them.** The `description` frontmatter field (or `metadata.promptSignals`)
149
+ tells the agent when to auto-invoke a skill without an explicit slash command. For example,
150
+ `/knowledge` auto-invokes whenever the conversation references org-model entities.
151
+
152
+ Walk through each of the 12 commands:
153
+
154
+ - `/setup` -- first-time bootstrap: placeholder replacement, deps, verification, then hands off
155
+ to `/knowledge`
156
+ - `/save` -- conversation fanout: updates knowledge docs, persists `prj_tasks.resume_context`
157
+ via `elevasis-sdk project:task:save`, creates a project note on blockers/status signals
158
+ - `/dsp` -- parallel agent dispatch for implementation tasks with multiple independent steps
159
+ - `/deploy` -- test, build, commit, push pipeline
160
+ - `/explore` -- codebase exploration anchored to docs
161
+ - `/git-sync` -- pull latest changes, surface new template sync notes, install when needed,
162
+ run baseline verification, then stop before manual reconciliation
163
+ - `/status` -- quick project health check
164
+ - `/project` -- primary work-tracking entry point; portfolio orientation, intent detection
165
+ (resume vs new), and project/milestone/task/note lifecycle via `elevasis-sdk project:*`
166
+ - `/elevasis` -- SDK operations: check, deploy, exec, describe, logs, creds
167
+ - `/sync` -- fresh reinstall and cache reset when local deps or build caches are stale
168
+ - `/knowledge` -- org model ceremony: read, codify, toggle, layered flow for identity through
169
+ goals
170
+ - `/submit-request` -- submit a structured request (bug, feature, support) to the platform
171
+
172
+ **Demo 1: `/status`.** Ask the user to type `/status`. Narrate what it shows: project health
173
+ summary, active projects, recent executions if any.
174
+
175
+ **Demo 2: `/project`.** Ask the user to type `/project`. Walk them through the portfolio
176
+ orientation output. Show how to create a task: `/project create task --title "My first task"`.
177
+
178
+ **Verification:** The user can name any 3 commands unprompted and describe what they do.
179
+
180
+ **Completion signal:** Mark `[ ] 2 The skill layer` as `[x] YYYY-MM-DD` in
181
+ `.claude/memory/tutorial-progress.md`.
182
+
183
+ ---
184
+
185
+ ### Item 3: The vibe layer -- ambient intent classification
186
+
187
+ **Goal:** The user understands the 7-intent classifier, can recognize when it fires, and knows
188
+ it is absent in the monorepo.
189
+
190
+ **Estimated time:** 15 min
191
+
192
+ **Files referenced:** `.claude/rules/vibe.md`, `CLAUDE.md` ambient vibe section
193
+
194
+ **Flow:**
195
+
196
+ Open with the skip affordance:
197
+
198
+ > "Item 3 covers the vibe layer -- an ambient intent classifier that fires on every natural-language
199
+ > message. If you already know the 7 intent types and how the classifier routes them, type 'skip'
200
+ > and I'll mark it complete."
201
+
202
+ If the user skips, mark complete and move on.
203
+
204
+ Otherwise, read `.claude/rules/vibe.md` before explaining. Then walk through:
205
+
206
+ **What vibe is.** Every natural-language message in an external project passes through the vibe
207
+ classifier before the agent acts. No slash command, no activation phrase. The agent silently
208
+ classifies the message into one of seven intent types and routes to the right behavior. The user
209
+ never sees the classification.
210
+
211
+ **The seven intent types.** Walk through each by name with one fixture example:
212
+
213
+ - **Capture** -- "Add a task to follow up with the Shopify client." Agent drafts, confirms,
214
+ writes via `elevasis-sdk project:*`.
215
+ - **Query** -- "What's pending in the HITL queue?" Agent reads and narrates.
216
+ - **Describe** -- "What's going on with this deal?" Agent narrates from org model labels.
217
+ - **Transition** -- "Done with the proposal draft." Agent confirms, updates task status via
218
+ `elevasis-sdk project:task:save`.
219
+ - **Navigate** -- "Let's focus on the onboarding flow." Agent updates scope, narrates the shift.
220
+ - **Codify** -- "We track deals by Shopify platform." Agent detects new organizational reality
221
+ and delegates to `/knowledge`.
222
+ - **Toggle** -- "Turn on the lead-gen feature." Agent delegates to `/knowledge features`.
223
+
224
+ **Important behavioral rules.** Codify and Toggle delegate immediately to `/knowledge` -- the
225
+ vibe layer detects intent but does NOT run the ceremony itself. The ceremony (snapshot, propose,
226
+ confirm, write, validate, rollback) belongs to `/knowledge`. Ambiguous messages get one
227
+ clarifying question; no precedence rule is applied.
228
+
229
+ **Where it applies.** Vibe is always-on in external projects. It is explicitly OFF in the
230
+ monorepo. The monorepo uses task-class routing via `agent-start-here.md` instead. If the user is
231
+ working inside the monorepo (`apps/`, `packages/`, etc.), vibe does not fire.
232
+
233
+ **Reference.** The full classifier definition, fixture tables, and codify heuristics live in
234
+ `.claude/rules/vibe.md`. The `CLAUDE.md` table is a pointer -- for authoritative behavior, read
235
+ the rule file.
236
+
237
+ **Verification:** Ask "What happens when you type 'turn on SEO'?" Answer: Vibe classifies it as
238
+ Toggle intent and delegates to `/knowledge features` without running the ceremony itself.
239
+
240
+ **Completion signal:** Mark `[ ] 3 The vibe layer` as `[x] YYYY-MM-DD` in
241
+ `.claude/memory/tutorial-progress.md`.
242
+
243
+ ---
244
+
245
+ ## SECTION B -- Build Your First Thing
246
+
247
+ ### Item 4: Echo workflow tour
248
+
249
+ **Goal:** The user reads the echo workflow source, validates it, deploys it, and executes it
250
+ end-to-end.
251
+
252
+ **Estimated time:** 20 min
253
+
254
+ **Files referenced:** `operations/src/example/echo.ts`, `core/types/index.ts`,
255
+ `operations/src/index.ts`
256
+
257
+ **Commands:** `pnpm -C operations check`, `pnpm -C operations deploy`,
258
+ `pnpm elevasis-sdk exec echo --input '{"message":"hi"}'`
259
+
260
+ **Flow:**
261
+
262
+ Tell the user:
263
+
264
+ > "Let's tour the echo workflow -- the starter resource that ships with every template project.
265
+ > We'll read the source, run the validator, deploy it, and execute it."
266
+
267
+ **Step 1: Read the source.** Open `operations/src/example/echo.ts` with the user. Walk through
268
+ each section:
269
+
270
+ - `config` block: `resourceId` (the deploy-time unique ID), `name`, `type: 'workflow'`,
271
+ `description`, `version` (semver, bump on contract changes), `status: 'dev'` (new resources
272
+ start here; `'prod'` means visible to end users in the production environment).
273
+ - `contract` block: `inputSchema` and `outputSchema` are imported from `core/types/index.ts`,
274
+ NOT defined inline. This is the pattern: schemas live in `core/` so both the frontend and
275
+ `operations/` can import them without creating a dependency cycle.
276
+ - `steps` map: each step has `id`, `name`, `description`, a `handler` async function, and
277
+ `next: null` (last step). The handler receives `input` and `context`. `context.logger.info()`
278
+ is the correct logging call -- `console.log` is not captured by the platform.
279
+ - `entryPoint`: the key from `steps` that runs first.
280
+ - `interface.form`: defines the UI form the Command Center renders when a user triggers this
281
+ workflow manually.
282
+
283
+ **Step 2: Validate.** Run:
284
+
285
+ ```bash
286
+ pnpm -C operations check
287
+ ```
288
+
289
+ Show the output. Explain: this catches duplicate `resourceId`s, broken step chains (`next`
290
+ referencing a non-existent step), invalid schema serialization, and other errors the platform
291
+ would reject at deploy time. Exit code 0 = pass.
292
+
293
+ **Step 3: Deploy.** Run:
294
+
295
+ ```bash
296
+ pnpm -C operations deploy
297
+ ```
298
+
299
+ Walk through the output stages: Authenticating, Validating, Bundling (esbuild packages
300
+ `operations/src/index.ts` and all dependencies into a single CJS file), Uploading. After deploy,
301
+ resources are live immediately -- no restart required.
302
+
303
+ **Step 4: Execute.** Run:
304
+
305
+ ```bash
306
+ pnpm elevasis-sdk exec echo --input '{"message":"hi"}'
307
+ ```
308
+
309
+ Show the output: status, duration, and output `{ "echo": "hi" }`. Explain: the platform
310
+ validated `{"message":"hi"}` against `echoInputSchema`, spun up an ephemeral worker thread,
311
+ ran the handler, returned the result, and terminated the worker.
312
+
313
+ **Step 5: Check history.** Run:
314
+
315
+ ```bash
316
+ pnpm elevasis-sdk executions echo
317
+ ```
318
+
319
+ Pick the execution ID from the list and run:
320
+
321
+ ```bash
322
+ pnpm elevasis-sdk execution echo <execution-id>
323
+ ```
324
+
325
+ Show the full detail including logs. Point out the `[echo] Received message: "hi"` log line
326
+ emitted by `context.logger.info()` in the handler.
327
+
328
+ **Verification:** The user sees the execution complete with `{ "echo": "hi" }` output.
329
+
330
+ **Completion signal:** Mark `[ ] 4 Echo workflow tour` as `[x] YYYY-MM-DD` in
331
+ `.claude/memory/tutorial-progress.md`.
332
+
333
+ ---
334
+
335
+ ### Item 5: Custom workflow with schemas
336
+
337
+ **Goal:** The user creates a new workflow in its own domain directory, defines a Zod input schema
338
+ in `core/types/`, registers it in `operations/src/index.ts`, and validates it.
339
+
340
+ **Estimated time:** 25 min
341
+
342
+ **Files referenced:** `operations/src/example/echo.ts` (model), `core/types/index.ts`,
343
+ `operations/src/index.ts`
344
+
345
+ **Commands:** `pnpm -C operations check`, `pnpm -C core check-types`
346
+
347
+ **Flow:**
348
+
349
+ Tell the user:
350
+
351
+ > "Now you'll build a real workflow from scratch. We'll pick a domain for your project, define
352
+ > the input and output schemas in `core/types/`, author the workflow in `operations/src/`, and
353
+ > register it."
354
+
355
+ **Step 1: Pick a domain.** Ask the user what their project does -- even a rough description is
356
+ enough. Suggest a simple workflow relevant to their context (e.g., "classify a support ticket",
357
+ "score a lead", "send a notification on a Stripe event"). Name the domain in kebab-case.
358
+
359
+ **Step 2: Define schemas in `core/types/`.** Open `core/types/index.ts`. Walk the user through
360
+ the schema convention:
361
+
362
+ ```typescript
363
+ export const myWorkflowInputSchema = z.object({
364
+ // define fields here
365
+ })
366
+ export type MyWorkflowInput = z.infer<typeof myWorkflowInputSchema>
367
+
368
+ export const myWorkflowOutputSchema = z.object({
369
+ // define fields here
370
+ })
371
+ export type MyWorkflowOutput = z.infer<typeof myWorkflowOutputSchema>
372
+ ```
373
+
374
+ Explain: `z.infer` gives you a TypeScript type from the schema so the type system can check your
375
+ handlers. You define once, get both runtime validation and compile-time inference. The schema
376
+ lives in `core/` so the frontend can import it for form validation without importing SDK worker
377
+ code.
378
+
379
+ Explain common field types: `z.string()`, `z.number()`, `z.boolean()`, `z.enum([...])`,
380
+ `z.string().optional()`, `z.object({...})`, `z.array(z.string())`.
381
+
382
+ After writing the schemas, run:
383
+
384
+ ```bash
385
+ pnpm -C core check-types
386
+ ```
387
+
388
+ Fix any TypeScript errors before continuing.
389
+
390
+ **Step 3: Create the domain directory.** Create `operations/src/<domain>/` with two files:
391
+ `index.ts` (the domain barrel) and `<workflow-name>.ts` (the workflow implementation). Mirror
392
+ the echo structure but use the new schemas imported from `@core/types`.
393
+
394
+ Key points to emphasize:
395
+
396
+ - `resourceId` must be unique within the organization (kebab-case).
397
+ - `status: 'dev'` for new resources.
398
+ - `next: null` on the last step.
399
+ - Import schemas from `@core/types`, never define them inline in the workflow file.
400
+
401
+ **Step 4: Register in `operations/src/index.ts`.** Show the current registry:
402
+
403
+ ```typescript
404
+ import * as example from './example/index.js'
405
+ import * as emailNotification from './email-notification/exports.js'
406
+
407
+ const org: DeploymentSpec = {
408
+ version: '0.1.0',
409
+ workflows: [...example.workflows, ...emailNotification.workflows],
410
+ agents: [...example.agents, ...emailNotification.agents]
411
+ }
412
+ ```
413
+
414
+ Add the new domain import and spread its `workflows` into the `DeploymentSpec`.
415
+
416
+ **Step 5: Validate.** Run `pnpm -C operations check`. The output should now show N+1 resources
417
+ with 0 errors.
418
+
419
+ **Verification:** `pnpm -C operations check` passes with the new workflow listed.
420
+
421
+ **Completion signal:** Mark `[ ] 5 Custom workflow with schemas` as `[x] YYYY-MM-DD` in
422
+ `.claude/memory/tutorial-progress.md`.
423
+
424
+ ---
425
+
426
+ ### Item 6: Platform tools and credentials
427
+
428
+ **Goal:** The user understands singleton adapters vs factory adapters, imports them correctly,
429
+ and knows how to create a credential in Command Center.
430
+
431
+ **Estimated time:** 20 min
432
+
433
+ **Files referenced:** `.claude/skills/elevasis/SKILL.md` (creds section),
434
+ `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
435
+
436
+ **Flow:**
437
+
438
+ Tell the user:
439
+
440
+ > "The platform provides typed adapters for external services: CRM, email, payments, AI, storage,
441
+ > and more. You call them from workflow handlers without putting API keys in your code."
442
+
443
+ **Singleton adapters.** Import once at the top of the worker file, use directly:
444
+
445
+ ```typescript
446
+ import { llm, scheduler, storage, notifications } from '@elevasis/sdk/worker'
447
+ ```
448
+
449
+ These are singletons -- no credential binding at construction. They use platform-managed config.
450
+ Use them for: LLM generation, scheduling, object storage, in-app notifications.
451
+
452
+ **Factory adapters.** Credential-bound: constructed with a credential name, then used:
453
+
454
+ ```typescript
455
+ import { createAttioAdapter } from '@elevasis/sdk/worker'
456
+
457
+ const attio = createAttioAdapter('my-attio-cred')
458
+ // inside a step handler:
459
+ await attio.createRecord({ ... })
460
+ ```
461
+
462
+ The string `'my-attio-cred'` is the credential name you register in Command Center. The naming
463
+ convention is `{org}-{service}` kebab-case, e.g. `acme-attio`, `acme-stripe`.
464
+
465
+ **Credential setup in Command Center.** Walk the user through: Command Center -> Settings ->
466
+ Credentials -> Create. The credential is stored encrypted on the platform. Workers access it at
467
+ runtime through the adapter. It never appears in code or `.env`. The `.env` file holds only
468
+ `ELEVASIS_PLATFORM_KEY` for CLI authentication.
469
+
470
+ **Common error.** `PlatformToolError: credential not found` means the credential name in code
471
+ does not exactly match what was created in Command Center. Check spelling and case; credential
472
+ names are case-sensitive.
473
+
474
+ **CLI credential management.** Show the `/elevasis creds` surface as an alternative to the UI:
475
+
476
+ ```bash
477
+ pnpm elevasis-sdk creds list
478
+ pnpm elevasis-sdk creds create --name acme-attio --type api-key --value '{"apiKey":"..."}'
479
+ ```
480
+
481
+ **Verification:** Ask: "Where do integration credentials live at runtime?" Answer: on the platform,
482
+ injected by the worker runtime. Not in `.env`, not in code.
483
+
484
+ **Completion signal:** Mark `[ ] 6 Platform tools and credentials` as `[x] YYYY-MM-DD` in
485
+ `.claude/memory/tutorial-progress.md`.
486
+
487
+ ---
488
+
489
+ ### Item 7: Multi-step and conditionals
490
+
491
+ **Goal:** The user implements a two-step LINEAR workflow and a CONDITIONAL workflow, understanding
492
+ how data flows between steps.
493
+
494
+ **Estimated time:** 25 min
495
+
496
+ **Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`,
497
+ `apps/docs/content/docs/sdk/concepts.mdx` (design decisions section)
498
+
499
+ **Flow:**
500
+
501
+ **Step 1: LINEAR steps.** Extend the workflow from Item 5 (or create a new one) to add a second
502
+ step. Show the `next` field on step 1 pointing to the key of step 2:
503
+
504
+ ```typescript
505
+ steps: {
506
+ fetchData: {
507
+ id: 'fetchData',
508
+ name: 'Fetch Data',
509
+ handler: async (input, context) => {
510
+ // ... fetch or compute something
511
+ return { result: 'some-value' }
512
+ },
513
+ inputSchema: myInputSchema,
514
+ outputSchema: z.object({ result: z.string() }),
515
+ next: 'processData' // <-- key of the next step
516
+ },
517
+ processData: {
518
+ id: 'processData',
519
+ name: 'Process Data',
520
+ handler: async (input, context) => {
521
+ const { result } = input // receives output from fetchData
522
+ return { finalResult: result.toUpperCase() }
523
+ },
524
+ inputSchema: z.object({ result: z.string() }),
525
+ outputSchema: myOutputSchema,
526
+ next: null
527
+ }
528
+ },
529
+ entryPoint: 'fetchData'
530
+ ```
531
+
532
+ Emphasize: the output schema of step N must match the input schema of step N+1. The platform
533
+ validates this chain at deploy time. Data passes from step to step -- no global state, no shared
534
+ variables.
535
+
536
+ **Step 2: CONDITIONAL routing.** Import `StepType` and show a conditional branch:
537
+
538
+ ```typescript
539
+ import type { WorkflowDefinition, StepType } from '@elevasis/sdk'
540
+
541
+ // ... inside steps:
542
+ routeStep: {
543
+ id: 'routeStep',
544
+ type: StepType.CONDITIONAL,
545
+ name: 'Route by Score',
546
+ handler: async (input, context) => {
547
+ if (input.score >= 80) return { path: 'approve', ...input }
548
+ if (input.score >= 50) return { path: 'review', ...input }
549
+ return { path: 'reject', ...input }
550
+ },
551
+ routes: {
552
+ approve: 'approveStep',
553
+ review: 'reviewStep',
554
+ default: 'rejectStep' // always include a default
555
+ },
556
+ // ...
557
+ }
558
+ ```
559
+
560
+ Always include a `default` route. Without it, an execution whose output does not match any
561
+ explicit key will fail at runtime.
562
+
563
+ **Verification:** `pnpm -C operations check` passes with the multi-step workflow registered. Ask
564
+ the user what happens if they omit the `default` route (runtime failure for unmatched paths).
565
+
566
+ **Completion signal:** Mark `[ ] 7 Multi-step and conditionals` as `[x] YYYY-MM-DD` in
567
+ `.claude/memory/tutorial-progress.md`.
568
+
569
+ ---
570
+
571
+ ### Item 8: Going to production
572
+
573
+ **Goal:** The user knows the dev/prod split, version bumping flags, and the `cli-cwd-invariant`
574
+ rule for `--prod` flag placement.
575
+
576
+ **Estimated time:** 15 min
577
+
578
+ **Files referenced:** `.claude/rules/deployment.md`, `.claude/rules/cli-cwd-invariant.md`
579
+
580
+ **Commands:** `pnpm -C operations deploy:prod`
581
+
582
+ **Flow:**
583
+
584
+ **Dev vs prod targeting.** The default `pnpm -C operations deploy` targets the development
585
+ environment. To deploy to production:
586
+
587
+ ```bash
588
+ pnpm -C operations deploy:prod
589
+ ```
590
+
591
+ In `config.status`: `'dev'` means the resource is visible only in dev mode. Change to `'prod'`
592
+ before deploying to production -- otherwise the resource deploys but is not exposed in the
593
+ production surface.
594
+
595
+ **Version bumping.** The deploy command accepts version bump flags that are written back to
596
+ `operations/src/index.ts`:
597
+
598
+ ```bash
599
+ pnpm -C operations deploy:prod # no bump (same version)
600
+ # or pass a flag via pnpm scripts if wired in package.json:
601
+ pnpm elevasis-sdk deploy --prod --patch # 1.0.0 -> 1.0.1
602
+ pnpm elevasis-sdk deploy --prod --minor # 1.0.0 -> 1.1.0
603
+ pnpm elevasis-sdk deploy --prod --major # 1.0.0 -> 2.0.0
604
+ ```
605
+
606
+ Bump rules: `--patch` for bug fixes, `--minor` for new features (no breaking schema changes),
607
+ `--major` for breaking input/output schema changes.
608
+
609
+ **The `cli-cwd-invariant`.** Read `.claude/rules/cli-cwd-invariant.md`. Two critical rules:
610
+
611
+ 1. The `--prod` flag on the `elevasis-sdk` binary MUST come before the subcommand name:
612
+
613
+ ```bash
614
+ # Correct
615
+ pnpm elevasis-sdk --prod deploy
616
+ # Wrong -- --prod after deploy is silently ignored
617
+ pnpm elevasis-sdk deploy --prod
618
+ ```
619
+
620
+ The `pnpm -C operations deploy:prod` script in `package.json` handles this correctly.
621
+ Prefer the script over a raw CLI call to avoid the pitfall.
622
+
623
+ 2. Never use bare `cd <dir> && elevasis-sdk ...` -- use a subshell `(cd <dir> && ...)` or
624
+ `pnpm -C <dir> ...` to avoid CWD drift that causes env resolution failures.
625
+
626
+ **Standard pre-production checklist.** Walk the user through:
627
+
628
+ ```
629
+ 1. pnpm -C operations check -- validate resources
630
+ 2. pnpm -C operations check-types -- TypeScript type-check
631
+ 3. pnpm elevasis-sdk exec <id> -i '...' -- test in dev
632
+ 4. pnpm -C operations deploy:prod -- ship to prod
633
+ ```
634
+
635
+ **Verification:** The user can explain where `--prod` must be placed and why.
636
+
637
+ **Completion signal:** Mark `[ ] 8 Going to production` as `[x] YYYY-MM-DD` in
638
+ `.claude/memory/tutorial-progress.md`.
639
+
640
+ ---
641
+
642
+ ## SECTION C -- The Organization Model
643
+
644
+ ### Item 9: /knowledge ceremony -- identity, customers, offerings via the layered flow
645
+
646
+ **Goal:** The user runs `/knowledge` and understands the six-step write ceremony, why direct
647
+ edits to `core/config/organization-model.ts` are blocked, and the layered flow order.
648
+
649
+ **Estimated time:** 25 min
650
+
651
+ **Files referenced:** `.claude/skills/knowledge/SKILL.md`, `core/config/organization-model.ts`,
652
+ `.claude/rules/organization-os.md`
653
+
654
+ **Flow:**
655
+
656
+ Tell the user:
657
+
658
+ > "The organization model is the semantic contract layer that defines your business reality:
659
+ > who you are, who your customers are, what you offer, your team, and your goals. All edits go
660
+ > through `/knowledge` -- not direct file edits."
661
+
662
+ **Why direct edits are blocked.** Read `core/config/organization-model.ts`. The file calls
663
+ `resolveOrganizationModel()` which runs Zod cross-reference validation: every customer segment
664
+ referenced in offerings must exist in `customers`, every resource mapping must reference a valid
665
+ feature ID, and so on. A direct edit that passes TypeScript can still fail the cross-ref check,
666
+ leaving the project in a broken state. The `/knowledge` ceremony runs both checks and rolls back
667
+ on failure.
668
+
669
+ **The six-step ceremony.** Walk through `.claude/skills/knowledge/SKILL.md`:
670
+
671
+ 1. **Snapshot** -- reads `core/config/organization-model.ts` into memory before any edit
672
+ 2. **Propose** -- shows the diff and the proposed new value alongside current value
673
+ 3. **Confirm** -- pauses for explicit user confirmation; never writes without a clear yes
674
+ 4. **Write** -- applies the edit
675
+ 5. **Validate** -- runs `pnpm -C operations check-types` (TypeScript) then `pnpm -C operations
676
+ check` (Zod parse via `resolveOrganizationModel()`)
677
+ 6. **Rollback** -- if either check fails, restores from the Step 1 snapshot
678
+
679
+ **The layered flow.** Run `/knowledge` with no argument to walk through the full flow:
680
+ identity -> customers -> offerings -> roles -> goals -> techStack. The agent reads the current
681
+ state of each domain, proposes any missing or incomplete fields, and asks for confirmation before
682
+ writing.
683
+
684
+ Or target a specific domain:
685
+
686
+ - `/knowledge identity` -- legal name, mission/vision, industry, geography, timezone
687
+ - `/knowledge customers` -- customer segments with jobs-to-be-done, pains, gains
688
+ - `/knowledge offerings` -- products and services with pricing model and segment references
689
+ - `/knowledge roles` -- role chart with responsibilities and reporting lines
690
+ - `/knowledge goals` -- organizational goals with period and measurable outcomes
691
+ - `/knowledge techStack` -- external SaaS integration metadata on resource mappings
692
+
693
+ **Demo.** Ask the user to run `/knowledge identity`. Walk through the ceremony together -- read
694
+ the current state, propose an edit, confirm, watch it validate.
695
+
696
+ **Verification:** The user can describe what `resolveOrganizationModel()` does and why the
697
+ rollback step exists.
698
+
699
+ **Completion signal:** Mark `[ ] 9 /knowledge ceremony` as `[x] YYYY-MM-DD` in
700
+ `.claude/memory/tutorial-progress.md`.
701
+
702
+ ---
703
+
704
+ ### Item 10: Features and labels
705
+
706
+ **Goal:** The user can enable/disable a feature via `/knowledge features` and rename a display
707
+ label via `/knowledge labels`.
708
+
709
+ **Estimated time:** 15 min
710
+
711
+ **Files referenced:** `.claude/skills/knowledge/operations/features.md`,
712
+ `.claude/skills/knowledge/operations/labels.md`, `core/config/organization-model.ts`
713
+
714
+ **Flow:**
715
+
716
+ **Features.** The org model has a `features` domain that controls which platform features are
717
+ active for this project. Toggle by running:
718
+
719
+ ```
720
+ /knowledge features
721
+ ```
722
+
723
+ The ceremony proposes the change (e.g., `seo: false -> true`), asks for confirmation, writes,
724
+ and validates. The TypeScript constant names are stable (`CRM_FEATURE_ID`, `LEAD_GEN_FEATURE_ID`,
725
+ `PROJECTS_FEATURE_ID`, `OPERATIONS_FEATURE_ID`, `MONITORING_FEATURE_ID`, `SETTINGS_FEATURE_ID`,
726
+ `SEO_FEATURE_ID`) -- use these constants instead of magic strings when writing workflow code that
727
+ references feature IDs.
728
+
729
+ Feature toggles affect the UI shell: a disabled feature removes its nav entry and routes from
730
+ the shell. The org model is the single gate for all feature visibility.
731
+
732
+ **Labels.** The `labels` domain controls display strings on enum entries -- CRM pipeline stages,
733
+ lead-gen lifecycle statuses, project statuses, and so on. Rename by running:
734
+
735
+ ```
736
+ /knowledge labels
737
+ ```
738
+
739
+ The agent reads the current enum entries (each carries an `id`, `label`, and `semanticClass`
740
+ field), proposes a label rename, and writes through the ceremony. The `semanticClass` field
741
+ should not change -- it maps to semantic behavior like `blocked`, `in_progress`, `complete`.
742
+
743
+ Show an example: renaming the `discovery` CRM stage to `qualification`.
744
+
745
+ **Verification:** Ask: "What is the difference between a feature toggle and a label rename?"
746
+ (Toggle controls visibility/routing. Label rename changes display text on an existing enum
747
+ entry without changing its semantic ID or behavior.)
748
+
749
+ **Completion signal:** Mark `[ ] 10 Features and labels` as `[x] YYYY-MM-DD` in
750
+ `.claude/memory/tutorial-progress.md`.
751
+
752
+ ---
753
+
754
+ ### Item 11: Entity extensions -- BaseProject, BaseDeal
755
+
756
+ **Goal:** The user reads the entity extension pattern in `core/types/entities.ts` and understands
757
+ how to add project-specific metadata fields to base entities.
758
+
759
+ **Estimated time:** 20 min
760
+
761
+ **Files referenced:** `core/types/entities.ts`, `core/config/organization-model.ts`
762
+ (for the `resourceMappings` context)
763
+
764
+ **Flow:**
765
+
766
+ **Read the demo extension.** Open `core/types/entities.ts`. Walk through both patterns it
767
+ demonstrates:
768
+
769
+ **Pattern 1: extend with metadata.**
770
+
771
+ ```typescript
772
+ import { BaseProjectSchema, type BaseProject } from '@elevasis/core/entities'
773
+
774
+ export const ProjectMetaSchema = z.object({
775
+ budget: z.number().nonnegative(),
776
+ clientPriority: z.enum(['low', 'medium', 'high'])
777
+ })
778
+ export type ProjectMeta = z.infer<typeof ProjectMetaSchema>
779
+
780
+ export const ProjectSchema = BaseProjectSchema.extend({ metadata: ProjectMetaSchema })
781
+ export type Project = BaseProject<ProjectMeta>
782
+ ```
783
+
784
+ The base types from `@elevasis/core/entities` are generic over a `\<TMeta>` slot. You pass your
785
+ metadata type to specialize the base. `BaseProjectSchema.extend(...)` is the Zod idiom; the
786
+ TypeScript type is `BaseProject<ProjectMeta>`.
787
+
788
+ **Pattern 2: use a base entity unchanged.**
789
+
790
+ ```typescript
791
+ export const DealSchema = BaseDealSchema
792
+ export type Deal = BaseDeal
793
+ ```
794
+
795
+ When you have no project-specific fields, re-export the base. This keeps the import path
796
+ consistent (`@core/types` everywhere) while avoiding redundant extension.
797
+
798
+ **Using entity types in workflows.** When authoring a workflow that receives or returns a
799
+ Project or Deal, reference these types in the input/output schemas rather than re-declaring the
800
+ shape. This ensures the workflow contract stays in sync with the entity definition as it evolves.
801
+
802
+ **Reference for all base shapes.** `@elevasis/core/entities` exports: `BaseProject`,
803
+ `BaseProjectSchema`, `BaseProjectInput`, `BaseMilestone`, `BaseMilestoneSchema`,
804
+ `BaseTask`, `BaseTaskSchema`, `BaseDeal`, `BaseDealSchema`, `BaseCompany`, `BaseCompanySchema`,
805
+ `BaseContact`, `BaseContactSchema`.
806
+
807
+ **Domain rename note.** In `core/config/organization-model.ts`, the domain config fields were
808
+ renamed in the 2026-04-20 expansion: `crm` -> `sales`, `leadGen` -> `prospecting`,
809
+ `delivery` -> `projects`. The feature ID constants (`CRM_FEATURE_ID`, `LEAD_GEN_FEATURE_ID`,
810
+ `PROJECTS_FEATURE_ID`) are unchanged -- use the constants, not the field names, when referencing
811
+ features in code.
812
+
813
+ **Verification:** The user can explain when to use Pattern 1 vs Pattern 2 and name two base
814
+ entity types they could extend.
815
+
816
+ **Completion signal:** Mark `[ ] 11 Entity extensions` as `[x] YYYY-MM-DD` in
817
+ `.claude/memory/tutorial-progress.md`.
818
+
819
+ ---
820
+
821
+ ## SECTION D -- Modules (load on demand)
822
+
823
+ Modules in Section D are available any time. No Section B or C prerequisites are enforced. After
824
+ completing a module, redisplay the full menu with updated progress markers.
825
+
826
+ ---
827
+
828
+ ### Item 12: HITL
829
+
830
+ **Goal:** The user implements an approval gate using `approval.create()` and understands the
831
+ full lifecycle through Command Queue.
832
+
833
+ **Estimated time:** 25 min
834
+
835
+ **Files referenced:** `.claude/rules/error-handling.md`,
836
+ `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
837
+
838
+ **Flow:**
839
+
840
+ **What HITL is.** Human-in-the-Loop (HITL) pauses a workflow execution and waits for a human
841
+ decision before continuing. The execution is suspended at the approval gate; the platform stores
842
+ the pending state. When a user approves or rejects in Command Center, the execution resumes.
843
+
844
+ **Implementation.** Import `approval` from `@elevasis/sdk/worker` and call it inside a step
845
+ handler:
846
+
847
+ ```typescript
848
+ import { approval } from '@elevasis/sdk/worker'
849
+
850
+ const decision = await approval.create({
851
+ title: 'Approve Invoice',
852
+ description: `Invoice #${input.invoiceId} for $${input.amount} ready for approval.`,
853
+ context: { invoiceId: input.invoiceId, amount: input.amount }
854
+ })
855
+
856
+ if (decision.approved) {
857
+ // continue the workflow
858
+ } else {
859
+ // handle rejection
860
+ }
861
+ ```
862
+
863
+ **Lifecycle.** The step calls `approval.create()` -> platform suspends the execution -> a
864
+ pending item appears in the Command Queue in Command Center -> user reviews and approves or
865
+ rejects -> execution resumes from the same step with `decision.approved` set.
866
+
867
+ **Variant rule.** Always use `variant: 'light'` (hardcoded by the platform; never pass `variant`
868
+ in the `approval.create()` call -- the field does not exist on the call signature and will cause
869
+ a TypeScript error).
870
+
871
+ **Verify.** Deploy the workflow, execute it, confirm that it appears as pending in Command Center
872
+ Command Queue, approve it, and confirm the execution completes.
873
+
874
+ **Completion signal:** Mark `[ ] 12 HITL` as `[x] YYYY-MM-DD` in
875
+ `.claude/memory/tutorial-progress.md`.
876
+
877
+ ---
878
+
879
+ ### Item 13: Schedules
880
+
881
+ **Goal:** The user understands the three Task Scheduler types (Recurring, Relative, Absolute),
882
+ uses the `scheduler` singleton inside a workflow, and knows the cron syntax the platform accepts.
883
+
884
+ **Estimated time:** 20 min
885
+
886
+ **Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
887
+
888
+ **Flow:**
889
+
890
+ **The `scheduler` singleton.** Import from `@elevasis/sdk/worker` to trigger workflow actions
891
+ from within a running step:
892
+
893
+ ```typescript
894
+ import { scheduler } from '@elevasis/sdk/worker'
895
+
896
+ await scheduler.schedule({
897
+ resourceId: 'my-workflow',
898
+ triggerAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour from now
899
+ input: { ... }
900
+ })
901
+ ```
902
+
903
+ **Task Scheduler UI -- three types.** Walk the user through Command Center -> Task Scheduler:
904
+
905
+ - **Recurring** -- cron-based, runs on a schedule. Standard cron syntax: `0 9 * * 1-5` (9am
906
+ weekdays). The platform interprets cron in UTC.
907
+ - **Relative** -- fires N minutes/hours/days after a trigger event (e.g., "24 hours after a
908
+ deal enters a stage").
909
+ - **Absolute** -- fires at a specific UTC datetime (one-shot, not recurring).
910
+
911
+ **Creating a schedule.** Show the Task Scheduler create flow: select resource, choose type,
912
+ configure the trigger, set input. Schedules appear as `workflow_schedules` rows in the DB -- the
913
+ `elevasis-sdk executions` command shows executions triggered by schedules alongside manual ones.
914
+
915
+ **Verification:** Ask: "What time zone does the platform use for cron expressions?" (UTC.)
916
+
917
+ **Completion signal:** Mark `[ ] 13 Schedules` as `[x] YYYY-MM-DD` in
918
+ `.claude/memory/tutorial-progress.md`.
919
+
920
+ ---
921
+
922
+ ### Item 14: Notifications + integrations
923
+
924
+ **Goal:** The user uses the `notifications` and `email` singletons and wires a real integration
925
+ adapter using a credential from the project's identity.
926
+
927
+ **Estimated time:** 25 min
928
+
929
+ **Files referenced:** `.claude/skills/elevasis/SKILL.md` (credentials section),
930
+ `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
931
+
932
+ **Flow:**
933
+
934
+ **`notifications` singleton.** Sends in-app notifications visible in Command Center:
935
+
936
+ ```typescript
937
+ import { notifications } from '@elevasis/sdk/worker'
938
+
939
+ await notifications.send({
940
+ title: 'Campaign complete',
941
+ message: `Processed ${count} leads.`,
942
+ level: 'info' // 'info' | 'warning' | 'error'
943
+ })
944
+ ```
945
+
946
+ **`email` singleton.** Sends transactional email through the platform's configured mail provider:
947
+
948
+ ```typescript
949
+ import { email } from '@elevasis/sdk/worker'
950
+
951
+ await email.send({
952
+ to: 'user@example.com',
953
+ subject: 'Your report is ready',
954
+ body: 'Here is a summary...'
955
+ })
956
+ ```
957
+
958
+ **Real adapter integration.** Read `core/config/organization-model.ts` and find the
959
+ `resourceMappings` entries. Each entry may carry a `techStack` extension showing the platform
960
+ (`attio`, `stripe`, `apify`, etc.), the credential name, and whether a credential is configured.
961
+ Use the credential name from there as the constructor argument:
962
+
963
+ ```typescript
964
+ import { createAttioAdapter } from '@elevasis/sdk/worker'
965
+ const attio = createAttioAdapter('acme-attio')
966
+ ```
967
+
968
+ Walk the user through the end-to-end: create the credential in Command Center, reference it in
969
+ the adapter constructor, deploy, execute a test run, check that the integration call succeeds.
970
+
971
+ **Common error.** If the credential is missing or misspelled, the adapter throws
972
+ `PlatformToolError: credential not found`. Check the exact name in Command Center.
973
+
974
+ **Verification:** A test execution that calls the integration adapter completes without a
975
+ `PlatformToolError`.
976
+
977
+ **Completion signal:** Mark `[ ] 14 Notifications + integrations` as `[x] YYYY-MM-DD` in
978
+ `.claude/memory/tutorial-progress.md`.
979
+
980
+ ---
981
+
982
+ ### Item 15: Error handling
983
+
984
+ **Goal:** The user knows the three error types, uses `context.logger`, and implements a
985
+ try/catch with retryable vs permanent error distinction.
986
+
987
+ **Estimated time:** 20 min
988
+
989
+ **Files referenced:** `.claude/rules/error-handling.md`,
990
+ `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
991
+
992
+ **Flow:**
993
+
994
+ Read `.claude/rules/error-handling.md` before starting. Then walk through each error type:
995
+
996
+ **`ExecutionError`.** Base class for workflow/step failures. Throw to halt an execution with a
997
+ user-readable message:
998
+
999
+ ```typescript
1000
+ import { ExecutionError } from '@elevasis/sdk'
1001
+
1002
+ throw new ExecutionError('Payment processing failed', {
1003
+ context: { invoiceId: input.invoiceId }
1004
+ })
1005
+ ```
1006
+
1007
+ **`PlatformToolError`.** Thrown by `platform.call()` and typed adapters when an integration
1008
+ call fails. Always check `err.retryable`:
1009
+
1010
+ ```typescript
1011
+ import { PlatformToolError } from '@elevasis/sdk'
1012
+
1013
+ try {
1014
+ await attio.createRecord({ ... })
1015
+ } catch (err) {
1016
+ if (err instanceof PlatformToolError) {
1017
+ if (err.retryable) {
1018
+ context.logger.warn(`Transient error (rate limit or timeout): ${err.message}`)
1019
+ // safe to retry or re-throw for the caller to handle
1020
+ } else {
1021
+ throw new ExecutionError(`Permanent integration error: ${err.message}`)
1022
+ }
1023
+ }
1024
+ throw err
1025
+ }
1026
+ ```
1027
+
1028
+ **`ToolingError`.** Internal SDK infrastructure error. Treat as non-retryable.
1029
+
1030
+ **`context.logger`.** Use it for all logging inside handlers. `console.log` is not captured by
1031
+ the platform worker runtime. Log levels: `debug`, `info`, `warn`, `error`. The error message in
1032
+ an unhandled throw surfaces directly to CLI output and Command Center -- write it for humans.
1033
+
1034
+ **Common retryable codes.** `rate_limit_exceeded` and `timeout_error` are retryable.
1035
+ `credentials_invalid` and `validation_error` are not.
1036
+
1037
+ **Verification:** Demonstrate a handler that catches a `PlatformToolError`, checks `retryable`,
1038
+ logs appropriately, and re-throws as an `ExecutionError` with a clear message.
1039
+
1040
+ **Completion signal:** Mark `[ ] 15 Error handling` as `[x] YYYY-MM-DD` in
1041
+ `.claude/memory/tutorial-progress.md`.
1042
+
1043
+ ---
1044
+
1045
+ ### Item 16: LLM and agents
1046
+
1047
+ **Goal:** The user calls `llm.generate()` with structured output (Zod -> JSON Schema -> typed
1048
+ result) and understands when to use an agent definition vs a workflow definition.
1049
+
1050
+ **Estimated time:** 25 min
1051
+
1052
+ **Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`,
1053
+ `apps/docs/content/docs/sdk/concepts.mdx` (workflow vs agent section)
1054
+
1055
+ **Flow:**
1056
+
1057
+ **`llm.generate()` with structured output.** The `llm` singleton from `@elevasis/sdk/worker`
1058
+ accepts a Zod output schema and returns a typed result:
1059
+
1060
+ ```typescript
1061
+ import { llm } from '@elevasis/sdk/worker'
1062
+ import { z } from 'zod'
1063
+
1064
+ const classificationSchema = z.object({
1065
+ category: z.enum(['support', 'billing', 'feature-request', 'other']),
1066
+ confidence: z.number().min(0).max(1),
1067
+ summary: z.string()
1068
+ })
1069
+
1070
+ const result = await llm.generate({
1071
+ prompt: `Classify this ticket: ${input.ticketBody}`,
1072
+ outputSchema: classificationSchema,
1073
+ model: 'gpt-4o',
1074
+ temperature: 0.2
1075
+ })
1076
+
1077
+ // result is typed as z.infer<typeof classificationSchema>
1078
+ context.logger.info(`Classified as: ${result.category} (${result.confidence})`)
1079
+ ```
1080
+
1081
+ The SDK converts the Zod schema to JSON Schema internally. The LLM is instructed to return JSON
1082
+ matching that shape. The result is validated and returned as the inferred TypeScript type.
1083
+
1084
+ **Agent definition vs workflow definition.** Read the glossary entries from
1085
+ `apps/docs/content/docs/sdk/concepts.mdx`:
1086
+
1087
+ - **Workflow** -- deterministic, step-by-step, 300s timeout. Use when inputs and outputs are
1088
+ known and the process is predefined.
1089
+ - **Agent** -- autonomous, tool-calling, 600s timeout (configurable). Use when the agent needs
1090
+ to make decisions, call tools iteratively, or navigate ambiguous inputs.
1091
+
1092
+ When to use an agent: the task requires the LLM to decide WHICH tools to call and in WHAT order
1093
+ based on what it finds (not predetermined routing). When to use a workflow: the sequence of steps
1094
+ is predetermined, even if LLM judgment is involved inside individual steps.
1095
+
1096
+ **Model selection.** `gpt-4o` for complex reasoning, `gpt-4o-mini` for speed-sensitive tasks,
1097
+ `o3-mini` for multi-step reasoning chains. Temperature: `0.0` for deterministic classification,
1098
+ `0.2-0.4` for structured extraction, `0.7+` for creative generation.
1099
+
1100
+ **Verification:** A workflow step calls `llm.generate()` with a Zod schema and returns a typed
1101
+ value. The type system confirms the result matches the schema without a cast.
1102
+
1103
+ **Completion signal:** Mark `[ ] 16 LLM and agents` as `[x] YYYY-MM-DD` in
1104
+ `.claude/memory/tutorial-progress.md`.
1105
+
1106
+ ---
1107
+
1108
+ ### Item 17: Composition (execution.trigger)
1109
+
1110
+ **Goal:** The user chains two workflows using `execution.trigger()`, declares the relationship in
1111
+ `DeploymentSpec`, and can find the edge in Command View.
1112
+
1113
+ **Estimated time:** 20 min
1114
+
1115
+ **Files referenced:** `operations/src/index.ts`,
1116
+ `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
1117
+
1118
+ **Flow:**
1119
+
1120
+ **`execution.trigger()`.** Call from inside a step handler to launch another workflow
1121
+ asynchronously:
1122
+
1123
+ ```typescript
1124
+ import { execution } from '@elevasis/sdk/worker'
1125
+
1126
+ const { executionId } = await execution.trigger({
1127
+ resourceId: 'downstream-workflow',
1128
+ input: { ...outputFromCurrentStep }
1129
+ })
1130
+
1131
+ context.logger.info(`Triggered downstream-workflow: ${executionId}`)
1132
+ ```
1133
+
1134
+ The calling step does not wait for the downstream workflow to complete. If you need the result,
1135
+ use async polling or chain via HITL.
1136
+
1137
+ **Declare the relationship in `DeploymentSpec`.** In `operations/src/index.ts`, add a
1138
+ `relationships` entry so the platform knows this workflow triggers another:
1139
+
1140
+ ```typescript
1141
+ const org: DeploymentSpec = {
1142
+ version: '0.1.0',
1143
+ workflows: [...],
1144
+ agents: [...],
1145
+ relationships: [
1146
+ {
1147
+ source: 'upstream-workflow',
1148
+ target: 'downstream-workflow',
1149
+ type: 'triggers'
1150
+ }
1151
+ ]
1152
+ }
1153
+ ```
1154
+
1155
+ This metadata populates the Command View graph. Without it, the edge is not visible in the UI
1156
+ even if the trigger call works.
1157
+
1158
+ **Command View.** Show the user how to find Command View in Command Center -> Operations.
1159
+ Deployed relationships appear as directed edges between resource nodes. This is the primary
1160
+ debugging surface for understanding how automations are connected.
1161
+
1162
+ **Verification:** Deploy both workflows with the relationship declared, then view the edge in
1163
+ Command View.
1164
+
1165
+ **Completion signal:** Mark `[ ] 17 Composition` as `[x] YYYY-MM-DD` in
1166
+ `.claude/memory/tutorial-progress.md`.
1167
+
1168
+ ---
1169
+
1170
+ ## SECTION E -- Power User
1171
+
1172
+ ### Item 18: Rules, memory, scaffold registry
1173
+
1174
+ **Goal:** The user can navigate the `.claude/rules/` and `.claude/memory/` directories and
1175
+ understands the scaffold registry's purpose.
1176
+
1177
+ **Estimated time:** 20 min
1178
+
1179
+ **Files referenced:** `.claude/rules/` (all files), `.claude/memory/` (layout),
1180
+ `.claude/scaffold-registry.yml` (reference, monorepo-side concept)
1181
+
1182
+ **Flow:**
1183
+
1184
+ **Rules directory.** List the current rule files in `.claude/rules/`. Each rule is automatically
1185
+ loaded by the agent on every session based on its `paths:` frontmatter (path globs that trigger
1186
+ auto-load). Key files:
1187
+
1188
+ - `agent-start-here.md` -- always-loaded canonical first-read; task-class routing and boundary
1189
+ resolution
1190
+ - `vibe.md` -- always-loaded ambient intent classifier
1191
+ - `organization-os.md` -- loaded when touching org model, feature access, or entity work
1192
+ - `deployment.md` -- loaded when deploying resources
1193
+ - `error-handling.md` -- loaded when authoring handlers with error concerns
1194
+ - `execution.md` -- loaded when reasoning about the runtime model
1195
+ - `observability.md` -- loaded when using `context.logger` or inspecting executions
1196
+ - `operations.md` -- loaded when touching `operations/src/`
1197
+ - `platform.md` -- loaded when importing from `@elevasis/sdk`
1198
+ - `shared-types.md` -- loaded when touching `core/types/`
1199
+ - `task-tracking.md` -- loaded when managing project tasks
1200
+ - `active-change-index.md` -- flags areas under active architecture change; trust this over
1201
+ stable scaffold docs for those areas
1202
+
1203
+ To add a new rule: create `.claude/rules/<domain>.md` with `paths:` frontmatter matching the
1204
+ glob of files it applies to. The rules system discovers it automatically -- no registration step.
1205
+
1206
+ **Memory directory.** List `.claude/memory/`. Common files:
1207
+
1208
+ - `profile.md` -- track choice and tone implications (written by this tutorial)
1209
+ - `tutorial-progress.md` -- per-item progress markers for both tracks
1210
+
1211
+ Memory files are readable by the agent at session start. The `profile.md` file is particularly
1212
+ important: it sets the project-wide tone for all sessions after the tutorial track is selected.
1213
+
1214
+ **Scaffold registry (monorepo-side concept).** `.claude/scaffold-registry.yml` is the
1215
+ monorepo's source-of-truth for scaffold dependencies -- it maps source paths to generated or
1216
+ manual scaffolds that depend on them. It drives the PostToolUse reminder hook and `/work handoff`
1217
+ preflight in the monorepo. In an external project you are a consumer, not a contributor, of that
1218
+ registry. The relevant thing to know: when the SDK ships a template update, the registry controls
1219
+ which files are Tier 1 (always replaced on sync), Tier 2 (merge-aware), and Tier 3 (never
1220
+ touched). Item 19 covers this in detail.
1221
+
1222
+ **Verification:** Ask: "How does the agent know to load `deployment.md` when you run a deploy?"
1223
+ (Answer: the `paths:` frontmatter in the rule file matches `operations/` file globs and deploy
1224
+ command patterns.)
1225
+
1226
+ **Completion signal:** Mark `[ ] 18 Rules, memory, scaffold registry` as `[x] YYYY-MM-DD` in
1227
+ `.claude/memory/tutorial-progress.md`.
1228
+
1229
+ ---
1230
+
1231
+ ### Item 19: Template lifecycle and /git-sync
1232
+
1233
+ **Goal:** The user understands the three-tier file classification, runs `/git-sync`, and knows
1234
+ what manual reconciliation is required after a template update.
1235
+
1236
+ **Estimated time:** 20 min
1237
+
1238
+ **Files referenced:** `.claude/skills/git-sync/SKILL.md`, `.claude/sync-notes/` (directory)
1239
+
1240
+ **Flow:**
1241
+
1242
+ Tell the user:
1243
+
1244
+ > "The template you scaffolded from is versioned. When a new SDK version ships, the template
1245
+ > updates. `/git-sync` pulls those changes and tells you what changed. You decide what to do
1246
+ > with them."
1247
+
1248
+ **Three-tier file classification.** Walk through the tiers:
1249
+
1250
+ - **Tier 1 (always replaced)** -- SDK-owned files that ship in a known correct state:
1251
+ `.claude/skills/*/SKILL.md` (except project-authored skills), `.claude/hooks/`, `CLAUDE.md`
1252
+ sections that are platform-managed. These are overwritten on every template sync. Do not put
1253
+ custom logic in them; instead use Tier 3 files for project-specific config.
1254
+ - **Tier 2 (merge-aware)** -- Files where the template ships defaults but the project may have
1255
+ customized: `core/config/organization-model.ts`, `.claude/rules/active-change-index.md`,
1256
+ vibe classifier threshold. The sync does not blindly overwrite -- the agent surfaces the diff
1257
+ and the user decides what to keep.
1258
+ - **Tier 3 (never touched)** -- Project-owned files the template never overwrites: project
1259
+ source code in `operations/src/`, `ui/src/`, all `core/types/` files, `core/config/extensions/`,
1260
+ custom rules added to `.claude/rules/`, everything in `.claude/memory/`.
1261
+
1262
+ **Running `/git-sync`.** Read `.claude/skills/git-sync/SKILL.md` and walk the user through each
1263
+ step:
1264
+
1265
+ 1. Check for uncommitted changes (`git status --short`). Stop if dirty.
1266
+ 2. Snapshot current sync-note filenames before pulling.
1267
+ 3. `git pull --rebase` -- stop and report conflicts; do not auto-resolve.
1268
+ 4. Detect dependency baseline changes; run `pnpm install` if `package.json` or
1269
+ `pnpm-lock.yaml` changed.
1270
+ 5. Run baseline verification: `pnpm -C ui check-types`, `pnpm -C ui build`,
1271
+ `pnpm -C operations check`, `pnpm -C operations check-types`.
1272
+ 6. Surface newly introduced sync notes from `.claude/sync-notes/`. Each note has required
1273
+ actions and a verification section.
1274
+ 7. Stop. `/git-sync` does not auto-reconcile. Manual follow-up is explicit.
1275
+
1276
+ **Sync notes contract.** Files in `.claude/sync-notes/` named `YYYY-MM-DD-<slug>.md` are
1277
+ operative release guidance. They use fixed headings: `## Why this note exists`,
1278
+ `## Applies to`, `## Required actions`, `## Verification`, `## Not handled by /git-sync`.
1279
+ Never edit operative note filenames after they ship -- they are append-only.
1280
+
1281
+ **Verification:** The user knows which of their own files are Tier 3 (never touched) and can
1282
+ explain why `/git-sync` stops before reconciliation rather than auto-applying diffs.
1283
+
1284
+ **Completion signal:** Mark `[ ] 19 Template lifecycle and /git-sync` as `[x] YYYY-MM-DD` in
1285
+ `.claude/memory/tutorial-progress.md`.
1286
+
1287
+ ---
1288
+
1289
+ ## After All 19 Items Complete
1290
+
1291
+ When all 19 items are marked `[x]` in `.claude/memory/tutorial-progress.md`, deliver the
1292
+ following closing script:
1293
+
1294
+ > "You've completed the technical track. Here's where to go from here:
1295
+ >
1296
+ > - **Build real automations.** You now have the full surface: workflows, schemas, HITL, schedules,
1297
+ > LLM integration, error handling, and composition. Start with the domain most relevant to your
1298
+ > project and use `/project create` to track the work.
1299
+ > - **SDK reference scaffold.** `node_modules/@elevasis/sdk/reference/scaffold/index.mdx` is the
1300
+ > canonical recipe index for anything you want to build: adding a feature, extending lead-gen,
1301
+ > customizing CRM actions, authoring agents. Read it whenever you are starting something new.
1302
+ > - **Explore the codebase.** `/explore` is your tool for open-ended questions about how things
1303
+ > are wired. Use it before making changes in unfamiliar parts of the project.
1304
+ > - **Keep the org model current.** Run `/knowledge` any time your business reality changes --
1305
+ > new offerings, new customer segments, new goals. The model is the agent's vocabulary for
1306
+ > your project; an accurate model means better agent output.
1307
+ > - **Stay current with template updates.** When a new SDK version ships, run `/git-sync` to pull
1308
+ > it and read the sync notes. They tell you exactly what changed and what manual follow-up is
1309
+ > needed."