@elevasis/sdk 1.22.0 → 1.23.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.
- package/dist/cli.cjs +980 -42
- package/dist/index.d.ts +1221 -220
- package/dist/index.js +390 -15
- package/dist/test-utils/index.d.ts +964 -211
- package/dist/test-utils/index.js +257 -14
- package/dist/worker/index.js +122 -14
- package/package.json +3 -3
- package/reference/claude-config/rules/operations.md +3 -3
- package/reference/claude-config/rules/organization-model.md +88 -85
- package/reference/claude-config/rules/organization-os.md +104 -104
- package/reference/claude-config/rules/vibe.md +235 -235
- package/reference/claude-config/skills/om/SKILL.md +324 -0
- package/reference/claude-config/skills/{knowledge → om}/operations/customers.md +110 -109
- package/reference/claude-config/skills/{knowledge → om}/operations/features.md +77 -76
- package/reference/claude-config/skills/{knowledge → om}/operations/goals.md +119 -118
- package/reference/claude-config/skills/{knowledge → om}/operations/identity.md +94 -93
- package/reference/claude-config/skills/{knowledge → om}/operations/labels.md +94 -94
- package/reference/claude-config/skills/{knowledge → om}/operations/offerings.md +110 -109
- package/reference/claude-config/skills/{knowledge → om}/operations/roles.md +100 -99
- package/reference/claude-config/skills/{knowledge → om}/operations/techStack.md +30 -30
- package/reference/claude-config/skills/project/SKILL.md +1088 -1088
- package/reference/claude-config/skills/setup/SKILL.md +275 -275
- package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
- package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
- package/reference/claude-config/skills/tutorial/technical.md +1303 -1303
- package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
- package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +7 -4
- package/reference/claude-config/sync-notes/2026-05-15-om-skill-rename-and-write-family.md +52 -0
- package/reference/examples/organization-model.ts +26 -2
- package/reference/scaffold/core/organization-model.mdx +16 -11
- package/reference/scaffold/recipes/add-a-feature.md +28 -26
- package/reference/scaffold/recipes/add-a-resource.md +26 -16
- package/reference/scaffold/recipes/customize-organization-model.md +5 -3
- package/reference/scaffold/recipes/extend-lead-gen.md +9 -9
- package/reference/scaffold/recipes/index.md +1 -1
- package/reference/scaffold/recipes/query-the-knowledge-graph.md +189 -185
- package/reference/scaffold/reference/contracts.md +139 -101
- package/reference/scaffold/reference/glossary.md +74 -72
- package/reference/claude-config/skills/knowledge/SKILL.md +0 -345
- /package/reference/claude-config/skills/{knowledge → om}/operations/codify-level-a.md +0 -0
- /package/reference/claude-config/skills/{knowledge → om}/operations/codify-level-b.md +0 -0
|
@@ -1,1303 +1,1303 @@
|
|
|
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 /
|
|
33
|
-
offerings via the layered flow
|
|
34
|
-
10 Systems, actions, 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 System or UI 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., `/
|
|
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
|
-
`/
|
|
151
|
-
|
|
152
|
-
Walk through each of the 12 commands:
|
|
153
|
-
|
|
154
|
-
- `/setup` -- first-time bootstrap: placeholder replacement, deps, verification, then hands off
|
|
155
|
-
to `/
|
|
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
|
-
- `/
|
|
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 `/
|
|
222
|
-
- **Toggle** -- "Turn on the lead-gen system." Agent delegates to `/
|
|
223
|
-
|
|
224
|
-
**Important behavioral rules.** Codify and Toggle delegate immediately to `/
|
|
225
|
-
vibe layer detects intent but does NOT run the ceremony itself. The ceremony (snapshot, propose,
|
|
226
|
-
confirm, write, validate, rollback) belongs to `/
|
|
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 `/
|
|
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 SDK CLI CWD
|
|
574
|
-
guidance in the deployment rule.
|
|
575
|
-
|
|
576
|
-
**Estimated time:** 15 min
|
|
577
|
-
|
|
578
|
-
**Files referenced:** `.claude/rules/deployment.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
|
-
**SDK CLI CWD guidance.** Read `.claude/rules/deployment.md`. Two critical rules:
|
|
610
|
-
|
|
611
|
-
1. The `--prod` flag belongs to the `deploy` command:
|
|
612
|
-
|
|
613
|
-
```bash
|
|
614
|
-
# Correct
|
|
615
|
-
pnpm elevasis-sdk deploy --prod
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
The `pnpm -C operations deploy:prod` script in `package.json` handles this correctly.
|
|
619
|
-
Prefer the script over a raw CLI call for production deploys.
|
|
620
|
-
|
|
621
|
-
2. Never use bare `cd <dir> && elevasis-sdk ...` -- use a subshell `(cd <dir> && ...)` or
|
|
622
|
-
`pnpm -C <dir> ...` to avoid CWD drift that causes env resolution failures.
|
|
623
|
-
|
|
624
|
-
**Standard pre-production checklist.** Walk the user through:
|
|
625
|
-
|
|
626
|
-
```
|
|
627
|
-
1. pnpm -C operations check -- validate resources
|
|
628
|
-
2. pnpm -C operations check-types -- TypeScript type-check
|
|
629
|
-
3. pnpm elevasis-sdk exec <id> -i '...' -- test in dev
|
|
630
|
-
4. pnpm -C operations deploy:prod -- ship to prod
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
**Verification:** The user can explain where `--prod` must be placed and why.
|
|
634
|
-
|
|
635
|
-
**Completion signal:** Mark `[ ] 8 Going to production` as `[x] YYYY-MM-DD` in
|
|
636
|
-
`.claude/memory/tutorial-progress.md`.
|
|
637
|
-
|
|
638
|
-
---
|
|
639
|
-
|
|
640
|
-
## SECTION C -- The Organization Model
|
|
641
|
-
|
|
642
|
-
### Item 9: /
|
|
643
|
-
|
|
644
|
-
**Goal:** The user runs `/
|
|
645
|
-
edits to `core/config/organization-model.ts` are blocked, and the layered flow order.
|
|
646
|
-
|
|
647
|
-
**Estimated time:** 25 min
|
|
648
|
-
|
|
649
|
-
**Files referenced:** `.claude/skills/
|
|
650
|
-
`.claude/rules/organization-os.md`
|
|
651
|
-
|
|
652
|
-
**Flow:**
|
|
653
|
-
|
|
654
|
-
Tell the user:
|
|
655
|
-
|
|
656
|
-
> "The organization model is the semantic contract layer that defines your business reality:
|
|
657
|
-
> who you are, who your customers are, what you offer, your team, and your goals. All edits go
|
|
658
|
-
> through `/
|
|
659
|
-
|
|
660
|
-
**Why direct edits are blocked.** Read `core/config/organization-model.ts`. The file calls
|
|
661
|
-
`resolveOrganizationModel()` which runs Zod cross-reference validation: every customer segment
|
|
662
|
-
referenced in offerings must exist in `customers`, every resource mapping must reference a valid
|
|
663
|
-
system ID, and so on. A direct edit that passes TypeScript can still fail the cross-ref check,
|
|
664
|
-
leaving the project in a broken state. The `/
|
|
665
|
-
on failure.
|
|
666
|
-
|
|
667
|
-
**The six-step ceremony.** Walk through `.claude/skills/
|
|
668
|
-
|
|
669
|
-
1. **Snapshot** -- reads `core/config/organization-model.ts` into memory before any edit
|
|
670
|
-
2. **Propose** -- shows the diff and the proposed new value alongside current value
|
|
671
|
-
3. **Confirm** -- pauses for explicit user confirmation; never writes without a clear yes
|
|
672
|
-
4. **Write** -- applies the edit
|
|
673
|
-
5. **Validate** -- runs `pnpm -C operations check-types` (TypeScript) then `pnpm -C operations
|
|
674
|
-
check` (Zod parse via `resolveOrganizationModel()`)
|
|
675
|
-
6. **Rollback** -- if either check fails, restores from the Step 1 snapshot
|
|
676
|
-
|
|
677
|
-
**The layered flow.** Run `/
|
|
678
|
-
identity -> customers -> offerings -> roles -> goals -> techStack. The agent reads the current
|
|
679
|
-
state of each domain, proposes any missing or incomplete fields, and asks for confirmation before
|
|
680
|
-
writing.
|
|
681
|
-
|
|
682
|
-
Or target a specific domain:
|
|
683
|
-
|
|
684
|
-
- `/
|
|
685
|
-
- `/
|
|
686
|
-
- `/
|
|
687
|
-
- `/
|
|
688
|
-
- `/
|
|
689
|
-
- `/
|
|
690
|
-
|
|
691
|
-
**Demo.** Ask the user to run `/
|
|
692
|
-
the current state, propose an edit, confirm, watch it validate.
|
|
693
|
-
|
|
694
|
-
**Verification:** The user can describe what `resolveOrganizationModel()` does and why the
|
|
695
|
-
rollback step exists.
|
|
696
|
-
|
|
697
|
-
**Completion signal:** Mark `[ ] 9 /
|
|
698
|
-
`.claude/memory/tutorial-progress.md`.
|
|
699
|
-
|
|
700
|
-
---
|
|
701
|
-
|
|
702
|
-
### Item 10: Systems, actions, and labels
|
|
703
|
-
|
|
704
|
-
**Goal:** The user can enable/disable a System via `/
|
|
705
|
-
invokable operations, and rename a display label via `/
|
|
706
|
-
|
|
707
|
-
**Estimated time:** 15 min
|
|
708
|
-
|
|
709
|
-
**Files referenced:** `.claude/skills/
|
|
710
|
-
`.claude/skills/
|
|
711
|
-
|
|
712
|
-
**Flow:**
|
|
713
|
-
|
|
714
|
-
**Systems.** The org model uses Systems for availability, routing, ownership, navigation
|
|
715
|
-
grouping, and knowledge mounts. Toggle availability by running:
|
|
716
|
-
|
|
717
|
-
```
|
|
718
|
-
/
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
The ceremony proposes the change (e.g., `seo: false -> true`), asks for confirmation, writes,
|
|
722
|
-
and validates. Older scaffold recipes and UI packages may still say "feature"; translate that to
|
|
723
|
-
System when discussing Organization OS availability or routing.
|
|
724
|
-
|
|
725
|
-
System toggles affect the UI shell: a disabled System removes its nav entry and routes from
|
|
726
|
-
the shell. Actions are the invokable operations owned or exposed by a System.
|
|
727
|
-
|
|
728
|
-
**Labels.** The `labels` domain controls display strings on enum entries -- CRM pipeline stages,
|
|
729
|
-
lead-gen lifecycle statuses, project statuses, and so on. Rename by running:
|
|
730
|
-
|
|
731
|
-
```
|
|
732
|
-
/
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
The agent reads the current enum entries (each carries an `id`, `label`, and `semanticClass`
|
|
736
|
-
field), proposes a label rename, and writes through the ceremony. The `semanticClass` field
|
|
737
|
-
should not change -- it maps to semantic behavior like `blocked`, `in_progress`, `complete`.
|
|
738
|
-
|
|
739
|
-
Show an example: renaming the `discovery` CRM stage to `qualification`.
|
|
740
|
-
|
|
741
|
-
**Verification:** Ask: "What is the difference between a System toggle and a label rename?"
|
|
742
|
-
(Toggle controls visibility/routing. Label rename changes display text on an existing enum
|
|
743
|
-
entry without changing its semantic ID or behavior.)
|
|
744
|
-
|
|
745
|
-
**Completion signal:** Mark `[ ] 10 Systems, actions, and labels` as `[x] YYYY-MM-DD` in
|
|
746
|
-
`.claude/memory/tutorial-progress.md`.
|
|
747
|
-
|
|
748
|
-
---
|
|
749
|
-
|
|
750
|
-
### Item 11: Entity extensions -- BaseProject, BaseDeal
|
|
751
|
-
|
|
752
|
-
**Goal:** The user reads the entity extension pattern in `core/types/entities.ts` and understands
|
|
753
|
-
how to add project-specific metadata fields to base entities.
|
|
754
|
-
|
|
755
|
-
**Estimated time:** 20 min
|
|
756
|
-
|
|
757
|
-
**Files referenced:** `core/types/entities.ts`, `core/config/organization-model.ts`
|
|
758
|
-
(for the OM Resources descriptor context)
|
|
759
|
-
|
|
760
|
-
**Flow:**
|
|
761
|
-
|
|
762
|
-
**Read the demo extension.** Open `core/types/entities.ts`. Walk through both patterns it
|
|
763
|
-
demonstrates:
|
|
764
|
-
|
|
765
|
-
**Pattern 1: extend with metadata.**
|
|
766
|
-
|
|
767
|
-
```typescript
|
|
768
|
-
import { BaseProjectSchema, type BaseProject } from '@elevasis/core/entities'
|
|
769
|
-
|
|
770
|
-
export const ProjectMetaSchema = z.object({
|
|
771
|
-
budget: z.number().nonnegative(),
|
|
772
|
-
clientPriority: z.enum(['low', 'medium', 'high'])
|
|
773
|
-
})
|
|
774
|
-
export type ProjectMeta = z.infer<typeof ProjectMetaSchema>
|
|
775
|
-
|
|
776
|
-
export const ProjectSchema = BaseProjectSchema.extend({ metadata: ProjectMetaSchema })
|
|
777
|
-
export type Project = BaseProject<ProjectMeta>
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
The base types from `@elevasis/core/entities` are generic over a `\<TMeta>` slot. You pass your
|
|
781
|
-
metadata type to specialize the base. `BaseProjectSchema.extend(...)` is the Zod idiom; the
|
|
782
|
-
TypeScript type is `BaseProject<ProjectMeta>`.
|
|
783
|
-
|
|
784
|
-
**Pattern 2: use a base entity unchanged.**
|
|
785
|
-
|
|
786
|
-
```typescript
|
|
787
|
-
export const DealSchema = BaseDealSchema
|
|
788
|
-
export type Deal = BaseDeal
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
When you have no project-specific fields, re-export the base. This keeps the import path
|
|
792
|
-
consistent (`@core/types` everywhere) while avoiding redundant extension.
|
|
793
|
-
|
|
794
|
-
**Using entity types in workflows.** When authoring a workflow that receives or returns a
|
|
795
|
-
Project or Deal, reference these types in the input/output schemas rather than re-declaring the
|
|
796
|
-
shape. This ensures the workflow contract stays in sync with the entity definition as it evolves.
|
|
797
|
-
|
|
798
|
-
**Reference for all base shapes.** `@elevasis/core/entities` exports: `BaseProject`,
|
|
799
|
-
`BaseProjectSchema`, `BaseProjectInput`, `BaseMilestone`, `BaseMilestoneSchema`,
|
|
800
|
-
`BaseTask`, `BaseTaskSchema`, `BaseDeal`, `BaseDealSchema`, `BaseCompany`, `BaseCompanySchema`,
|
|
801
|
-
`BaseContact`, `BaseContactSchema`.
|
|
802
|
-
|
|
803
|
-
**Domain rename note.** In `core/config/organization-model.ts`, the domain config fields were
|
|
804
|
-
renamed in the 2026-04-20 expansion: `crm` -> `sales`, `leadGen` -> `prospecting`,
|
|
805
|
-
`delivery` -> `projects`. Older feature ID constants may still appear in UI package code and
|
|
806
|
-
scaffold recipes; treat them as implementation constants, not live Organization OS primitives.
|
|
807
|
-
|
|
808
|
-
**Verification:** The user can explain when to use Pattern 1 vs Pattern 2 and name two base
|
|
809
|
-
entity types they could extend.
|
|
810
|
-
|
|
811
|
-
**Completion signal:** Mark `[ ] 11 Entity extensions` as `[x] YYYY-MM-DD` in
|
|
812
|
-
`.claude/memory/tutorial-progress.md`.
|
|
813
|
-
|
|
814
|
-
---
|
|
815
|
-
|
|
816
|
-
## SECTION D -- Modules (load on demand)
|
|
817
|
-
|
|
818
|
-
Modules in Section D are available any time. No Section B or C prerequisites are enforced. After
|
|
819
|
-
completing a module, redisplay the full menu with updated progress markers.
|
|
820
|
-
|
|
821
|
-
---
|
|
822
|
-
|
|
823
|
-
### Item 12: HITL
|
|
824
|
-
|
|
825
|
-
**Goal:** The user implements an approval gate using `approval.create()` and understands the
|
|
826
|
-
full lifecycle through Command Queue.
|
|
827
|
-
|
|
828
|
-
**Estimated time:** 25 min
|
|
829
|
-
|
|
830
|
-
**Files referenced:** `.claude/rules/error-handling.md`,
|
|
831
|
-
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
832
|
-
|
|
833
|
-
**Flow:**
|
|
834
|
-
|
|
835
|
-
**What HITL is.** Human-in-the-Loop (HITL) pauses a workflow execution and waits for a human
|
|
836
|
-
decision before continuing. The execution is suspended at the approval gate; the platform stores
|
|
837
|
-
the pending state. When a user approves or rejects in Command Center, the execution resumes.
|
|
838
|
-
|
|
839
|
-
**Implementation.** Import `approval` from `@elevasis/sdk/worker` and call it inside a step
|
|
840
|
-
handler:
|
|
841
|
-
|
|
842
|
-
```typescript
|
|
843
|
-
import { approval } from '@elevasis/sdk/worker'
|
|
844
|
-
|
|
845
|
-
const decision = await approval.create({
|
|
846
|
-
title: 'Approve Invoice',
|
|
847
|
-
description: `Invoice #${input.invoiceId} for $${input.amount} ready for approval.`,
|
|
848
|
-
context: { invoiceId: input.invoiceId, amount: input.amount }
|
|
849
|
-
})
|
|
850
|
-
|
|
851
|
-
if (decision.approved) {
|
|
852
|
-
// continue the workflow
|
|
853
|
-
} else {
|
|
854
|
-
// handle rejection
|
|
855
|
-
}
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
**Lifecycle.** The step calls `approval.create()` -> platform suspends the execution -> a
|
|
859
|
-
pending item appears in the Command Queue in Command Center -> user reviews and approves or
|
|
860
|
-
rejects -> execution resumes from the same step with `decision.approved` set.
|
|
861
|
-
|
|
862
|
-
**Variant rule.** Always use `variant: 'light'` (hardcoded by the platform; never pass `variant`
|
|
863
|
-
in the `approval.create()` call -- the field does not exist on the call signature and will cause
|
|
864
|
-
a TypeScript error).
|
|
865
|
-
|
|
866
|
-
**Verify.** Deploy the workflow, execute it, confirm that it appears as pending in Command Center
|
|
867
|
-
Command Queue, approve it, and confirm the execution completes.
|
|
868
|
-
|
|
869
|
-
**Completion signal:** Mark `[ ] 12 HITL` as `[x] YYYY-MM-DD` in
|
|
870
|
-
`.claude/memory/tutorial-progress.md`.
|
|
871
|
-
|
|
872
|
-
---
|
|
873
|
-
|
|
874
|
-
### Item 13: Schedules
|
|
875
|
-
|
|
876
|
-
**Goal:** The user understands the three Task Scheduler types (Recurring, Relative, Absolute),
|
|
877
|
-
uses the `scheduler` singleton inside a workflow, and knows the cron syntax the platform accepts.
|
|
878
|
-
|
|
879
|
-
**Estimated time:** 20 min
|
|
880
|
-
|
|
881
|
-
**Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
882
|
-
|
|
883
|
-
**Flow:**
|
|
884
|
-
|
|
885
|
-
**The `scheduler` singleton.** Import from `@elevasis/sdk/worker` to trigger workflow actions
|
|
886
|
-
from within a running step:
|
|
887
|
-
|
|
888
|
-
```typescript
|
|
889
|
-
import { scheduler } from '@elevasis/sdk/worker'
|
|
890
|
-
|
|
891
|
-
await scheduler.schedule({
|
|
892
|
-
resourceId: 'my-workflow',
|
|
893
|
-
triggerAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour from now
|
|
894
|
-
input: { ... }
|
|
895
|
-
})
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
**Task Scheduler UI -- three types.** Walk the user through Command Center -> Task Scheduler:
|
|
899
|
-
|
|
900
|
-
- **Recurring** -- cron-based, runs on a schedule. Standard cron syntax: `0 9 * * 1-5` (9am
|
|
901
|
-
weekdays). The platform interprets cron in UTC.
|
|
902
|
-
- **Relative** -- fires N minutes/hours/days after a trigger event (e.g., "24 hours after a
|
|
903
|
-
deal enters a stage").
|
|
904
|
-
- **Absolute** -- fires at a specific UTC datetime (one-shot, not recurring).
|
|
905
|
-
|
|
906
|
-
**Creating a schedule.** Show the Task Scheduler create flow: select resource, choose type,
|
|
907
|
-
configure the trigger, set input. Schedules appear as `workflow_schedules` rows in the DB -- the
|
|
908
|
-
`elevasis-sdk executions` command shows executions triggered by schedules alongside manual ones.
|
|
909
|
-
|
|
910
|
-
**Verification:** Ask: "What time zone does the platform use for cron expressions?" (UTC.)
|
|
911
|
-
|
|
912
|
-
**Completion signal:** Mark `[ ] 13 Schedules` as `[x] YYYY-MM-DD` in
|
|
913
|
-
`.claude/memory/tutorial-progress.md`.
|
|
914
|
-
|
|
915
|
-
---
|
|
916
|
-
|
|
917
|
-
### Item 14: Notifications + integrations
|
|
918
|
-
|
|
919
|
-
**Goal:** The user uses the `notifications` and `email` singletons and wires a real integration
|
|
920
|
-
adapter using a credential from the project's identity.
|
|
921
|
-
|
|
922
|
-
**Estimated time:** 25 min
|
|
923
|
-
|
|
924
|
-
**Files referenced:** `.claude/skills/elevasis/SKILL.md` (credentials section),
|
|
925
|
-
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
926
|
-
|
|
927
|
-
**Flow:**
|
|
928
|
-
|
|
929
|
-
**`notifications` singleton.** Sends in-app notifications visible in Command Center:
|
|
930
|
-
|
|
931
|
-
```typescript
|
|
932
|
-
import { notifications } from '@elevasis/sdk/worker'
|
|
933
|
-
|
|
934
|
-
await notifications.send({
|
|
935
|
-
title: 'Campaign complete',
|
|
936
|
-
message: `Processed ${count} leads.`,
|
|
937
|
-
level: 'info' // 'info' | 'warning' | 'error'
|
|
938
|
-
})
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
**`email` singleton.** Sends transactional email through the platform's configured mail provider:
|
|
942
|
-
|
|
943
|
-
```typescript
|
|
944
|
-
import { email } from '@elevasis/sdk/worker'
|
|
945
|
-
|
|
946
|
-
await email.send({
|
|
947
|
-
to: 'user@example.com',
|
|
948
|
-
subject: 'Your report is ready',
|
|
949
|
-
body: 'Here is a summary...'
|
|
950
|
-
})
|
|
951
|
-
```
|
|
952
|
-
|
|
953
|
-
**Real adapter integration.** Read `core/config/organization-model.ts` and find the relevant
|
|
954
|
-
OM Resources descriptor or project knowledge node for the integration. Use the documented
|
|
955
|
-
credential name as the constructor argument:
|
|
956
|
-
|
|
957
|
-
```typescript
|
|
958
|
-
import { createAttioAdapter } from '@elevasis/sdk/worker'
|
|
959
|
-
const attio = createAttioAdapter('acme-attio')
|
|
960
|
-
```
|
|
961
|
-
|
|
962
|
-
Walk the user through the end-to-end: create the credential in Command Center, reference it in
|
|
963
|
-
the adapter constructor, deploy, execute a test run, check that the integration call succeeds.
|
|
964
|
-
|
|
965
|
-
**Common error.** If the credential is missing or misspelled, the adapter throws
|
|
966
|
-
`PlatformToolError: credential not found`. Check the exact name in Command Center.
|
|
967
|
-
|
|
968
|
-
**Verification:** A test execution that calls the integration adapter completes without a
|
|
969
|
-
`PlatformToolError`.
|
|
970
|
-
|
|
971
|
-
**Completion signal:** Mark `[ ] 14 Notifications + integrations` as `[x] YYYY-MM-DD` in
|
|
972
|
-
`.claude/memory/tutorial-progress.md`.
|
|
973
|
-
|
|
974
|
-
---
|
|
975
|
-
|
|
976
|
-
### Item 15: Error handling
|
|
977
|
-
|
|
978
|
-
**Goal:** The user knows the three error types, uses `context.logger`, and implements a
|
|
979
|
-
try/catch with retryable vs permanent error distinction.
|
|
980
|
-
|
|
981
|
-
**Estimated time:** 20 min
|
|
982
|
-
|
|
983
|
-
**Files referenced:** `.claude/rules/error-handling.md`,
|
|
984
|
-
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
985
|
-
|
|
986
|
-
**Flow:**
|
|
987
|
-
|
|
988
|
-
Read `.claude/rules/error-handling.md` before starting. Then walk through each error type:
|
|
989
|
-
|
|
990
|
-
**`ExecutionError`.** Base class for workflow/step failures. Throw to halt an execution with a
|
|
991
|
-
user-readable message:
|
|
992
|
-
|
|
993
|
-
```typescript
|
|
994
|
-
import { ExecutionError } from '@elevasis/sdk'
|
|
995
|
-
|
|
996
|
-
throw new ExecutionError('Payment processing failed', {
|
|
997
|
-
context: { invoiceId: input.invoiceId }
|
|
998
|
-
})
|
|
999
|
-
```
|
|
1000
|
-
|
|
1001
|
-
**`PlatformToolError`.** Thrown by `platform.call()` and typed adapters when an integration
|
|
1002
|
-
call fails. Always check `err.retryable`:
|
|
1003
|
-
|
|
1004
|
-
```typescript
|
|
1005
|
-
import { PlatformToolError } from '@elevasis/sdk'
|
|
1006
|
-
|
|
1007
|
-
try {
|
|
1008
|
-
await attio.createRecord({ ... })
|
|
1009
|
-
} catch (err) {
|
|
1010
|
-
if (err instanceof PlatformToolError) {
|
|
1011
|
-
if (err.retryable) {
|
|
1012
|
-
context.logger.warn(`Transient error (rate limit or timeout): ${err.message}`)
|
|
1013
|
-
// safe to retry or re-throw for the caller to handle
|
|
1014
|
-
} else {
|
|
1015
|
-
throw new ExecutionError(`Permanent integration error: ${err.message}`)
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
throw err
|
|
1019
|
-
}
|
|
1020
|
-
```
|
|
1021
|
-
|
|
1022
|
-
**`ToolingError`.** Internal SDK infrastructure error. Treat as non-retryable.
|
|
1023
|
-
|
|
1024
|
-
**`context.logger`.** Use it for all logging inside handlers. `console.log` is not captured by
|
|
1025
|
-
the platform worker runtime. Log levels: `debug`, `info`, `warn`, `error`. The error message in
|
|
1026
|
-
an unhandled throw surfaces directly to CLI output and Command Center -- write it for humans.
|
|
1027
|
-
|
|
1028
|
-
**Common retryable codes.** `rate_limit_exceeded` and `timeout_error` are retryable.
|
|
1029
|
-
`credentials_invalid` and `validation_error` are not.
|
|
1030
|
-
|
|
1031
|
-
**Verification:** Demonstrate a handler that catches a `PlatformToolError`, checks `retryable`,
|
|
1032
|
-
logs appropriately, and re-throws as an `ExecutionError` with a clear message.
|
|
1033
|
-
|
|
1034
|
-
**Completion signal:** Mark `[ ] 15 Error handling` as `[x] YYYY-MM-DD` in
|
|
1035
|
-
`.claude/memory/tutorial-progress.md`.
|
|
1036
|
-
|
|
1037
|
-
---
|
|
1038
|
-
|
|
1039
|
-
### Item 16: LLM and agents
|
|
1040
|
-
|
|
1041
|
-
**Goal:** The user calls `llm.generate()` with structured output (Zod -> JSON Schema -> typed
|
|
1042
|
-
result) and understands when to use an agent definition vs a workflow definition.
|
|
1043
|
-
|
|
1044
|
-
**Estimated time:** 25 min
|
|
1045
|
-
|
|
1046
|
-
**Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`,
|
|
1047
|
-
`apps/docs/content/docs/sdk/concepts.mdx` (workflow vs agent section)
|
|
1048
|
-
|
|
1049
|
-
**Flow:**
|
|
1050
|
-
|
|
1051
|
-
**`llm.generate()` with structured output.** The `llm` singleton from `@elevasis/sdk/worker`
|
|
1052
|
-
accepts a Zod output schema and returns a typed result:
|
|
1053
|
-
|
|
1054
|
-
```typescript
|
|
1055
|
-
import { llm } from '@elevasis/sdk/worker'
|
|
1056
|
-
import { z } from 'zod'
|
|
1057
|
-
|
|
1058
|
-
const classificationSchema = z.object({
|
|
1059
|
-
category: z.enum(['support', 'billing', 'feature-request', 'other']),
|
|
1060
|
-
confidence: z.number().min(0).max(1),
|
|
1061
|
-
summary: z.string()
|
|
1062
|
-
})
|
|
1063
|
-
|
|
1064
|
-
const result = await llm.generate({
|
|
1065
|
-
prompt: `Classify this ticket: ${input.ticketBody}`,
|
|
1066
|
-
outputSchema: classificationSchema,
|
|
1067
|
-
model: 'gpt-4o',
|
|
1068
|
-
temperature: 0.2
|
|
1069
|
-
})
|
|
1070
|
-
|
|
1071
|
-
// result is typed as z.infer<typeof classificationSchema>
|
|
1072
|
-
context.logger.info(`Classified as: ${result.category} (${result.confidence})`)
|
|
1073
|
-
```
|
|
1074
|
-
|
|
1075
|
-
The SDK converts the Zod schema to JSON Schema internally. The LLM is instructed to return JSON
|
|
1076
|
-
matching that shape. The result is validated and returned as the inferred TypeScript type.
|
|
1077
|
-
|
|
1078
|
-
**Agent definition vs workflow definition.** Read the glossary entries from
|
|
1079
|
-
`apps/docs/content/docs/sdk/concepts.mdx`:
|
|
1080
|
-
|
|
1081
|
-
- **Workflow** -- deterministic, step-by-step, 300s timeout. Use when inputs and outputs are
|
|
1082
|
-
known and the process is predefined.
|
|
1083
|
-
- **Agent** -- autonomous, tool-calling, 600s timeout (configurable). Use when the agent needs
|
|
1084
|
-
to make decisions, call tools iteratively, or navigate ambiguous inputs.
|
|
1085
|
-
|
|
1086
|
-
When to use an agent: the task requires the LLM to decide WHICH tools to call and in WHAT order
|
|
1087
|
-
based on what it finds (not predetermined routing). When to use a workflow: the sequence of steps
|
|
1088
|
-
is predetermined, even if LLM judgment is involved inside individual steps.
|
|
1089
|
-
|
|
1090
|
-
**Model selection.** `gpt-4o` for complex reasoning, `gpt-4o-mini` for speed-sensitive tasks,
|
|
1091
|
-
`o3-mini` for multi-step reasoning chains. Temperature: `0.0` for deterministic classification,
|
|
1092
|
-
`0.2-0.4` for structured extraction, `0.7+` for creative generation.
|
|
1093
|
-
|
|
1094
|
-
**Verification:** A workflow step calls `llm.generate()` with a Zod schema and returns a typed
|
|
1095
|
-
value. The type system confirms the result matches the schema without a cast.
|
|
1096
|
-
|
|
1097
|
-
**Completion signal:** Mark `[ ] 16 LLM and agents` as `[x] YYYY-MM-DD` in
|
|
1098
|
-
`.claude/memory/tutorial-progress.md`.
|
|
1099
|
-
|
|
1100
|
-
---
|
|
1101
|
-
|
|
1102
|
-
### Item 17: Composition (execution.trigger)
|
|
1103
|
-
|
|
1104
|
-
**Goal:** The user chains two workflows using `execution.trigger()`, declares the relationship in
|
|
1105
|
-
`DeploymentSpec`, and can find the edge in Command View.
|
|
1106
|
-
|
|
1107
|
-
**Estimated time:** 20 min
|
|
1108
|
-
|
|
1109
|
-
**Files referenced:** `operations/src/index.ts`,
|
|
1110
|
-
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
1111
|
-
|
|
1112
|
-
**Flow:**
|
|
1113
|
-
|
|
1114
|
-
**`execution.trigger()`.** Call from inside a step handler to launch another workflow
|
|
1115
|
-
asynchronously:
|
|
1116
|
-
|
|
1117
|
-
```typescript
|
|
1118
|
-
import { execution } from '@elevasis/sdk/worker'
|
|
1119
|
-
|
|
1120
|
-
const { executionId } = await execution.trigger({
|
|
1121
|
-
resourceId: 'downstream-workflow',
|
|
1122
|
-
input: { ...outputFromCurrentStep }
|
|
1123
|
-
})
|
|
1124
|
-
|
|
1125
|
-
context.logger.info(`Triggered downstream-workflow: ${executionId}`)
|
|
1126
|
-
```
|
|
1127
|
-
|
|
1128
|
-
The calling step does not wait for the downstream workflow to complete. If you need the result,
|
|
1129
|
-
use async polling or chain via HITL.
|
|
1130
|
-
|
|
1131
|
-
**Declare the relationship in `DeploymentSpec`.** In `operations/src/index.ts`, add a
|
|
1132
|
-
`relationships` entry so the platform knows this workflow triggers another:
|
|
1133
|
-
|
|
1134
|
-
```typescript
|
|
1135
|
-
const org: DeploymentSpec = {
|
|
1136
|
-
version: '0.1.0',
|
|
1137
|
-
workflows: [...],
|
|
1138
|
-
agents: [...],
|
|
1139
|
-
relationships: [
|
|
1140
|
-
{
|
|
1141
|
-
source: 'upstream-workflow',
|
|
1142
|
-
target: 'downstream-workflow',
|
|
1143
|
-
type: 'triggers'
|
|
1144
|
-
}
|
|
1145
|
-
]
|
|
1146
|
-
}
|
|
1147
|
-
```
|
|
1148
|
-
|
|
1149
|
-
This metadata populates the Command View graph. Without it, the edge is not visible in the UI
|
|
1150
|
-
even if the trigger call works.
|
|
1151
|
-
|
|
1152
|
-
**Command View.** Show the user how to find Command View in Command Center -> Operations.
|
|
1153
|
-
Deployed relationships appear as directed edges between resource nodes. This is the primary
|
|
1154
|
-
debugging surface for understanding how automations are connected.
|
|
1155
|
-
|
|
1156
|
-
**Verification:** Deploy both workflows with the relationship declared, then view the edge in
|
|
1157
|
-
Command View.
|
|
1158
|
-
|
|
1159
|
-
**Completion signal:** Mark `[ ] 17 Composition` as `[x] YYYY-MM-DD` in
|
|
1160
|
-
`.claude/memory/tutorial-progress.md`.
|
|
1161
|
-
|
|
1162
|
-
---
|
|
1163
|
-
|
|
1164
|
-
## SECTION E -- Power User
|
|
1165
|
-
|
|
1166
|
-
### Item 18: Rules, memory, scaffold registry
|
|
1167
|
-
|
|
1168
|
-
**Goal:** The user can navigate the `.claude/rules/` and `.claude/memory/` directories and
|
|
1169
|
-
understands the scaffold registry's purpose.
|
|
1170
|
-
|
|
1171
|
-
**Estimated time:** 20 min
|
|
1172
|
-
|
|
1173
|
-
**Files referenced:** `.claude/rules/` (all files), `.claude/memory/` (layout),
|
|
1174
|
-
`.claude/scaffold-registry.yml` (reference, monorepo-side concept)
|
|
1175
|
-
|
|
1176
|
-
**Flow:**
|
|
1177
|
-
|
|
1178
|
-
**Rules directory.** List the current rule files in `.claude/rules/`. Each rule is automatically
|
|
1179
|
-
loaded by the agent on every session based on its `paths:` frontmatter (path globs that trigger
|
|
1180
|
-
auto-load). Key files:
|
|
1181
|
-
|
|
1182
|
-
- `agent-start-here.md` -- always-loaded canonical first-read; task-class routing and boundary
|
|
1183
|
-
resolution
|
|
1184
|
-
- `vibe.md` -- always-loaded ambient intent classifier
|
|
1185
|
-
- `organization-os.md` -- loaded when touching org model, System access, Action routing, or entity work
|
|
1186
|
-
- `deployment.md` -- loaded when deploying resources
|
|
1187
|
-
- `error-handling.md` -- loaded when authoring handlers with error concerns
|
|
1188
|
-
- `execution.md` -- loaded when reasoning about the runtime model
|
|
1189
|
-
- `observability.md` -- loaded when using `context.logger` or inspecting executions
|
|
1190
|
-
- `operations.md` -- loaded when touching `operations/src/`
|
|
1191
|
-
- `platform.md` -- loaded when importing from `@elevasis/sdk`
|
|
1192
|
-
- `shared-types.md` -- loaded when touching `core/types/`
|
|
1193
|
-
- `task-tracking.md` -- loaded when managing project tasks
|
|
1194
|
-
- `active-change-index.md` -- flags areas under active architecture change; trust this over
|
|
1195
|
-
stable scaffold docs for those areas
|
|
1196
|
-
|
|
1197
|
-
To add a new rule: create `.claude/rules/<domain>.md` with `paths:` frontmatter matching the
|
|
1198
|
-
glob of files it applies to. The rules system discovers it automatically -- no registration step.
|
|
1199
|
-
|
|
1200
|
-
**Memory directory.** List `.claude/memory/`. Common files:
|
|
1201
|
-
|
|
1202
|
-
- `profile.md` -- track choice and tone implications (written by this tutorial)
|
|
1203
|
-
- `tutorial-progress.md` -- per-item progress markers for both tracks
|
|
1204
|
-
|
|
1205
|
-
Memory files are readable by the agent at session start. The `profile.md` file is particularly
|
|
1206
|
-
important: it sets the project-wide tone for all sessions after the tutorial track is selected.
|
|
1207
|
-
|
|
1208
|
-
**Scaffold registry (monorepo-side concept).** `.claude/scaffold-registry.yml` is the
|
|
1209
|
-
monorepo's source-of-truth for scaffold dependencies -- it maps source paths to generated or
|
|
1210
|
-
manual scaffolds that depend on them. It drives the PostToolUse reminder hook and `/work handoff`
|
|
1211
|
-
preflight in the monorepo. In an external project you are a consumer, not a contributor, of that
|
|
1212
|
-
registry. The relevant thing to know: when the SDK ships a template update, the registry controls
|
|
1213
|
-
which files are Tier 1 (always replaced on sync), Tier 2 (merge-aware), and Tier 3 (never
|
|
1214
|
-
touched). Item 19 covers this in detail.
|
|
1215
|
-
|
|
1216
|
-
**Verification:** Ask: "How does the agent know to load `deployment.md` when you run a deploy?"
|
|
1217
|
-
(Answer: the `paths:` frontmatter in the rule file matches `operations/` file globs and deploy
|
|
1218
|
-
command patterns.)
|
|
1219
|
-
|
|
1220
|
-
**Completion signal:** Mark `[ ] 18 Rules, memory, scaffold registry` as `[x] YYYY-MM-DD` in
|
|
1221
|
-
`.claude/memory/tutorial-progress.md`.
|
|
1222
|
-
|
|
1223
|
-
---
|
|
1224
|
-
|
|
1225
|
-
### Item 19: Template lifecycle and /git-sync
|
|
1226
|
-
|
|
1227
|
-
**Goal:** The user understands the three-tier file classification, runs `/git-sync`, and knows
|
|
1228
|
-
what manual reconciliation is required after a template update.
|
|
1229
|
-
|
|
1230
|
-
**Estimated time:** 20 min
|
|
1231
|
-
|
|
1232
|
-
**Files referenced:** `.claude/skills/git-sync/SKILL.md`, `.claude/sync-notes/` (directory)
|
|
1233
|
-
|
|
1234
|
-
**Flow:**
|
|
1235
|
-
|
|
1236
|
-
Tell the user:
|
|
1237
|
-
|
|
1238
|
-
> "The template you scaffolded from is versioned. When a new SDK version ships, the template
|
|
1239
|
-
> updates. `/git-sync` pulls those changes and tells you what changed. You decide what to do
|
|
1240
|
-
> with them."
|
|
1241
|
-
|
|
1242
|
-
**Three-tier file classification.** Walk through the tiers:
|
|
1243
|
-
|
|
1244
|
-
- **Tier 1 (always replaced)** -- SDK-owned files that ship in a known correct state:
|
|
1245
|
-
`.claude/skills/*/SKILL.md` (except project-authored skills), `.claude/hooks/`, `CLAUDE.md`
|
|
1246
|
-
sections that are platform-managed. These are overwritten on every template sync. Do not put
|
|
1247
|
-
custom logic in them; instead use Tier 3 files for project-specific config.
|
|
1248
|
-
- **Tier 2 (merge-aware)** -- Files where the template ships defaults but the project may have
|
|
1249
|
-
customized: `core/config/organization-model.ts`, `.claude/rules/active-change-index.md`,
|
|
1250
|
-
vibe classifier threshold. The sync does not blindly overwrite -- the agent surfaces the diff
|
|
1251
|
-
and the user decides what to keep.
|
|
1252
|
-
- **Tier 3 (never touched)** -- Project-owned files the template never overwrites: project
|
|
1253
|
-
source code in `operations/src/`, `ui/src/`, all `core/types/` files, `core/config/extensions/`,
|
|
1254
|
-
custom rules added to `.claude/rules/`, everything in `.claude/memory/`.
|
|
1255
|
-
|
|
1256
|
-
**Running `/git-sync`.** Read `.claude/skills/git-sync/SKILL.md` and walk the user through each
|
|
1257
|
-
step:
|
|
1258
|
-
|
|
1259
|
-
1. Check for uncommitted changes (`git status --short`). Stop if dirty.
|
|
1260
|
-
2. Snapshot current sync-note filenames before pulling.
|
|
1261
|
-
3. `git pull --rebase` -- stop and report conflicts; do not auto-resolve.
|
|
1262
|
-
4. Detect dependency baseline changes; run `pnpm install` if `package.json` or
|
|
1263
|
-
`pnpm-lock.yaml` changed.
|
|
1264
|
-
5. Run baseline verification: `pnpm -C ui check-types`, `pnpm -C ui build`,
|
|
1265
|
-
`pnpm -C operations check`, `pnpm -C operations check-types`.
|
|
1266
|
-
6. Surface newly introduced sync notes from `.claude/sync-notes/`. Each note has required
|
|
1267
|
-
actions and a verification section.
|
|
1268
|
-
7. Stop. `/git-sync` does not auto-reconcile. Manual follow-up is explicit.
|
|
1269
|
-
|
|
1270
|
-
**Sync notes contract.** Files in `.claude/sync-notes/` named `YYYY-MM-DD-<slug>.md` are
|
|
1271
|
-
operative release guidance. They use fixed headings: `## Why this note exists`,
|
|
1272
|
-
`## Applies to`, `## Required actions`, `## Verification`, `## Not handled by /git-sync`.
|
|
1273
|
-
Never edit operative note filenames after they ship -- they are append-only.
|
|
1274
|
-
|
|
1275
|
-
**Verification:** The user knows which of their own files are Tier 3 (never touched) and can
|
|
1276
|
-
explain why `/git-sync` stops before reconciliation rather than auto-applying diffs.
|
|
1277
|
-
|
|
1278
|
-
**Completion signal:** Mark `[ ] 19 Template lifecycle and /git-sync` as `[x] YYYY-MM-DD` in
|
|
1279
|
-
`.claude/memory/tutorial-progress.md`.
|
|
1280
|
-
|
|
1281
|
-
---
|
|
1282
|
-
|
|
1283
|
-
## After All 19 Items Complete
|
|
1284
|
-
|
|
1285
|
-
When all 19 items are marked `[x]` in `.claude/memory/tutorial-progress.md`, deliver the
|
|
1286
|
-
following closing script:
|
|
1287
|
-
|
|
1288
|
-
> "You've completed the technical track. Here's where to go from here:
|
|
1289
|
-
>
|
|
1290
|
-
> - **Build real automations.** You now have the full surface: workflows, schemas, HITL, schedules,
|
|
1291
|
-
> LLM integration, error handling, and composition. Start with the domain most relevant to your
|
|
1292
|
-
> project and use `/project create` to track the work.
|
|
1293
|
-
> - **SDK reference scaffold.** `node_modules/@elevasis/sdk/reference/scaffold/index.mdx` is the
|
|
1294
|
-
> canonical recipe index for anything you want to build: adding a System or UI feature, extending lead-gen,
|
|
1295
|
-
> customizing CRM actions, authoring agents. Read it whenever you are starting something new.
|
|
1296
|
-
> - **Explore the codebase.** `/explore` is your tool for open-ended questions about how things
|
|
1297
|
-
> are wired. Use it before making changes in unfamiliar parts of the project.
|
|
1298
|
-
> - **Keep the org model current.** Run `/
|
|
1299
|
-
> new offerings, new customer segments, new goals. The model is the agent's vocabulary for
|
|
1300
|
-
> your project; an accurate model means better agent output.
|
|
1301
|
-
> - **Stay current with template updates.** When a new SDK version ships, run `/git-sync` to pull
|
|
1302
|
-
> it and read the sync notes. They tell you exactly what changed and what manual follow-up is
|
|
1303
|
-
> needed."
|
|
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 /om ceremony -- identity, customers, [ ]
|
|
33
|
+
offerings via the layered flow
|
|
34
|
+
10 Systems, actions, 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 System or UI 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., `/om systems`). 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
|
+
`/om` 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 `/om`
|
|
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
|
+
- `/om` -- 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 `/om`.
|
|
222
|
+
- **Toggle** -- "Turn on the lead-gen system." Agent delegates to `/om systems`.
|
|
223
|
+
|
|
224
|
+
**Important behavioral rules.** Codify and Toggle delegate immediately to `/om` -- the
|
|
225
|
+
vibe layer detects intent but does NOT run the ceremony itself. The ceremony (snapshot, propose,
|
|
226
|
+
confirm, write, validate, rollback) belongs to `/om`. 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 `/om systems` 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 SDK CLI CWD
|
|
574
|
+
guidance in the deployment rule.
|
|
575
|
+
|
|
576
|
+
**Estimated time:** 15 min
|
|
577
|
+
|
|
578
|
+
**Files referenced:** `.claude/rules/deployment.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
|
+
**SDK CLI CWD guidance.** Read `.claude/rules/deployment.md`. Two critical rules:
|
|
610
|
+
|
|
611
|
+
1. The `--prod` flag belongs to the `deploy` command:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
# Correct
|
|
615
|
+
pnpm elevasis-sdk deploy --prod
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
The `pnpm -C operations deploy:prod` script in `package.json` handles this correctly.
|
|
619
|
+
Prefer the script over a raw CLI call for production deploys.
|
|
620
|
+
|
|
621
|
+
2. Never use bare `cd <dir> && elevasis-sdk ...` -- use a subshell `(cd <dir> && ...)` or
|
|
622
|
+
`pnpm -C <dir> ...` to avoid CWD drift that causes env resolution failures.
|
|
623
|
+
|
|
624
|
+
**Standard pre-production checklist.** Walk the user through:
|
|
625
|
+
|
|
626
|
+
```
|
|
627
|
+
1. pnpm -C operations check -- validate resources
|
|
628
|
+
2. pnpm -C operations check-types -- TypeScript type-check
|
|
629
|
+
3. pnpm elevasis-sdk exec <id> -i '...' -- test in dev
|
|
630
|
+
4. pnpm -C operations deploy:prod -- ship to prod
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**Verification:** The user can explain where `--prod` must be placed and why.
|
|
634
|
+
|
|
635
|
+
**Completion signal:** Mark `[ ] 8 Going to production` as `[x] YYYY-MM-DD` in
|
|
636
|
+
`.claude/memory/tutorial-progress.md`.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## SECTION C -- The Organization Model
|
|
641
|
+
|
|
642
|
+
### Item 9: /om ceremony -- identity, customers, offerings via the layered flow
|
|
643
|
+
|
|
644
|
+
**Goal:** The user runs `/om` and understands the six-step write ceremony, why direct
|
|
645
|
+
edits to `core/config/organization-model.ts` are blocked, and the layered flow order.
|
|
646
|
+
|
|
647
|
+
**Estimated time:** 25 min
|
|
648
|
+
|
|
649
|
+
**Files referenced:** `.claude/skills/om/SKILL.md`, `core/config/organization-model.ts`,
|
|
650
|
+
`.claude/rules/organization-os.md`
|
|
651
|
+
|
|
652
|
+
**Flow:**
|
|
653
|
+
|
|
654
|
+
Tell the user:
|
|
655
|
+
|
|
656
|
+
> "The organization model is the semantic contract layer that defines your business reality:
|
|
657
|
+
> who you are, who your customers are, what you offer, your team, and your goals. All edits go
|
|
658
|
+
> through `/om` -- not direct file edits."
|
|
659
|
+
|
|
660
|
+
**Why direct edits are blocked.** Read `core/config/organization-model.ts`. The file calls
|
|
661
|
+
`resolveOrganizationModel()` which runs Zod cross-reference validation: every customer segment
|
|
662
|
+
referenced in offerings must exist in `customers`, every resource mapping must reference a valid
|
|
663
|
+
system ID, and so on. A direct edit that passes TypeScript can still fail the cross-ref check,
|
|
664
|
+
leaving the project in a broken state. The `/om` ceremony runs both checks and rolls back
|
|
665
|
+
on failure.
|
|
666
|
+
|
|
667
|
+
**The six-step ceremony.** Walk through `.claude/skills/om/SKILL.md`:
|
|
668
|
+
|
|
669
|
+
1. **Snapshot** -- reads `core/config/organization-model.ts` into memory before any edit
|
|
670
|
+
2. **Propose** -- shows the diff and the proposed new value alongside current value
|
|
671
|
+
3. **Confirm** -- pauses for explicit user confirmation; never writes without a clear yes
|
|
672
|
+
4. **Write** -- applies the edit
|
|
673
|
+
5. **Validate** -- runs `pnpm -C operations check-types` (TypeScript) then `pnpm -C operations
|
|
674
|
+
check` (Zod parse via `resolveOrganizationModel()`)
|
|
675
|
+
6. **Rollback** -- if either check fails, restores from the Step 1 snapshot
|
|
676
|
+
|
|
677
|
+
**The layered flow.** Run `/om` with no argument to walk through the full flow:
|
|
678
|
+
identity -> customers -> offerings -> roles -> goals -> techStack. The agent reads the current
|
|
679
|
+
state of each domain, proposes any missing or incomplete fields, and asks for confirmation before
|
|
680
|
+
writing.
|
|
681
|
+
|
|
682
|
+
Or target a specific domain:
|
|
683
|
+
|
|
684
|
+
- `/om identity` -- legal name, mission/vision, industry, geography, timezone
|
|
685
|
+
- `/om customers` -- customer segments with jobs-to-be-done, pains, gains
|
|
686
|
+
- `/om offerings` -- products and services with pricing model and segment references
|
|
687
|
+
- `/om roles` -- role chart with responsibilities and reporting lines
|
|
688
|
+
- `/om goals` -- organizational goals with period and measurable outcomes
|
|
689
|
+
- `/om techStack` -- external SaaS and integration context tied to OM Resources descriptors or knowledge nodes
|
|
690
|
+
|
|
691
|
+
**Demo.** Ask the user to run `/om identity`. Walk through the ceremony together -- read
|
|
692
|
+
the current state, propose an edit, confirm, watch it validate.
|
|
693
|
+
|
|
694
|
+
**Verification:** The user can describe what `resolveOrganizationModel()` does and why the
|
|
695
|
+
rollback step exists.
|
|
696
|
+
|
|
697
|
+
**Completion signal:** Mark `[ ] 9 /om ceremony` as `[x] YYYY-MM-DD` in
|
|
698
|
+
`.claude/memory/tutorial-progress.md`.
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
### Item 10: Systems, actions, and labels
|
|
703
|
+
|
|
704
|
+
**Goal:** The user can enable/disable a System via `/om systems`, understand Actions as
|
|
705
|
+
invokable operations, and rename a display label via `/om labels`.
|
|
706
|
+
|
|
707
|
+
**Estimated time:** 15 min
|
|
708
|
+
|
|
709
|
+
**Files referenced:** `.claude/skills/om/operations/features.md` (legacy compatibility),
|
|
710
|
+
`.claude/skills/om/operations/labels.md`, `core/config/organization-model.ts`
|
|
711
|
+
|
|
712
|
+
**Flow:**
|
|
713
|
+
|
|
714
|
+
**Systems.** The org model uses Systems for availability, routing, ownership, navigation
|
|
715
|
+
grouping, and knowledge mounts. Toggle availability by running:
|
|
716
|
+
|
|
717
|
+
```
|
|
718
|
+
/om systems
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
The ceremony proposes the change (e.g., `seo: false -> true`), asks for confirmation, writes,
|
|
722
|
+
and validates. Older scaffold recipes and UI packages may still say "feature"; translate that to
|
|
723
|
+
System when discussing Organization OS availability or routing.
|
|
724
|
+
|
|
725
|
+
System toggles affect the UI shell: a disabled System removes its nav entry and routes from
|
|
726
|
+
the shell. Actions are the invokable operations owned or exposed by a System.
|
|
727
|
+
|
|
728
|
+
**Labels.** The `labels` domain controls display strings on enum entries -- CRM pipeline stages,
|
|
729
|
+
lead-gen lifecycle statuses, project statuses, and so on. Rename by running:
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
/om labels
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
The agent reads the current enum entries (each carries an `id`, `label`, and `semanticClass`
|
|
736
|
+
field), proposes a label rename, and writes through the ceremony. The `semanticClass` field
|
|
737
|
+
should not change -- it maps to semantic behavior like `blocked`, `in_progress`, `complete`.
|
|
738
|
+
|
|
739
|
+
Show an example: renaming the `discovery` CRM stage to `qualification`.
|
|
740
|
+
|
|
741
|
+
**Verification:** Ask: "What is the difference between a System toggle and a label rename?"
|
|
742
|
+
(Toggle controls visibility/routing. Label rename changes display text on an existing enum
|
|
743
|
+
entry without changing its semantic ID or behavior.)
|
|
744
|
+
|
|
745
|
+
**Completion signal:** Mark `[ ] 10 Systems, actions, and labels` as `[x] YYYY-MM-DD` in
|
|
746
|
+
`.claude/memory/tutorial-progress.md`.
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
### Item 11: Entity extensions -- BaseProject, BaseDeal
|
|
751
|
+
|
|
752
|
+
**Goal:** The user reads the entity extension pattern in `core/types/entities.ts` and understands
|
|
753
|
+
how to add project-specific metadata fields to base entities.
|
|
754
|
+
|
|
755
|
+
**Estimated time:** 20 min
|
|
756
|
+
|
|
757
|
+
**Files referenced:** `core/types/entities.ts`, `core/config/organization-model.ts`
|
|
758
|
+
(for the OM Resources descriptor context)
|
|
759
|
+
|
|
760
|
+
**Flow:**
|
|
761
|
+
|
|
762
|
+
**Read the demo extension.** Open `core/types/entities.ts`. Walk through both patterns it
|
|
763
|
+
demonstrates:
|
|
764
|
+
|
|
765
|
+
**Pattern 1: extend with metadata.**
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
import { BaseProjectSchema, type BaseProject } from '@elevasis/core/entities'
|
|
769
|
+
|
|
770
|
+
export const ProjectMetaSchema = z.object({
|
|
771
|
+
budget: z.number().nonnegative(),
|
|
772
|
+
clientPriority: z.enum(['low', 'medium', 'high'])
|
|
773
|
+
})
|
|
774
|
+
export type ProjectMeta = z.infer<typeof ProjectMetaSchema>
|
|
775
|
+
|
|
776
|
+
export const ProjectSchema = BaseProjectSchema.extend({ metadata: ProjectMetaSchema })
|
|
777
|
+
export type Project = BaseProject<ProjectMeta>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
The base types from `@elevasis/core/entities` are generic over a `\<TMeta>` slot. You pass your
|
|
781
|
+
metadata type to specialize the base. `BaseProjectSchema.extend(...)` is the Zod idiom; the
|
|
782
|
+
TypeScript type is `BaseProject<ProjectMeta>`.
|
|
783
|
+
|
|
784
|
+
**Pattern 2: use a base entity unchanged.**
|
|
785
|
+
|
|
786
|
+
```typescript
|
|
787
|
+
export const DealSchema = BaseDealSchema
|
|
788
|
+
export type Deal = BaseDeal
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
When you have no project-specific fields, re-export the base. This keeps the import path
|
|
792
|
+
consistent (`@core/types` everywhere) while avoiding redundant extension.
|
|
793
|
+
|
|
794
|
+
**Using entity types in workflows.** When authoring a workflow that receives or returns a
|
|
795
|
+
Project or Deal, reference these types in the input/output schemas rather than re-declaring the
|
|
796
|
+
shape. This ensures the workflow contract stays in sync with the entity definition as it evolves.
|
|
797
|
+
|
|
798
|
+
**Reference for all base shapes.** `@elevasis/core/entities` exports: `BaseProject`,
|
|
799
|
+
`BaseProjectSchema`, `BaseProjectInput`, `BaseMilestone`, `BaseMilestoneSchema`,
|
|
800
|
+
`BaseTask`, `BaseTaskSchema`, `BaseDeal`, `BaseDealSchema`, `BaseCompany`, `BaseCompanySchema`,
|
|
801
|
+
`BaseContact`, `BaseContactSchema`.
|
|
802
|
+
|
|
803
|
+
**Domain rename note.** In `core/config/organization-model.ts`, the domain config fields were
|
|
804
|
+
renamed in the 2026-04-20 expansion: `crm` -> `sales`, `leadGen` -> `prospecting`,
|
|
805
|
+
`delivery` -> `projects`. Older feature ID constants may still appear in UI package code and
|
|
806
|
+
scaffold recipes; treat them as implementation constants, not live Organization OS primitives.
|
|
807
|
+
|
|
808
|
+
**Verification:** The user can explain when to use Pattern 1 vs Pattern 2 and name two base
|
|
809
|
+
entity types they could extend.
|
|
810
|
+
|
|
811
|
+
**Completion signal:** Mark `[ ] 11 Entity extensions` as `[x] YYYY-MM-DD` in
|
|
812
|
+
`.claude/memory/tutorial-progress.md`.
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
## SECTION D -- Modules (load on demand)
|
|
817
|
+
|
|
818
|
+
Modules in Section D are available any time. No Section B or C prerequisites are enforced. After
|
|
819
|
+
completing a module, redisplay the full menu with updated progress markers.
|
|
820
|
+
|
|
821
|
+
---
|
|
822
|
+
|
|
823
|
+
### Item 12: HITL
|
|
824
|
+
|
|
825
|
+
**Goal:** The user implements an approval gate using `approval.create()` and understands the
|
|
826
|
+
full lifecycle through Command Queue.
|
|
827
|
+
|
|
828
|
+
**Estimated time:** 25 min
|
|
829
|
+
|
|
830
|
+
**Files referenced:** `.claude/rules/error-handling.md`,
|
|
831
|
+
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
832
|
+
|
|
833
|
+
**Flow:**
|
|
834
|
+
|
|
835
|
+
**What HITL is.** Human-in-the-Loop (HITL) pauses a workflow execution and waits for a human
|
|
836
|
+
decision before continuing. The execution is suspended at the approval gate; the platform stores
|
|
837
|
+
the pending state. When a user approves or rejects in Command Center, the execution resumes.
|
|
838
|
+
|
|
839
|
+
**Implementation.** Import `approval` from `@elevasis/sdk/worker` and call it inside a step
|
|
840
|
+
handler:
|
|
841
|
+
|
|
842
|
+
```typescript
|
|
843
|
+
import { approval } from '@elevasis/sdk/worker'
|
|
844
|
+
|
|
845
|
+
const decision = await approval.create({
|
|
846
|
+
title: 'Approve Invoice',
|
|
847
|
+
description: `Invoice #${input.invoiceId} for $${input.amount} ready for approval.`,
|
|
848
|
+
context: { invoiceId: input.invoiceId, amount: input.amount }
|
|
849
|
+
})
|
|
850
|
+
|
|
851
|
+
if (decision.approved) {
|
|
852
|
+
// continue the workflow
|
|
853
|
+
} else {
|
|
854
|
+
// handle rejection
|
|
855
|
+
}
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
**Lifecycle.** The step calls `approval.create()` -> platform suspends the execution -> a
|
|
859
|
+
pending item appears in the Command Queue in Command Center -> user reviews and approves or
|
|
860
|
+
rejects -> execution resumes from the same step with `decision.approved` set.
|
|
861
|
+
|
|
862
|
+
**Variant rule.** Always use `variant: 'light'` (hardcoded by the platform; never pass `variant`
|
|
863
|
+
in the `approval.create()` call -- the field does not exist on the call signature and will cause
|
|
864
|
+
a TypeScript error).
|
|
865
|
+
|
|
866
|
+
**Verify.** Deploy the workflow, execute it, confirm that it appears as pending in Command Center
|
|
867
|
+
Command Queue, approve it, and confirm the execution completes.
|
|
868
|
+
|
|
869
|
+
**Completion signal:** Mark `[ ] 12 HITL` as `[x] YYYY-MM-DD` in
|
|
870
|
+
`.claude/memory/tutorial-progress.md`.
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
### Item 13: Schedules
|
|
875
|
+
|
|
876
|
+
**Goal:** The user understands the three Task Scheduler types (Recurring, Relative, Absolute),
|
|
877
|
+
uses the `scheduler` singleton inside a workflow, and knows the cron syntax the platform accepts.
|
|
878
|
+
|
|
879
|
+
**Estimated time:** 20 min
|
|
880
|
+
|
|
881
|
+
**Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
882
|
+
|
|
883
|
+
**Flow:**
|
|
884
|
+
|
|
885
|
+
**The `scheduler` singleton.** Import from `@elevasis/sdk/worker` to trigger workflow actions
|
|
886
|
+
from within a running step:
|
|
887
|
+
|
|
888
|
+
```typescript
|
|
889
|
+
import { scheduler } from '@elevasis/sdk/worker'
|
|
890
|
+
|
|
891
|
+
await scheduler.schedule({
|
|
892
|
+
resourceId: 'my-workflow',
|
|
893
|
+
triggerAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour from now
|
|
894
|
+
input: { ... }
|
|
895
|
+
})
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
**Task Scheduler UI -- three types.** Walk the user through Command Center -> Task Scheduler:
|
|
899
|
+
|
|
900
|
+
- **Recurring** -- cron-based, runs on a schedule. Standard cron syntax: `0 9 * * 1-5` (9am
|
|
901
|
+
weekdays). The platform interprets cron in UTC.
|
|
902
|
+
- **Relative** -- fires N minutes/hours/days after a trigger event (e.g., "24 hours after a
|
|
903
|
+
deal enters a stage").
|
|
904
|
+
- **Absolute** -- fires at a specific UTC datetime (one-shot, not recurring).
|
|
905
|
+
|
|
906
|
+
**Creating a schedule.** Show the Task Scheduler create flow: select resource, choose type,
|
|
907
|
+
configure the trigger, set input. Schedules appear as `workflow_schedules` rows in the DB -- the
|
|
908
|
+
`elevasis-sdk executions` command shows executions triggered by schedules alongside manual ones.
|
|
909
|
+
|
|
910
|
+
**Verification:** Ask: "What time zone does the platform use for cron expressions?" (UTC.)
|
|
911
|
+
|
|
912
|
+
**Completion signal:** Mark `[ ] 13 Schedules` as `[x] YYYY-MM-DD` in
|
|
913
|
+
`.claude/memory/tutorial-progress.md`.
|
|
914
|
+
|
|
915
|
+
---
|
|
916
|
+
|
|
917
|
+
### Item 14: Notifications + integrations
|
|
918
|
+
|
|
919
|
+
**Goal:** The user uses the `notifications` and `email` singletons and wires a real integration
|
|
920
|
+
adapter using a credential from the project's identity.
|
|
921
|
+
|
|
922
|
+
**Estimated time:** 25 min
|
|
923
|
+
|
|
924
|
+
**Files referenced:** `.claude/skills/elevasis/SKILL.md` (credentials section),
|
|
925
|
+
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
926
|
+
|
|
927
|
+
**Flow:**
|
|
928
|
+
|
|
929
|
+
**`notifications` singleton.** Sends in-app notifications visible in Command Center:
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
import { notifications } from '@elevasis/sdk/worker'
|
|
933
|
+
|
|
934
|
+
await notifications.send({
|
|
935
|
+
title: 'Campaign complete',
|
|
936
|
+
message: `Processed ${count} leads.`,
|
|
937
|
+
level: 'info' // 'info' | 'warning' | 'error'
|
|
938
|
+
})
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**`email` singleton.** Sends transactional email through the platform's configured mail provider:
|
|
942
|
+
|
|
943
|
+
```typescript
|
|
944
|
+
import { email } from '@elevasis/sdk/worker'
|
|
945
|
+
|
|
946
|
+
await email.send({
|
|
947
|
+
to: 'user@example.com',
|
|
948
|
+
subject: 'Your report is ready',
|
|
949
|
+
body: 'Here is a summary...'
|
|
950
|
+
})
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
**Real adapter integration.** Read `core/config/organization-model.ts` and find the relevant
|
|
954
|
+
OM Resources descriptor or project knowledge node for the integration. Use the documented
|
|
955
|
+
credential name as the constructor argument:
|
|
956
|
+
|
|
957
|
+
```typescript
|
|
958
|
+
import { createAttioAdapter } from '@elevasis/sdk/worker'
|
|
959
|
+
const attio = createAttioAdapter('acme-attio')
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
Walk the user through the end-to-end: create the credential in Command Center, reference it in
|
|
963
|
+
the adapter constructor, deploy, execute a test run, check that the integration call succeeds.
|
|
964
|
+
|
|
965
|
+
**Common error.** If the credential is missing or misspelled, the adapter throws
|
|
966
|
+
`PlatformToolError: credential not found`. Check the exact name in Command Center.
|
|
967
|
+
|
|
968
|
+
**Verification:** A test execution that calls the integration adapter completes without a
|
|
969
|
+
`PlatformToolError`.
|
|
970
|
+
|
|
971
|
+
**Completion signal:** Mark `[ ] 14 Notifications + integrations` as `[x] YYYY-MM-DD` in
|
|
972
|
+
`.claude/memory/tutorial-progress.md`.
|
|
973
|
+
|
|
974
|
+
---
|
|
975
|
+
|
|
976
|
+
### Item 15: Error handling
|
|
977
|
+
|
|
978
|
+
**Goal:** The user knows the three error types, uses `context.logger`, and implements a
|
|
979
|
+
try/catch with retryable vs permanent error distinction.
|
|
980
|
+
|
|
981
|
+
**Estimated time:** 20 min
|
|
982
|
+
|
|
983
|
+
**Files referenced:** `.claude/rules/error-handling.md`,
|
|
984
|
+
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
985
|
+
|
|
986
|
+
**Flow:**
|
|
987
|
+
|
|
988
|
+
Read `.claude/rules/error-handling.md` before starting. Then walk through each error type:
|
|
989
|
+
|
|
990
|
+
**`ExecutionError`.** Base class for workflow/step failures. Throw to halt an execution with a
|
|
991
|
+
user-readable message:
|
|
992
|
+
|
|
993
|
+
```typescript
|
|
994
|
+
import { ExecutionError } from '@elevasis/sdk'
|
|
995
|
+
|
|
996
|
+
throw new ExecutionError('Payment processing failed', {
|
|
997
|
+
context: { invoiceId: input.invoiceId }
|
|
998
|
+
})
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
**`PlatformToolError`.** Thrown by `platform.call()` and typed adapters when an integration
|
|
1002
|
+
call fails. Always check `err.retryable`:
|
|
1003
|
+
|
|
1004
|
+
```typescript
|
|
1005
|
+
import { PlatformToolError } from '@elevasis/sdk'
|
|
1006
|
+
|
|
1007
|
+
try {
|
|
1008
|
+
await attio.createRecord({ ... })
|
|
1009
|
+
} catch (err) {
|
|
1010
|
+
if (err instanceof PlatformToolError) {
|
|
1011
|
+
if (err.retryable) {
|
|
1012
|
+
context.logger.warn(`Transient error (rate limit or timeout): ${err.message}`)
|
|
1013
|
+
// safe to retry or re-throw for the caller to handle
|
|
1014
|
+
} else {
|
|
1015
|
+
throw new ExecutionError(`Permanent integration error: ${err.message}`)
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
throw err
|
|
1019
|
+
}
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
**`ToolingError`.** Internal SDK infrastructure error. Treat as non-retryable.
|
|
1023
|
+
|
|
1024
|
+
**`context.logger`.** Use it for all logging inside handlers. `console.log` is not captured by
|
|
1025
|
+
the platform worker runtime. Log levels: `debug`, `info`, `warn`, `error`. The error message in
|
|
1026
|
+
an unhandled throw surfaces directly to CLI output and Command Center -- write it for humans.
|
|
1027
|
+
|
|
1028
|
+
**Common retryable codes.** `rate_limit_exceeded` and `timeout_error` are retryable.
|
|
1029
|
+
`credentials_invalid` and `validation_error` are not.
|
|
1030
|
+
|
|
1031
|
+
**Verification:** Demonstrate a handler that catches a `PlatformToolError`, checks `retryable`,
|
|
1032
|
+
logs appropriately, and re-throws as an `ExecutionError` with a clear message.
|
|
1033
|
+
|
|
1034
|
+
**Completion signal:** Mark `[ ] 15 Error handling` as `[x] YYYY-MM-DD` in
|
|
1035
|
+
`.claude/memory/tutorial-progress.md`.
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
### Item 16: LLM and agents
|
|
1040
|
+
|
|
1041
|
+
**Goal:** The user calls `llm.generate()` with structured output (Zod -> JSON Schema -> typed
|
|
1042
|
+
result) and understands when to use an agent definition vs a workflow definition.
|
|
1043
|
+
|
|
1044
|
+
**Estimated time:** 25 min
|
|
1045
|
+
|
|
1046
|
+
**Files referenced:** `node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`,
|
|
1047
|
+
`apps/docs/content/docs/sdk/concepts.mdx` (workflow vs agent section)
|
|
1048
|
+
|
|
1049
|
+
**Flow:**
|
|
1050
|
+
|
|
1051
|
+
**`llm.generate()` with structured output.** The `llm` singleton from `@elevasis/sdk/worker`
|
|
1052
|
+
accepts a Zod output schema and returns a typed result:
|
|
1053
|
+
|
|
1054
|
+
```typescript
|
|
1055
|
+
import { llm } from '@elevasis/sdk/worker'
|
|
1056
|
+
import { z } from 'zod'
|
|
1057
|
+
|
|
1058
|
+
const classificationSchema = z.object({
|
|
1059
|
+
category: z.enum(['support', 'billing', 'feature-request', 'other']),
|
|
1060
|
+
confidence: z.number().min(0).max(1),
|
|
1061
|
+
summary: z.string()
|
|
1062
|
+
})
|
|
1063
|
+
|
|
1064
|
+
const result = await llm.generate({
|
|
1065
|
+
prompt: `Classify this ticket: ${input.ticketBody}`,
|
|
1066
|
+
outputSchema: classificationSchema,
|
|
1067
|
+
model: 'gpt-4o',
|
|
1068
|
+
temperature: 0.2
|
|
1069
|
+
})
|
|
1070
|
+
|
|
1071
|
+
// result is typed as z.infer<typeof classificationSchema>
|
|
1072
|
+
context.logger.info(`Classified as: ${result.category} (${result.confidence})`)
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
The SDK converts the Zod schema to JSON Schema internally. The LLM is instructed to return JSON
|
|
1076
|
+
matching that shape. The result is validated and returned as the inferred TypeScript type.
|
|
1077
|
+
|
|
1078
|
+
**Agent definition vs workflow definition.** Read the glossary entries from
|
|
1079
|
+
`apps/docs/content/docs/sdk/concepts.mdx`:
|
|
1080
|
+
|
|
1081
|
+
- **Workflow** -- deterministic, step-by-step, 300s timeout. Use when inputs and outputs are
|
|
1082
|
+
known and the process is predefined.
|
|
1083
|
+
- **Agent** -- autonomous, tool-calling, 600s timeout (configurable). Use when the agent needs
|
|
1084
|
+
to make decisions, call tools iteratively, or navigate ambiguous inputs.
|
|
1085
|
+
|
|
1086
|
+
When to use an agent: the task requires the LLM to decide WHICH tools to call and in WHAT order
|
|
1087
|
+
based on what it finds (not predetermined routing). When to use a workflow: the sequence of steps
|
|
1088
|
+
is predetermined, even if LLM judgment is involved inside individual steps.
|
|
1089
|
+
|
|
1090
|
+
**Model selection.** `gpt-4o` for complex reasoning, `gpt-4o-mini` for speed-sensitive tasks,
|
|
1091
|
+
`o3-mini` for multi-step reasoning chains. Temperature: `0.0` for deterministic classification,
|
|
1092
|
+
`0.2-0.4` for structured extraction, `0.7+` for creative generation.
|
|
1093
|
+
|
|
1094
|
+
**Verification:** A workflow step calls `llm.generate()` with a Zod schema and returns a typed
|
|
1095
|
+
value. The type system confirms the result matches the schema without a cast.
|
|
1096
|
+
|
|
1097
|
+
**Completion signal:** Mark `[ ] 16 LLM and agents` as `[x] YYYY-MM-DD` in
|
|
1098
|
+
`.claude/memory/tutorial-progress.md`.
|
|
1099
|
+
|
|
1100
|
+
---
|
|
1101
|
+
|
|
1102
|
+
### Item 17: Composition (execution.trigger)
|
|
1103
|
+
|
|
1104
|
+
**Goal:** The user chains two workflows using `execution.trigger()`, declares the relationship in
|
|
1105
|
+
`DeploymentSpec`, and can find the edge in Command View.
|
|
1106
|
+
|
|
1107
|
+
**Estimated time:** 20 min
|
|
1108
|
+
|
|
1109
|
+
**Files referenced:** `operations/src/index.ts`,
|
|
1110
|
+
`node_modules/@elevasis/sdk/reference/scaffold/operations/workflow-recipes.md`
|
|
1111
|
+
|
|
1112
|
+
**Flow:**
|
|
1113
|
+
|
|
1114
|
+
**`execution.trigger()`.** Call from inside a step handler to launch another workflow
|
|
1115
|
+
asynchronously:
|
|
1116
|
+
|
|
1117
|
+
```typescript
|
|
1118
|
+
import { execution } from '@elevasis/sdk/worker'
|
|
1119
|
+
|
|
1120
|
+
const { executionId } = await execution.trigger({
|
|
1121
|
+
resourceId: 'downstream-workflow',
|
|
1122
|
+
input: { ...outputFromCurrentStep }
|
|
1123
|
+
})
|
|
1124
|
+
|
|
1125
|
+
context.logger.info(`Triggered downstream-workflow: ${executionId}`)
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
The calling step does not wait for the downstream workflow to complete. If you need the result,
|
|
1129
|
+
use async polling or chain via HITL.
|
|
1130
|
+
|
|
1131
|
+
**Declare the relationship in `DeploymentSpec`.** In `operations/src/index.ts`, add a
|
|
1132
|
+
`relationships` entry so the platform knows this workflow triggers another:
|
|
1133
|
+
|
|
1134
|
+
```typescript
|
|
1135
|
+
const org: DeploymentSpec = {
|
|
1136
|
+
version: '0.1.0',
|
|
1137
|
+
workflows: [...],
|
|
1138
|
+
agents: [...],
|
|
1139
|
+
relationships: [
|
|
1140
|
+
{
|
|
1141
|
+
source: 'upstream-workflow',
|
|
1142
|
+
target: 'downstream-workflow',
|
|
1143
|
+
type: 'triggers'
|
|
1144
|
+
}
|
|
1145
|
+
]
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
This metadata populates the Command View graph. Without it, the edge is not visible in the UI
|
|
1150
|
+
even if the trigger call works.
|
|
1151
|
+
|
|
1152
|
+
**Command View.** Show the user how to find Command View in Command Center -> Operations.
|
|
1153
|
+
Deployed relationships appear as directed edges between resource nodes. This is the primary
|
|
1154
|
+
debugging surface for understanding how automations are connected.
|
|
1155
|
+
|
|
1156
|
+
**Verification:** Deploy both workflows with the relationship declared, then view the edge in
|
|
1157
|
+
Command View.
|
|
1158
|
+
|
|
1159
|
+
**Completion signal:** Mark `[ ] 17 Composition` as `[x] YYYY-MM-DD` in
|
|
1160
|
+
`.claude/memory/tutorial-progress.md`.
|
|
1161
|
+
|
|
1162
|
+
---
|
|
1163
|
+
|
|
1164
|
+
## SECTION E -- Power User
|
|
1165
|
+
|
|
1166
|
+
### Item 18: Rules, memory, scaffold registry
|
|
1167
|
+
|
|
1168
|
+
**Goal:** The user can navigate the `.claude/rules/` and `.claude/memory/` directories and
|
|
1169
|
+
understands the scaffold registry's purpose.
|
|
1170
|
+
|
|
1171
|
+
**Estimated time:** 20 min
|
|
1172
|
+
|
|
1173
|
+
**Files referenced:** `.claude/rules/` (all files), `.claude/memory/` (layout),
|
|
1174
|
+
`.claude/scaffold-registry.yml` (reference, monorepo-side concept)
|
|
1175
|
+
|
|
1176
|
+
**Flow:**
|
|
1177
|
+
|
|
1178
|
+
**Rules directory.** List the current rule files in `.claude/rules/`. Each rule is automatically
|
|
1179
|
+
loaded by the agent on every session based on its `paths:` frontmatter (path globs that trigger
|
|
1180
|
+
auto-load). Key files:
|
|
1181
|
+
|
|
1182
|
+
- `agent-start-here.md` -- always-loaded canonical first-read; task-class routing and boundary
|
|
1183
|
+
resolution
|
|
1184
|
+
- `vibe.md` -- always-loaded ambient intent classifier
|
|
1185
|
+
- `organization-os.md` -- loaded when touching org model, System access, Action routing, or entity work
|
|
1186
|
+
- `deployment.md` -- loaded when deploying resources
|
|
1187
|
+
- `error-handling.md` -- loaded when authoring handlers with error concerns
|
|
1188
|
+
- `execution.md` -- loaded when reasoning about the runtime model
|
|
1189
|
+
- `observability.md` -- loaded when using `context.logger` or inspecting executions
|
|
1190
|
+
- `operations.md` -- loaded when touching `operations/src/`
|
|
1191
|
+
- `platform.md` -- loaded when importing from `@elevasis/sdk`
|
|
1192
|
+
- `shared-types.md` -- loaded when touching `core/types/`
|
|
1193
|
+
- `task-tracking.md` -- loaded when managing project tasks
|
|
1194
|
+
- `active-change-index.md` -- flags areas under active architecture change; trust this over
|
|
1195
|
+
stable scaffold docs for those areas
|
|
1196
|
+
|
|
1197
|
+
To add a new rule: create `.claude/rules/<domain>.md` with `paths:` frontmatter matching the
|
|
1198
|
+
glob of files it applies to. The rules system discovers it automatically -- no registration step.
|
|
1199
|
+
|
|
1200
|
+
**Memory directory.** List `.claude/memory/`. Common files:
|
|
1201
|
+
|
|
1202
|
+
- `profile.md` -- track choice and tone implications (written by this tutorial)
|
|
1203
|
+
- `tutorial-progress.md` -- per-item progress markers for both tracks
|
|
1204
|
+
|
|
1205
|
+
Memory files are readable by the agent at session start. The `profile.md` file is particularly
|
|
1206
|
+
important: it sets the project-wide tone for all sessions after the tutorial track is selected.
|
|
1207
|
+
|
|
1208
|
+
**Scaffold registry (monorepo-side concept).** `.claude/scaffold-registry.yml` is the
|
|
1209
|
+
monorepo's source-of-truth for scaffold dependencies -- it maps source paths to generated or
|
|
1210
|
+
manual scaffolds that depend on them. It drives the PostToolUse reminder hook and `/work handoff`
|
|
1211
|
+
preflight in the monorepo. In an external project you are a consumer, not a contributor, of that
|
|
1212
|
+
registry. The relevant thing to know: when the SDK ships a template update, the registry controls
|
|
1213
|
+
which files are Tier 1 (always replaced on sync), Tier 2 (merge-aware), and Tier 3 (never
|
|
1214
|
+
touched). Item 19 covers this in detail.
|
|
1215
|
+
|
|
1216
|
+
**Verification:** Ask: "How does the agent know to load `deployment.md` when you run a deploy?"
|
|
1217
|
+
(Answer: the `paths:` frontmatter in the rule file matches `operations/` file globs and deploy
|
|
1218
|
+
command patterns.)
|
|
1219
|
+
|
|
1220
|
+
**Completion signal:** Mark `[ ] 18 Rules, memory, scaffold registry` as `[x] YYYY-MM-DD` in
|
|
1221
|
+
`.claude/memory/tutorial-progress.md`.
|
|
1222
|
+
|
|
1223
|
+
---
|
|
1224
|
+
|
|
1225
|
+
### Item 19: Template lifecycle and /git-sync
|
|
1226
|
+
|
|
1227
|
+
**Goal:** The user understands the three-tier file classification, runs `/git-sync`, and knows
|
|
1228
|
+
what manual reconciliation is required after a template update.
|
|
1229
|
+
|
|
1230
|
+
**Estimated time:** 20 min
|
|
1231
|
+
|
|
1232
|
+
**Files referenced:** `.claude/skills/git-sync/SKILL.md`, `.claude/sync-notes/` (directory)
|
|
1233
|
+
|
|
1234
|
+
**Flow:**
|
|
1235
|
+
|
|
1236
|
+
Tell the user:
|
|
1237
|
+
|
|
1238
|
+
> "The template you scaffolded from is versioned. When a new SDK version ships, the template
|
|
1239
|
+
> updates. `/git-sync` pulls those changes and tells you what changed. You decide what to do
|
|
1240
|
+
> with them."
|
|
1241
|
+
|
|
1242
|
+
**Three-tier file classification.** Walk through the tiers:
|
|
1243
|
+
|
|
1244
|
+
- **Tier 1 (always replaced)** -- SDK-owned files that ship in a known correct state:
|
|
1245
|
+
`.claude/skills/*/SKILL.md` (except project-authored skills), `.claude/hooks/`, `CLAUDE.md`
|
|
1246
|
+
sections that are platform-managed. These are overwritten on every template sync. Do not put
|
|
1247
|
+
custom logic in them; instead use Tier 3 files for project-specific config.
|
|
1248
|
+
- **Tier 2 (merge-aware)** -- Files where the template ships defaults but the project may have
|
|
1249
|
+
customized: `core/config/organization-model.ts`, `.claude/rules/active-change-index.md`,
|
|
1250
|
+
vibe classifier threshold. The sync does not blindly overwrite -- the agent surfaces the diff
|
|
1251
|
+
and the user decides what to keep.
|
|
1252
|
+
- **Tier 3 (never touched)** -- Project-owned files the template never overwrites: project
|
|
1253
|
+
source code in `operations/src/`, `ui/src/`, all `core/types/` files, `core/config/extensions/`,
|
|
1254
|
+
custom rules added to `.claude/rules/`, everything in `.claude/memory/`.
|
|
1255
|
+
|
|
1256
|
+
**Running `/git-sync`.** Read `.claude/skills/git-sync/SKILL.md` and walk the user through each
|
|
1257
|
+
step:
|
|
1258
|
+
|
|
1259
|
+
1. Check for uncommitted changes (`git status --short`). Stop if dirty.
|
|
1260
|
+
2. Snapshot current sync-note filenames before pulling.
|
|
1261
|
+
3. `git pull --rebase` -- stop and report conflicts; do not auto-resolve.
|
|
1262
|
+
4. Detect dependency baseline changes; run `pnpm install` if `package.json` or
|
|
1263
|
+
`pnpm-lock.yaml` changed.
|
|
1264
|
+
5. Run baseline verification: `pnpm -C ui check-types`, `pnpm -C ui build`,
|
|
1265
|
+
`pnpm -C operations check`, `pnpm -C operations check-types`.
|
|
1266
|
+
6. Surface newly introduced sync notes from `.claude/sync-notes/`. Each note has required
|
|
1267
|
+
actions and a verification section.
|
|
1268
|
+
7. Stop. `/git-sync` does not auto-reconcile. Manual follow-up is explicit.
|
|
1269
|
+
|
|
1270
|
+
**Sync notes contract.** Files in `.claude/sync-notes/` named `YYYY-MM-DD-<slug>.md` are
|
|
1271
|
+
operative release guidance. They use fixed headings: `## Why this note exists`,
|
|
1272
|
+
`## Applies to`, `## Required actions`, `## Verification`, `## Not handled by /git-sync`.
|
|
1273
|
+
Never edit operative note filenames after they ship -- they are append-only.
|
|
1274
|
+
|
|
1275
|
+
**Verification:** The user knows which of their own files are Tier 3 (never touched) and can
|
|
1276
|
+
explain why `/git-sync` stops before reconciliation rather than auto-applying diffs.
|
|
1277
|
+
|
|
1278
|
+
**Completion signal:** Mark `[ ] 19 Template lifecycle and /git-sync` as `[x] YYYY-MM-DD` in
|
|
1279
|
+
`.claude/memory/tutorial-progress.md`.
|
|
1280
|
+
|
|
1281
|
+
---
|
|
1282
|
+
|
|
1283
|
+
## After All 19 Items Complete
|
|
1284
|
+
|
|
1285
|
+
When all 19 items are marked `[x]` in `.claude/memory/tutorial-progress.md`, deliver the
|
|
1286
|
+
following closing script:
|
|
1287
|
+
|
|
1288
|
+
> "You've completed the technical track. Here's where to go from here:
|
|
1289
|
+
>
|
|
1290
|
+
> - **Build real automations.** You now have the full surface: workflows, schemas, HITL, schedules,
|
|
1291
|
+
> LLM integration, error handling, and composition. Start with the domain most relevant to your
|
|
1292
|
+
> project and use `/project create` to track the work.
|
|
1293
|
+
> - **SDK reference scaffold.** `node_modules/@elevasis/sdk/reference/scaffold/index.mdx` is the
|
|
1294
|
+
> canonical recipe index for anything you want to build: adding a System or UI feature, extending lead-gen,
|
|
1295
|
+
> customizing CRM actions, authoring agents. Read it whenever you are starting something new.
|
|
1296
|
+
> - **Explore the codebase.** `/explore` is your tool for open-ended questions about how things
|
|
1297
|
+
> are wired. Use it before making changes in unfamiliar parts of the project.
|
|
1298
|
+
> - **Keep the org model current.** Run `/om` any time your business reality changes --
|
|
1299
|
+
> new offerings, new customer segments, new goals. The model is the agent's vocabulary for
|
|
1300
|
+
> your project; an accurate model means better agent output.
|
|
1301
|
+
> - **Stay current with template updates.** When a new SDK version ships, run `/git-sync` to pull
|
|
1302
|
+
> it and read the sync notes. They tell you exactly what changed and what manual follow-up is
|
|
1303
|
+
> needed."
|