@oneie/sdk 0.13.8 → 0.14.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/templates.js CHANGED
@@ -1,125 +1,931 @@
1
+ // ─── Industry & department resource-type templates ───────────────────────────
2
+ //
3
+ // Each template is a ready-made ontology a workspace can seed into its manifest
4
+ // (world:declare-types { template }). Every type maps to one of the 6 ONE
5
+ // dimensions (groups · actors · things · paths · events · learning) and the
6
+ // `/u/{slug}/types` canvas renders them — dimension sets the node tone, and a
7
+ // `relation` field whose option names a sibling key draws an edge between nodes.
8
+ //
9
+ // The library is layered:
10
+ // • one / world — the substrate itself and an operating workspace
11
+ // • crm — the shared contact graph every department reads
12
+ // • sales · marketing · service · education · staff — the departments
13
+ // • ecommerce — a store
14
+ // • roofers · movers — field-service verticals
15
+ //
16
+ // Authoring rules that keep the graph clean:
17
+ // • columns = the 2–4 fields shown on the card and the table list
18
+ // • fields = the full edit form
19
+ // • filters = the select/tag fields worth faceting on
20
+ // • relation field options MUST name a key in the SAME template, or no edge
21
+ // is drawn (extractRelationEdges drops dangling targets).
22
+ // • `ecommerce` is intentionally relation-free — a unit test pins that.
23
+ const THING_ACTIONS = ["world:create-thing", "world:update-thing", "world:remove-thing"];
24
+ const ACTOR_ACTIONS = ["world:create-actor", "world:update-actor"];
25
+ const GROUP_ACTIONS = ["world:create-group", "world:update-group"];
1
26
  export const TEMPLATES = {
2
- ecommerce: [
27
+ // ── one — the substrate itself: the 6 dimensions as a living graph ──────────
28
+ one: [
3
29
  {
4
- key: "products", label: "Products", dimension: "things", type: "product", icon: "Package",
30
+ key: "groups", label: "Groups", dimension: "groups", type: "group", icon: "Boxes",
5
31
  columns: [
6
32
  { field: "name", label: "Name", kind: "text", required: true },
7
- { field: "price", label: "Price", kind: "currency" },
33
+ { field: "group_type", label: "Type", kind: "select", options: ["world", "team", "org", "community", "dao", "workspace", "personal"] },
34
+ { field: "plan", label: "Plan", kind: "select", options: ["starter", "growth", "enterprise"] },
35
+ ],
36
+ fields: [
37
+ { field: "name", label: "Name", kind: "text", required: true },
38
+ { field: "group_type", label: "Group type", kind: "select", options: ["world", "team", "org", "community", "dao", "workspace", "personal"] },
39
+ { field: "brand", label: "Brand", kind: "text" },
40
+ { field: "plan", label: "Plan", kind: "select", options: ["starter", "growth", "enterprise"] },
41
+ { field: "visibility", label: "Visibility", kind: "select", options: ["private", "group", "public"] },
42
+ ],
43
+ filters: [{ field: "group_type", label: "Type", kind: "select", options: ["world", "team", "org", "community", "dao", "workspace", "personal"] }],
44
+ actions: GROUP_ACTIONS,
45
+ },
46
+ {
47
+ key: "actors", label: "Actors", dimension: "actors", type: "actor", icon: "Users",
48
+ columns: [
49
+ { field: "name", label: "Name", kind: "text", required: true },
50
+ { field: "actor_type", label: "Kind", kind: "select", options: ["human", "agent", "world", "animal"] },
51
+ { field: "model", label: "Model", kind: "text" },
52
+ ],
53
+ fields: [
54
+ { field: "name", label: "Name", kind: "text", required: true },
55
+ { field: "actor_type", label: "Actor type", kind: "select", options: ["human", "agent", "world", "animal"] },
56
+ { field: "model", label: "LLM model", kind: "text" },
57
+ { field: "prompt", label: "System prompt", kind: "markdown" },
58
+ { field: "wallet", label: "Wallet", kind: "text" },
8
59
  { field: "tags", label: "Tags", kind: "tags" },
60
+ { field: "group", label: "Member of", kind: "relation", options: ["groups"] },
61
+ ],
62
+ filters: [{ field: "actor_type", label: "Kind", kind: "select", options: ["human", "agent", "world", "animal"] }],
63
+ actions: ACTOR_ACTIONS,
64
+ },
65
+ {
66
+ key: "things", label: "Things", dimension: "things", type: "thing", icon: "Box",
67
+ columns: [
68
+ { field: "name", label: "Name", kind: "text", required: true },
69
+ { field: "thing_type", label: "Type", kind: "select", options: ["skill", "task", "token", "service", "plan"] },
70
+ { field: "price", label: "Price", kind: "currency" },
9
71
  ],
10
72
  fields: [
11
73
  { field: "name", label: "Name", kind: "text", required: true },
74
+ { field: "thing_type", label: "Thing type", kind: "select", options: ["skill", "task", "token", "service", "plan"] },
12
75
  { field: "price", label: "Price", kind: "currency" },
13
- { field: "description", label: "Description", kind: "markdown" },
14
- { field: "image", label: "Image", kind: "image" },
76
+ { field: "status", label: "Status", kind: "text" },
15
77
  { field: "tags", label: "Tags", kind: "tags" },
78
+ { field: "provider", label: "Provided by", kind: "relation", options: ["actors"] },
16
79
  ],
17
- filters: [{ field: "tags", label: "Tags", kind: "tags" }],
18
- actions: ["world:create-thing", "world:update-thing", "world:remove-thing"],
80
+ filters: [{ field: "thing_type", label: "Type", kind: "select", options: ["skill", "task", "token", "service", "plan"] }],
81
+ actions: THING_ACTIONS,
19
82
  },
20
83
  {
21
- key: "orders", label: "Orders", dimension: "things", type: "order", icon: "ShoppingCart",
84
+ key: "paths", label: "Paths", dimension: "paths", type: "path", icon: "Spline",
22
85
  columns: [
23
- { field: "name", label: "Order #", kind: "text", required: true },
24
- { field: "price", label: "Total", kind: "currency" },
25
- { field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] },
86
+ { field: "name", label: "Path", kind: "text", required: true },
87
+ { field: "strength", label: "Strength", kind: "number" },
88
+ { field: "resistance", label: "Resistance", kind: "number" },
26
89
  ],
27
90
  fields: [
28
- { field: "name", label: "Order #", kind: "text", required: true },
29
- { field: "price", label: "Total", kind: "currency" },
30
- { field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] },
91
+ { field: "name", label: "Path", kind: "text", required: true },
92
+ { field: "strength", label: "Strength (mark)", kind: "number" },
93
+ { field: "resistance", label: "Resistance (warn)", kind: "number" },
94
+ { field: "traversals", label: "Traversals", kind: "number" },
95
+ { field: "scope", label: "Scope", kind: "select", options: ["private", "group", "public"] },
96
+ { field: "from", label: "Source", kind: "relation", options: ["actors"] },
97
+ { field: "to", label: "Target", kind: "relation", options: ["actors"] },
31
98
  ],
32
- filters: [{ field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] }],
33
- actions: ["world:update-thing"],
99
+ filters: [{ field: "scope", label: "Scope", kind: "select", options: ["private", "group", "public"] }],
100
+ actions: [],
34
101
  },
35
102
  {
36
- key: "customers", label: "Customers", dimension: "actors", type: "customer", icon: "Users",
103
+ key: "events", label: "Events", dimension: "events", type: "signal", icon: "Zap",
104
+ columns: [
105
+ { field: "name", label: "Signal", kind: "text", required: true },
106
+ { field: "amount", label: "Amount", kind: "currency" },
107
+ { field: "ts", label: "When", kind: "date" },
108
+ ],
109
+ fields: [
110
+ { field: "name", label: "Receiver", kind: "text", required: true },
111
+ { field: "payload", label: "Payload", kind: "markdown" },
112
+ { field: "amount", label: "Amount", kind: "currency" },
113
+ { field: "success", label: "Success", kind: "select", options: ["yes", "no"] },
114
+ { field: "latency", label: "Latency (ms)", kind: "number" },
115
+ { field: "ts", label: "Timestamp", kind: "date" },
116
+ { field: "sender", label: "Sender", kind: "relation", options: ["actors"] },
117
+ { field: "receiver", label: "Receiver", kind: "relation", options: ["actors"] },
118
+ ],
119
+ filters: [{ field: "success", label: "Success", kind: "select", options: ["yes", "no"] }],
120
+ actions: [],
121
+ },
122
+ {
123
+ key: "learning", label: "Learning", dimension: "learning", type: "hypothesis", icon: "Lightbulb",
124
+ columns: [
125
+ { field: "name", label: "Statement", kind: "text", required: true },
126
+ { field: "status", label: "Status", kind: "select", options: ["pending", "confirmed", "rejected"] },
127
+ { field: "confidence", label: "Confidence", kind: "number" },
128
+ ],
129
+ fields: [
130
+ { field: "name", label: "Statement", kind: "text", required: true },
131
+ { field: "status", label: "Status", kind: "select", options: ["pending", "confirmed", "rejected"] },
132
+ { field: "confidence", label: "Confidence", kind: "number" },
133
+ { field: "p_value", label: "p-value", kind: "number" },
134
+ { field: "observations", label: "Observations", kind: "number" },
135
+ { field: "source", label: "Source receiver", kind: "text" },
136
+ { field: "about", label: "Belongs to", kind: "relation", options: ["groups"] },
137
+ ],
138
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["pending", "confirmed", "rejected"] }],
139
+ actions: ["world:create-hypothesis"],
140
+ },
141
+ ],
142
+ // ── world — an operating workspace: who acts, what they make, what they aim at ─
143
+ world: [
144
+ {
145
+ key: "members", label: "Members", dimension: "actors", type: "member", icon: "Users",
37
146
  columns: [
38
147
  { field: "name", label: "Name", kind: "text", required: true },
39
- { field: "tags", label: "Tags", kind: "tags" },
148
+ { field: "role", label: "Role", kind: "select", options: ["owner", "admin", "member", "viewer", "agent", "auditor"] },
149
+ { field: "actor_type", label: "Kind", kind: "select", options: ["human", "agent"] },
40
150
  ],
41
151
  fields: [
42
152
  { field: "name", label: "Name", kind: "text", required: true },
153
+ { field: "role", label: "Role", kind: "select", options: ["owner", "admin", "member", "viewer", "agent", "auditor"] },
154
+ { field: "actor_type", label: "Kind", kind: "select", options: ["human", "agent"] },
43
155
  { field: "tags", label: "Tags", kind: "tags" },
44
156
  ],
45
- filters: [],
46
- actions: ["world:create-actor", "world:update-actor"],
157
+ filters: [{ field: "role", label: "Role", kind: "select", options: ["owner", "admin", "member", "viewer", "agent", "auditor"] }],
158
+ actions: ACTOR_ACTIONS,
159
+ },
160
+ {
161
+ key: "skills", label: "Skills", dimension: "things", type: "skill", icon: "Sparkles",
162
+ columns: [
163
+ { field: "name", label: "Skill", kind: "text", required: true },
164
+ { field: "price", label: "Price", kind: "currency" },
165
+ { field: "tags", label: "Tags", kind: "tags" },
166
+ ],
167
+ fields: [
168
+ { field: "name", label: "Skill", kind: "text", required: true },
169
+ { field: "description", label: "Description", kind: "markdown" },
170
+ { field: "price", label: "Price", kind: "currency" },
171
+ { field: "tags", label: "Tags", kind: "tags" },
172
+ { field: "provider", label: "Provider", kind: "relation", options: ["members"] },
173
+ ],
174
+ filters: [{ field: "tags", label: "Tags", kind: "tags" }],
175
+ actions: THING_ACTIONS,
176
+ },
177
+ {
178
+ key: "tasks", label: "Tasks", dimension: "things", type: "task", icon: "ListTodo",
179
+ columns: [
180
+ { field: "name", label: "Task", kind: "text", required: true },
181
+ { field: "status", label: "Status", kind: "select", options: ["open", "blocked", "picked", "done", "verified", "failed"] },
182
+ { field: "wave", label: "Wave", kind: "select", options: ["W1", "W2", "W3", "W4"] },
183
+ ],
184
+ fields: [
185
+ { field: "name", label: "Task", kind: "text", required: true },
186
+ { field: "status", label: "Status", kind: "select", options: ["open", "blocked", "picked", "done", "verified", "failed"] },
187
+ { field: "wave", label: "Wave", kind: "select", options: ["W1", "W2", "W3", "W4"] },
188
+ { field: "effort", label: "Effort", kind: "number" },
189
+ { field: "value", label: "Value", kind: "number" },
190
+ { field: "assignee", label: "Assignee", kind: "relation", options: ["members"] },
191
+ ],
192
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["open", "blocked", "picked", "done", "verified", "failed"] }],
193
+ actions: THING_ACTIONS,
194
+ },
195
+ {
196
+ key: "objectives", label: "Objectives", dimension: "things", type: "objective", icon: "Target",
197
+ columns: [
198
+ { field: "name", label: "Objective", kind: "text", required: true },
199
+ { field: "status", label: "Status", kind: "select", options: ["pending", "active", "complete"] },
200
+ { field: "progress", label: "Progress", kind: "number" },
201
+ ],
202
+ fields: [
203
+ { field: "name", label: "Objective", kind: "text", required: true },
204
+ { field: "status", label: "Status", kind: "select", options: ["pending", "active", "complete"] },
205
+ { field: "priority", label: "Priority", kind: "number" },
206
+ { field: "progress", label: "Progress", kind: "number" },
207
+ { field: "frontier", label: "Spawned from", kind: "relation", options: ["frontiers"] },
208
+ ],
209
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["pending", "active", "complete"] }],
210
+ actions: THING_ACTIONS,
211
+ },
212
+ {
213
+ key: "frontiers", label: "Frontiers", dimension: "learning", type: "frontier", icon: "Compass",
214
+ columns: [
215
+ { field: "name", label: "Frontier", kind: "text", required: true },
216
+ { field: "status", label: "Status", kind: "select", options: ["unexplored", "exploring", "exhausted"] },
217
+ { field: "expected_value", label: "Expected value", kind: "number" },
218
+ ],
219
+ fields: [
220
+ { field: "name", label: "Frontier", kind: "text", required: true },
221
+ { field: "frontier_type", label: "Type", kind: "text" },
222
+ { field: "status", label: "Status", kind: "select", options: ["unexplored", "exploring", "exhausted"] },
223
+ { field: "expected_value", label: "Expected value", kind: "number" },
224
+ ],
225
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["unexplored", "exploring", "exhausted"] }],
226
+ actions: [],
227
+ },
228
+ {
229
+ key: "contributions", label: "Contributions", dimension: "events", type: "contribution", icon: "Activity",
230
+ columns: [
231
+ { field: "name", label: "Contribution", kind: "text", required: true },
232
+ { field: "contribution_type", label: "Type", kind: "select", options: ["signal", "payment", "discovery"] },
233
+ { field: "impact", label: "Impact", kind: "number" },
234
+ ],
235
+ fields: [
236
+ { field: "name", label: "Contribution", kind: "text", required: true },
237
+ { field: "contribution_type", label: "Type", kind: "select", options: ["signal", "payment", "discovery"] },
238
+ { field: "impact", label: "Impact score", kind: "number" },
239
+ { field: "contributor", label: "Contributor", kind: "relation", options: ["members"] },
240
+ ],
241
+ filters: [{ field: "contribution_type", label: "Type", kind: "select", options: ["signal", "payment", "discovery"] }],
242
+ actions: [],
47
243
  },
48
244
  ],
245
+ // ── crm — contacts, the companies they belong to, deals, and the work ───────
49
246
  crm: [
50
247
  {
51
248
  key: "contacts", label: "Contacts", dimension: "actors", type: "contact", icon: "UserRound",
52
249
  columns: [
53
250
  { field: "name", label: "Name", kind: "text", required: true },
54
- { field: "tags", label: "Tags", kind: "tags" },
251
+ { field: "lifecycle", label: "Stage", kind: "select", options: ["lead", "mql", "sql", "customer", "churned"] },
252
+ { field: "company", label: "Company", kind: "relation", options: ["companies"] },
55
253
  ],
56
254
  fields: [
57
255
  { field: "name", label: "Name", kind: "text", required: true },
256
+ { field: "email", label: "Email", kind: "text" },
257
+ { field: "phone", label: "Phone", kind: "text" },
258
+ { field: "title", label: "Job title", kind: "text" },
259
+ { field: "lifecycle", label: "Lifecycle", kind: "select", options: ["lead", "mql", "sql", "customer", "churned"] },
58
260
  { field: "tags", label: "Tags", kind: "tags" },
261
+ { field: "company", label: "Company", kind: "relation", options: ["companies"] },
59
262
  ],
60
- filters: [],
61
- actions: ["world:create-actor", "world:update-actor"],
263
+ filters: [{ field: "lifecycle", label: "Stage", kind: "select", options: ["lead", "mql", "sql", "customer", "churned"] }],
264
+ actions: ACTOR_ACTIONS,
265
+ },
266
+ {
267
+ key: "companies", label: "Companies", dimension: "groups", type: "company", icon: "Building2",
268
+ columns: [
269
+ { field: "name", label: "Company", kind: "text", required: true },
270
+ { field: "industry", label: "Industry", kind: "text" },
271
+ { field: "size", label: "Size", kind: "select", options: ["1-10", "11-50", "51-200", "201-1000", "1000+"] },
272
+ ],
273
+ fields: [
274
+ { field: "name", label: "Company", kind: "text", required: true },
275
+ { field: "domain", label: "Domain", kind: "text" },
276
+ { field: "industry", label: "Industry", kind: "text" },
277
+ { field: "size", label: "Headcount", kind: "select", options: ["1-10", "11-50", "51-200", "201-1000", "1000+"] },
278
+ { field: "tags", label: "Tags", kind: "tags" },
279
+ ],
280
+ filters: [{ field: "size", label: "Size", kind: "select", options: ["1-10", "11-50", "51-200", "201-1000", "1000+"] }],
281
+ actions: GROUP_ACTIONS,
62
282
  },
63
283
  {
64
284
  key: "deals", label: "Deals", dimension: "things", type: "deal", icon: "Handshake",
65
285
  columns: [
66
286
  { field: "name", label: "Deal", kind: "text", required: true },
67
- { field: "price", label: "Value", kind: "currency" },
68
- { field: "tags", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "closed-won", "closed-lost"] },
287
+ { field: "value", label: "Value", kind: "currency" },
288
+ { field: "stage", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "negotiation", "closed-won", "closed-lost"] },
69
289
  ],
70
290
  fields: [
71
291
  { field: "name", label: "Deal", kind: "text", required: true },
72
- { field: "price", label: "Value", kind: "currency" },
73
- { field: "tags", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "closed-won", "closed-lost"] },
292
+ { field: "value", label: "Value", kind: "currency" },
293
+ { field: "stage", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "negotiation", "closed-won", "closed-lost"] },
294
+ { field: "close_date", label: "Close date", kind: "date" },
295
+ { field: "probability", label: "Probability %", kind: "number" },
296
+ { field: "contact", label: "Primary contact", kind: "relation", options: ["contacts"] },
297
+ { field: "company", label: "Company", kind: "relation", options: ["companies"] },
74
298
  ],
75
- filters: [{ field: "tags", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "closed-won", "closed-lost"] }],
76
- actions: ["world:create-thing", "world:update-thing"],
299
+ filters: [{ field: "stage", label: "Stage", kind: "select", options: ["prospect", "qualified", "proposal", "negotiation", "closed-won", "closed-lost"] }],
300
+ actions: THING_ACTIONS,
77
301
  },
78
302
  {
79
- key: "companies", label: "Companies", dimension: "groups", type: "company", icon: "Building2",
303
+ key: "activities", label: "Activities", dimension: "events", type: "activity", icon: "CalendarClock",
80
304
  columns: [
81
- { field: "name", label: "Company", kind: "text", required: true },
82
- { field: "tags", label: "Tags", kind: "tags" },
305
+ { field: "name", label: "Activity", kind: "text", required: true },
306
+ { field: "kind", label: "Kind", kind: "select", options: ["call", "email", "meeting", "note", "task"] },
307
+ { field: "date", label: "Date", kind: "date" },
308
+ ],
309
+ fields: [
310
+ { field: "name", label: "Activity", kind: "text", required: true },
311
+ { field: "kind", label: "Kind", kind: "select", options: ["call", "email", "meeting", "note", "task"] },
312
+ { field: "date", label: "Date", kind: "date" },
313
+ { field: "notes", label: "Notes", kind: "markdown" },
314
+ { field: "contact", label: "Contact", kind: "relation", options: ["contacts"] },
315
+ { field: "deal", label: "Deal", kind: "relation", options: ["deals"] },
316
+ ],
317
+ filters: [{ field: "kind", label: "Kind", kind: "select", options: ["call", "email", "meeting", "note", "task"] }],
318
+ actions: [],
319
+ },
320
+ {
321
+ key: "tasks", label: "Tasks", dimension: "things", type: "task", icon: "CheckSquare",
322
+ columns: [
323
+ { field: "name", label: "Task", kind: "text", required: true },
324
+ { field: "status", label: "Status", kind: "select", options: ["open", "done"] },
325
+ { field: "due", label: "Due", kind: "date" },
326
+ ],
327
+ fields: [
328
+ { field: "name", label: "Task", kind: "text", required: true },
329
+ { field: "status", label: "Status", kind: "select", options: ["open", "done"] },
330
+ { field: "priority", label: "Priority", kind: "select", options: ["high", "medium", "low"] },
331
+ { field: "due", label: "Due date", kind: "date" },
332
+ { field: "deal", label: "Deal", kind: "relation", options: ["deals"] },
333
+ ],
334
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["open", "done"] }],
335
+ actions: THING_ACTIONS,
336
+ },
337
+ ],
338
+ // ── sales — pipeline: lead → opportunity → quote → order, worked by reps ─────
339
+ sales: [
340
+ {
341
+ key: "leads", label: "Leads", dimension: "actors", type: "lead", icon: "Flame",
342
+ columns: [
343
+ { field: "name", label: "Name", kind: "text", required: true },
344
+ { field: "status", label: "Status", kind: "select", options: ["new", "contacted", "qualified", "unqualified"] },
345
+ { field: "source", label: "Source", kind: "select", options: ["inbound", "outbound", "referral", "event", "ads"] },
346
+ ],
347
+ fields: [
348
+ { field: "name", label: "Name", kind: "text", required: true },
349
+ { field: "email", label: "Email", kind: "text" },
350
+ { field: "phone", label: "Phone", kind: "text" },
351
+ { field: "status", label: "Status", kind: "select", options: ["new", "contacted", "qualified", "unqualified"] },
352
+ { field: "source", label: "Source", kind: "select", options: ["inbound", "outbound", "referral", "event", "ads"] },
353
+ { field: "owner", label: "Owner", kind: "relation", options: ["reps"] },
354
+ ],
355
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["new", "contacted", "qualified", "unqualified"] }],
356
+ actions: ACTOR_ACTIONS,
357
+ },
358
+ {
359
+ key: "opportunities", label: "Opportunities", dimension: "things", type: "opportunity", icon: "TrendingUp",
360
+ columns: [
361
+ { field: "name", label: "Opportunity", kind: "text", required: true },
362
+ { field: "value", label: "Value", kind: "currency" },
363
+ { field: "stage", label: "Stage", kind: "select", options: ["prospecting", "qualification", "proposal", "negotiation", "closed-won", "closed-lost"] },
364
+ ],
365
+ fields: [
366
+ { field: "name", label: "Opportunity", kind: "text", required: true },
367
+ { field: "value", label: "Value", kind: "currency" },
368
+ { field: "stage", label: "Stage", kind: "select", options: ["prospecting", "qualification", "proposal", "negotiation", "closed-won", "closed-lost"] },
369
+ { field: "close_date", label: "Expected close", kind: "date" },
370
+ { field: "probability", label: "Probability %", kind: "number" },
371
+ { field: "lead", label: "Lead", kind: "relation", options: ["leads"] },
372
+ { field: "rep", label: "Owner", kind: "relation", options: ["reps"] },
373
+ ],
374
+ filters: [{ field: "stage", label: "Stage", kind: "select", options: ["prospecting", "qualification", "proposal", "negotiation", "closed-won", "closed-lost"] }],
375
+ actions: THING_ACTIONS,
376
+ },
377
+ {
378
+ key: "quotes", label: "Quotes", dimension: "things", type: "quote", icon: "FileText",
379
+ columns: [
380
+ { field: "name", label: "Quote", kind: "text", required: true },
381
+ { field: "amount", label: "Amount", kind: "currency" },
382
+ { field: "status", label: "Status", kind: "select", options: ["draft", "sent", "accepted", "expired"] },
383
+ ],
384
+ fields: [
385
+ { field: "name", label: "Quote", kind: "text", required: true },
386
+ { field: "amount", label: "Amount", kind: "currency" },
387
+ { field: "status", label: "Status", kind: "select", options: ["draft", "sent", "accepted", "expired"] },
388
+ { field: "valid_until", label: "Valid until", kind: "date" },
389
+ { field: "opportunity", label: "Opportunity", kind: "relation", options: ["opportunities"] },
390
+ ],
391
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["draft", "sent", "accepted", "expired"] }],
392
+ actions: THING_ACTIONS,
393
+ },
394
+ {
395
+ key: "orders", label: "Orders", dimension: "things", type: "order", icon: "ShoppingBag",
396
+ columns: [
397
+ { field: "name", label: "Order #", kind: "text", required: true },
398
+ { field: "total", label: "Total", kind: "currency" },
399
+ { field: "status", label: "Status", kind: "select", options: ["pending", "paid", "fulfilled", "refunded"] },
400
+ ],
401
+ fields: [
402
+ { field: "name", label: "Order #", kind: "text", required: true },
403
+ { field: "total", label: "Total", kind: "currency" },
404
+ { field: "status", label: "Status", kind: "select", options: ["pending", "paid", "fulfilled", "refunded"] },
405
+ { field: "ordered_at", label: "Ordered", kind: "date" },
406
+ { field: "quote", label: "Quote", kind: "relation", options: ["quotes"] },
407
+ ],
408
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["pending", "paid", "fulfilled", "refunded"] }],
409
+ actions: THING_ACTIONS,
410
+ },
411
+ {
412
+ key: "reps", label: "Reps", dimension: "actors", type: "rep", icon: "UserRound",
413
+ columns: [
414
+ { field: "name", label: "Name", kind: "text", required: true },
415
+ { field: "region", label: "Region", kind: "text" },
416
+ { field: "quota", label: "Quota", kind: "currency" },
83
417
  ],
84
418
  fields: [
85
419
  { field: "name", label: "Name", kind: "text", required: true },
420
+ { field: "region", label: "Region", kind: "text" },
421
+ { field: "quota", label: "Quota", kind: "currency" },
86
422
  { field: "tags", label: "Tags", kind: "tags" },
87
423
  ],
88
424
  filters: [],
89
- actions: [],
425
+ actions: ACTOR_ACTIONS,
426
+ },
427
+ ],
428
+ // ── marketing — campaigns → audiences → content → leads, with experiments ───
429
+ marketing: [
430
+ {
431
+ key: "campaigns", label: "Campaigns", dimension: "groups", type: "campaign", icon: "Megaphone",
432
+ columns: [
433
+ { field: "name", label: "Campaign", kind: "text", required: true },
434
+ { field: "status", label: "Status", kind: "select", options: ["draft", "scheduled", "running", "paused", "shipped", "killed"] },
435
+ { field: "budget", label: "Budget", kind: "currency" },
436
+ ],
437
+ fields: [
438
+ { field: "name", label: "Campaign", kind: "text", required: true },
439
+ { field: "status", label: "Status", kind: "select", options: ["draft", "scheduled", "running", "paused", "shipped", "killed"] },
440
+ { field: "budget", label: "Budget", kind: "currency" },
441
+ { field: "spend", label: "Spend", kind: "currency" },
442
+ { field: "north_star", label: "North-star KPI", kind: "text" },
443
+ { field: "start", label: "Start", kind: "date" },
444
+ { field: "end", label: "End", kind: "date" },
445
+ { field: "audience", label: "Audience", kind: "relation", options: ["audiences"] },
446
+ { field: "channel", label: "Channel", kind: "relation", options: ["channels"] },
447
+ ],
448
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["draft", "scheduled", "running", "paused", "shipped", "killed"] }],
449
+ actions: GROUP_ACTIONS,
450
+ },
451
+ {
452
+ key: "audiences", label: "Audiences", dimension: "groups", type: "audience", icon: "Target",
453
+ columns: [
454
+ { field: "name", label: "Audience", kind: "text", required: true },
455
+ { field: "stage", label: "Stage", kind: "select", options: ["awareness", "consideration", "decision", "retention", "advocacy"] },
456
+ { field: "size", label: "Size", kind: "number" },
457
+ ],
458
+ fields: [
459
+ { field: "name", label: "Audience", kind: "text", required: true },
460
+ { field: "stage", label: "Lifecycle stage", kind: "select", options: ["awareness", "consideration", "decision", "retention", "advocacy"] },
461
+ { field: "size", label: "Estimated size", kind: "number" },
462
+ { field: "geo", label: "Geo", kind: "text" },
463
+ { field: "tags", label: "Tags", kind: "tags" },
464
+ ],
465
+ filters: [{ field: "stage", label: "Stage", kind: "select", options: ["awareness", "consideration", "decision", "retention", "advocacy"] }],
466
+ actions: GROUP_ACTIONS,
467
+ },
468
+ {
469
+ key: "offers", label: "Offers", dimension: "things", type: "offer", icon: "Gift",
470
+ columns: [
471
+ { field: "name", label: "Offer", kind: "text", required: true },
472
+ { field: "price", label: "Price", kind: "currency" },
473
+ { field: "guarantee", label: "Guarantee", kind: "select", options: ["money-back", "better-than", "service", "conditional", "reverse"] },
474
+ ],
475
+ fields: [
476
+ { field: "name", label: "Offer", kind: "text", required: true },
477
+ { field: "dream_outcome", label: "Dream outcome", kind: "text" },
478
+ { field: "price", label: "Price", kind: "currency" },
479
+ { field: "guarantee", label: "Guarantee", kind: "select", options: ["money-back", "better-than", "service", "conditional", "reverse"] },
480
+ { field: "value_score", label: "Value score", kind: "number" },
481
+ { field: "tags", label: "Tags", kind: "tags" },
482
+ ],
483
+ filters: [{ field: "guarantee", label: "Guarantee", kind: "select", options: ["money-back", "better-than", "service", "conditional", "reverse"] }],
484
+ actions: THING_ACTIONS,
485
+ },
486
+ {
487
+ key: "content", label: "Content", dimension: "things", type: "content", icon: "FileText",
488
+ columns: [
489
+ { field: "name", label: "Title", kind: "text", required: true },
490
+ { field: "state", label: "State", kind: "select", options: ["draft", "review", "approved", "scheduled", "published", "live", "archived"] },
491
+ { field: "variant", label: "Variant", kind: "select", options: ["a", "b", "c"] },
492
+ ],
493
+ fields: [
494
+ { field: "name", label: "Title", kind: "text", required: true },
495
+ { field: "headline", label: "Headline", kind: "text" },
496
+ { field: "cta", label: "Call to action", kind: "text" },
497
+ { field: "image", label: "Creative", kind: "image" },
498
+ { field: "state", label: "Workflow state", kind: "select", options: ["draft", "review", "approved", "scheduled", "published", "live", "archived"] },
499
+ { field: "variant", label: "Variant", kind: "select", options: ["a", "b", "c"] },
500
+ { field: "publish_at", label: "Publish at", kind: "date" },
501
+ { field: "campaign", label: "Campaign", kind: "relation", options: ["campaigns"] },
502
+ { field: "offer", label: "Offer", kind: "relation", options: ["offers"] },
503
+ ],
504
+ filters: [{ field: "state", label: "State", kind: "select", options: ["draft", "review", "approved", "scheduled", "published", "live", "archived"] }],
505
+ actions: THING_ACTIONS,
506
+ },
507
+ {
508
+ key: "leads", label: "Leads", dimension: "actors", type: "lead", icon: "UserPlus",
509
+ columns: [
510
+ { field: "name", label: "Name", kind: "text", required: true },
511
+ { field: "lifecycle", label: "Lifecycle", kind: "select", options: ["anonymous", "lead", "mql", "sql", "customer", "advocate", "churned"] },
512
+ { field: "ltv", label: "LTV", kind: "currency" },
513
+ ],
514
+ fields: [
515
+ { field: "name", label: "Name", kind: "text", required: true },
516
+ { field: "lifecycle", label: "Lifecycle", kind: "select", options: ["anonymous", "lead", "mql", "sql", "customer", "advocate", "churned"] },
517
+ { field: "ltv", label: "Lifetime value", kind: "currency" },
518
+ { field: "cac", label: "Acquisition cost", kind: "currency" },
519
+ { field: "fit_score", label: "Fit score", kind: "number" },
520
+ { field: "consent", label: "Consent", kind: "tags" },
521
+ { field: "audience", label: "Audience", kind: "relation", options: ["audiences"] },
522
+ ],
523
+ filters: [{ field: "lifecycle", label: "Lifecycle", kind: "select", options: ["anonymous", "lead", "mql", "sql", "customer", "advocate", "churned"] }],
524
+ actions: ACTOR_ACTIONS,
525
+ },
526
+ {
527
+ key: "channels", label: "Channels", dimension: "groups", type: "channel", icon: "Radio",
528
+ columns: [
529
+ { field: "name", label: "Channel", kind: "text", required: true },
530
+ { field: "kind", label: "Kind", kind: "select", options: ["email", "meta", "google-ads", "tiktok", "seo", "sms"] },
531
+ { field: "roas", label: "ROAS", kind: "number" },
532
+ ],
533
+ fields: [
534
+ { field: "name", label: "Channel", kind: "text", required: true },
535
+ { field: "kind", label: "Kind", kind: "select", options: ["email", "meta", "google-ads", "tiktok", "seo", "sms"] },
536
+ { field: "spend", label: "Spend", kind: "currency" },
537
+ { field: "roas", label: "ROAS", kind: "number" },
538
+ ],
539
+ filters: [{ field: "kind", label: "Kind", kind: "select", options: ["email", "meta", "google-ads", "tiktok", "seo", "sms"] }],
540
+ actions: GROUP_ACTIONS,
541
+ },
542
+ {
543
+ key: "experiments", label: "Experiments", dimension: "learning", type: "experiment", icon: "FlaskConical",
544
+ columns: [
545
+ { field: "name", label: "Hypothesis", kind: "text", required: true },
546
+ { field: "status", label: "Status", kind: "select", options: ["backlog", "running", "shipped", "killed", "inconclusive"] },
547
+ { field: "metric", label: "Metric", kind: "select", options: ["ctr", "cvr", "aov", "ltv", "cac", "roas", "open-rate"] },
548
+ ],
549
+ fields: [
550
+ { field: "name", label: "Hypothesis", kind: "text", required: true },
551
+ { field: "metric", label: "Target metric", kind: "select", options: ["ctr", "cvr", "aov", "ltv", "cac", "roas", "open-rate"] },
552
+ { field: "status", label: "Status", kind: "select", options: ["backlog", "running", "shipped", "killed", "inconclusive"] },
553
+ { field: "ice_score", label: "ICE score", kind: "number" },
554
+ { field: "observed_lift", label: "Observed lift", kind: "number" },
555
+ { field: "p_value", label: "p-value", kind: "number" },
556
+ { field: "campaign", label: "Campaign", kind: "relation", options: ["campaigns"] },
557
+ ],
558
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["backlog", "running", "shipped", "killed", "inconclusive"] }],
559
+ actions: ["world:create-hypothesis"],
90
560
  },
91
561
  ],
92
- lms: [
562
+ // ── service — support desk: tickets worked by agents for customers ──────────
563
+ service: [
564
+ {
565
+ key: "tickets", label: "Tickets", dimension: "things", type: "ticket", icon: "Ticket",
566
+ columns: [
567
+ { field: "name", label: "Subject", kind: "text", required: true },
568
+ { field: "priority", label: "Priority", kind: "select", options: ["urgent", "high", "normal", "low"] },
569
+ { field: "status", label: "Status", kind: "select", options: ["open", "pending", "on-hold", "solved", "closed"] },
570
+ ],
571
+ fields: [
572
+ { field: "name", label: "Subject", kind: "text", required: true },
573
+ { field: "description", label: "Description", kind: "markdown" },
574
+ { field: "priority", label: "Priority", kind: "select", options: ["urgent", "high", "normal", "low"] },
575
+ { field: "status", label: "Status", kind: "select", options: ["open", "pending", "on-hold", "solved", "closed"] },
576
+ { field: "channel", label: "Channel", kind: "select", options: ["email", "chat", "phone", "web", "social"] },
577
+ { field: "customer", label: "Customer", kind: "relation", options: ["customers"] },
578
+ { field: "agent", label: "Assigned agent", kind: "relation", options: ["agents"] },
579
+ ],
580
+ filters: [
581
+ { field: "priority", label: "Priority", kind: "select", options: ["urgent", "high", "normal", "low"] },
582
+ { field: "status", label: "Status", kind: "select", options: ["open", "pending", "on-hold", "solved", "closed"] },
583
+ ],
584
+ actions: THING_ACTIONS,
585
+ },
93
586
  {
94
- key: "courses", label: "Courses", dimension: "things", type: "course", icon: "BookOpen",
587
+ key: "customers", label: "Customers", dimension: "actors", type: "customer", icon: "UserRound",
95
588
  columns: [
589
+ { field: "name", label: "Name", kind: "text", required: true },
590
+ { field: "tier", label: "Tier", kind: "select", options: ["free", "pro", "enterprise"] },
591
+ { field: "email", label: "Email", kind: "text" },
592
+ ],
593
+ fields: [
594
+ { field: "name", label: "Name", kind: "text", required: true },
595
+ { field: "email", label: "Email", kind: "text" },
596
+ { field: "tier", label: "Plan tier", kind: "select", options: ["free", "pro", "enterprise"] },
597
+ { field: "tags", label: "Tags", kind: "tags" },
598
+ ],
599
+ filters: [{ field: "tier", label: "Tier", kind: "select", options: ["free", "pro", "enterprise"] }],
600
+ actions: ACTOR_ACTIONS,
601
+ },
602
+ {
603
+ key: "agents", label: "Agents", dimension: "actors", type: "support-agent", icon: "Headset",
604
+ columns: [
605
+ { field: "name", label: "Name", kind: "text", required: true },
606
+ { field: "team", label: "Team", kind: "text" },
607
+ { field: "status", label: "Status", kind: "select", options: ["online", "away", "offline"] },
608
+ ],
609
+ fields: [
610
+ { field: "name", label: "Name", kind: "text", required: true },
611
+ { field: "team", label: "Team", kind: "text" },
612
+ { field: "status", label: "Status", kind: "select", options: ["online", "away", "offline"] },
613
+ { field: "tags", label: "Skills", kind: "tags" },
614
+ ],
615
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["online", "away", "offline"] }],
616
+ actions: ACTOR_ACTIONS,
617
+ },
618
+ {
619
+ key: "messages", label: "Messages", dimension: "events", type: "message", icon: "MessageSquare",
620
+ columns: [
621
+ { field: "name", label: "Message", kind: "text", required: true },
622
+ { field: "direction", label: "Direction", kind: "select", options: ["inbound", "outbound"] },
623
+ { field: "date", label: "Date", kind: "date" },
624
+ ],
625
+ fields: [
626
+ { field: "name", label: "Message", kind: "text", required: true },
627
+ { field: "body", label: "Body", kind: "markdown" },
628
+ { field: "direction", label: "Direction", kind: "select", options: ["inbound", "outbound"] },
629
+ { field: "date", label: "Date", kind: "date" },
630
+ { field: "ticket", label: "Ticket", kind: "relation", options: ["tickets"] },
631
+ ],
632
+ filters: [{ field: "direction", label: "Direction", kind: "select", options: ["inbound", "outbound"] }],
633
+ actions: [],
634
+ },
635
+ {
636
+ key: "articles", label: "Help articles", dimension: "things", type: "article", icon: "BookOpen",
637
+ columns: [
638
+ { field: "name", label: "Title", kind: "text", required: true },
639
+ { field: "category", label: "Category", kind: "text" },
640
+ { field: "status", label: "Status", kind: "select", options: ["draft", "published", "archived"] },
641
+ ],
642
+ fields: [
96
643
  { field: "name", label: "Title", kind: "text", required: true },
644
+ { field: "category", label: "Category", kind: "text" },
645
+ { field: "body", label: "Body", kind: "markdown" },
646
+ { field: "status", label: "Status", kind: "select", options: ["draft", "published", "archived"] },
97
647
  { field: "tags", label: "Tags", kind: "tags" },
98
648
  ],
649
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["draft", "published", "archived"] }],
650
+ actions: THING_ACTIONS,
651
+ },
652
+ ],
653
+ // ── education — courses, learners, lessons, mastery, attempts, certificates ──
654
+ education: [
655
+ {
656
+ key: "courses", label: "Courses", dimension: "groups", type: "course", icon: "BookOpen",
657
+ columns: [
658
+ { field: "name", label: "Title", kind: "text", required: true },
659
+ { field: "level", label: "Level", kind: "select", options: ["beginner", "intermediate", "advanced"] },
660
+ { field: "status", label: "Status", kind: "select", options: ["draft", "published", "archived"] },
661
+ ],
99
662
  fields: [
100
663
  { field: "name", label: "Title", kind: "text", required: true },
101
664
  { field: "description", label: "Description", kind: "markdown" },
102
665
  { field: "image", label: "Cover", kind: "image" },
666
+ { field: "level", label: "Level", kind: "select", options: ["beginner", "intermediate", "advanced"] },
667
+ { field: "status", label: "Status", kind: "select", options: ["draft", "published", "archived"] },
103
668
  { field: "tags", label: "Tags", kind: "tags" },
104
669
  ],
105
- filters: [{ field: "tags", label: "Tags", kind: "tags" }],
106
- actions: ["world:create-thing", "world:update-thing"],
670
+ filters: [{ field: "level", label: "Level", kind: "select", options: ["beginner", "intermediate", "advanced"] }],
671
+ actions: GROUP_ACTIONS,
672
+ },
673
+ {
674
+ key: "learners", label: "Learners", dimension: "actors", type: "learner", icon: "GraduationCap",
675
+ columns: [
676
+ { field: "name", label: "Name", kind: "text", required: true },
677
+ { field: "status", label: "Status", kind: "select", options: ["active", "paused", "completed"] },
678
+ { field: "email", label: "Email", kind: "text" },
679
+ ],
680
+ fields: [
681
+ { field: "name", label: "Name", kind: "text", required: true },
682
+ { field: "email", label: "Email", kind: "text" },
683
+ { field: "status", label: "Status", kind: "select", options: ["active", "paused", "completed"] },
684
+ { field: "tags", label: "Tags", kind: "tags" },
685
+ ],
686
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["active", "paused", "completed"] }],
687
+ actions: ACTOR_ACTIONS,
688
+ },
689
+ {
690
+ key: "lessons", label: "Lessons", dimension: "things", type: "lesson", icon: "PlayCircle",
691
+ columns: [
692
+ { field: "name", label: "Lesson", kind: "text", required: true },
693
+ { field: "kind", label: "Kind", kind: "select", options: ["reading", "video", "quiz", "interactive", "problem"] },
694
+ { field: "order", label: "Order", kind: "number" },
695
+ ],
696
+ fields: [
697
+ { field: "name", label: "Lesson", kind: "text", required: true },
698
+ { field: "kind", label: "Kind", kind: "select", options: ["reading", "video", "quiz", "interactive", "problem"] },
699
+ { field: "order", label: "Order", kind: "number" },
700
+ { field: "duration", label: "Duration (min)", kind: "number" },
701
+ { field: "course", label: "Course", kind: "relation", options: ["courses"] },
702
+ { field: "skill", label: "Teaches skill", kind: "relation", options: ["skills"] },
703
+ ],
704
+ filters: [{ field: "kind", label: "Kind", kind: "select", options: ["reading", "video", "quiz", "interactive", "problem"] }],
705
+ actions: THING_ACTIONS,
706
+ },
707
+ {
708
+ key: "skills", label: "Skills", dimension: "things", type: "skill", icon: "Award",
709
+ columns: [
710
+ { field: "name", label: "Skill", kind: "text", required: true },
711
+ { field: "domain", label: "Domain", kind: "tags" },
712
+ ],
713
+ fields: [
714
+ { field: "name", label: "Skill", kind: "text", required: true },
715
+ { field: "description", label: "Description", kind: "markdown" },
716
+ { field: "domain", label: "Domain", kind: "tags" },
717
+ ],
718
+ filters: [{ field: "domain", label: "Domain", kind: "tags" }],
719
+ actions: THING_ACTIONS,
107
720
  },
108
721
  {
109
722
  key: "enrollments", label: "Enrollments", dimension: "paths", type: "enrollment", icon: "UserCheck",
110
723
  columns: [
111
724
  { field: "name", label: "Enrollment", kind: "text", required: true },
112
- { field: "tags", label: "Status", kind: "select", options: ["active", "completed", "paused"] },
725
+ { field: "status", label: "Status", kind: "select", options: ["active", "completed", "paused", "dropped"] },
726
+ { field: "progress", label: "Progress", kind: "number" },
113
727
  ],
114
728
  fields: [
115
729
  { field: "name", label: "Enrollment", kind: "text", required: true },
116
- { field: "tags", label: "Status", kind: "select", options: ["active", "completed", "paused"] },
730
+ { field: "status", label: "Status", kind: "select", options: ["active", "completed", "paused", "dropped"] },
731
+ { field: "progress", label: "Progress %", kind: "number" },
732
+ { field: "enrolled_at", label: "Enrolled", kind: "date" },
733
+ { field: "learner", label: "Learner", kind: "relation", options: ["learners"] },
734
+ { field: "course", label: "Course", kind: "relation", options: ["courses"] },
735
+ ],
736
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["active", "completed", "paused", "dropped"] }],
737
+ actions: [],
738
+ },
739
+ {
740
+ key: "attempts", label: "Attempts", dimension: "events", type: "attempt", icon: "ClipboardCheck",
741
+ columns: [
742
+ { field: "name", label: "Attempt", kind: "text", required: true },
743
+ { field: "verb", label: "Verb", kind: "select", options: ["started", "submitted", "completed", "scored"] },
744
+ { field: "score", label: "Score", kind: "number" },
745
+ ],
746
+ fields: [
747
+ { field: "name", label: "Attempt", kind: "text", required: true },
748
+ { field: "verb", label: "Verb", kind: "select", options: ["started", "submitted", "completed", "scored"] },
749
+ { field: "correct", label: "Correct", kind: "select", options: ["yes", "no"] },
750
+ { field: "score", label: "Score", kind: "number" },
751
+ { field: "elapsed_ms", label: "Elapsed (ms)", kind: "number" },
752
+ { field: "learner", label: "Learner", kind: "relation", options: ["learners"] },
753
+ { field: "lesson", label: "Lesson", kind: "relation", options: ["lessons"] },
754
+ ],
755
+ filters: [{ field: "verb", label: "Verb", kind: "select", options: ["started", "submitted", "completed", "scored"] }],
756
+ actions: [],
757
+ },
758
+ {
759
+ key: "certificates", label: "Certificates", dimension: "things", type: "certificate", icon: "Medal",
760
+ columns: [
761
+ { field: "name", label: "Certificate", kind: "text", required: true },
762
+ { field: "issued_at", label: "Issued", kind: "date" },
763
+ ],
764
+ fields: [
765
+ { field: "name", label: "Certificate", kind: "text", required: true },
766
+ { field: "issued_at", label: "Issued", kind: "date" },
767
+ { field: "tags", label: "Tags", kind: "tags" },
768
+ { field: "learner", label: "Learner", kind: "relation", options: ["learners"] },
769
+ { field: "course", label: "Course", kind: "relation", options: ["courses"] },
770
+ ],
771
+ filters: [],
772
+ actions: THING_ACTIONS,
773
+ },
774
+ ],
775
+ // ── staff — people ops: employees in departments, roles, shifts, time-off ───
776
+ staff: [
777
+ {
778
+ key: "employees", label: "Employees", dimension: "actors", type: "employee", icon: "Users",
779
+ columns: [
780
+ { field: "name", label: "Name", kind: "text", required: true },
781
+ { field: "title", label: "Title", kind: "text" },
782
+ { field: "status", label: "Status", kind: "select", options: ["active", "on-leave", "offboarded"] },
783
+ ],
784
+ fields: [
785
+ { field: "name", label: "Name", kind: "text", required: true },
786
+ { field: "email", label: "Email", kind: "text" },
787
+ { field: "title", label: "Title", kind: "text" },
788
+ { field: "status", label: "Status", kind: "select", options: ["active", "on-leave", "offboarded"] },
789
+ { field: "start_date", label: "Start date", kind: "date" },
790
+ { field: "department", label: "Department", kind: "relation", options: ["departments"] },
791
+ { field: "role", label: "Role", kind: "relation", options: ["roles"] },
792
+ { field: "manager", label: "Manager", kind: "relation", options: ["employees"] },
793
+ ],
794
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["active", "on-leave", "offboarded"] }],
795
+ actions: ACTOR_ACTIONS,
796
+ },
797
+ {
798
+ key: "departments", label: "Departments", dimension: "groups", type: "department", icon: "Building2",
799
+ columns: [
800
+ { field: "name", label: "Department", kind: "text", required: true },
801
+ { field: "function", label: "Function", kind: "select", options: ["sales", "marketing", "service", "education", "engineering", "ops", "finance"] },
802
+ ],
803
+ fields: [
804
+ { field: "name", label: "Department", kind: "text", required: true },
805
+ { field: "function", label: "Function", kind: "select", options: ["sales", "marketing", "service", "education", "engineering", "ops", "finance"] },
806
+ { field: "head", label: "Department head", kind: "relation", options: ["employees"] },
807
+ ],
808
+ filters: [{ field: "function", label: "Function", kind: "select", options: ["sales", "marketing", "service", "education", "engineering", "ops", "finance"] }],
809
+ actions: GROUP_ACTIONS,
810
+ },
811
+ {
812
+ key: "roles", label: "Roles", dimension: "things", type: "role", icon: "BadgeCheck",
813
+ columns: [
814
+ { field: "name", label: "Role", kind: "text", required: true },
815
+ { field: "level", label: "Level", kind: "select", options: ["junior", "mid", "senior", "lead", "head"] },
816
+ ],
817
+ fields: [
818
+ { field: "name", label: "Role", kind: "text", required: true },
819
+ { field: "level", label: "Level", kind: "select", options: ["junior", "mid", "senior", "lead", "head"] },
820
+ { field: "description", label: "Description", kind: "markdown" },
821
+ ],
822
+ filters: [{ field: "level", label: "Level", kind: "select", options: ["junior", "mid", "senior", "lead", "head"] }],
823
+ actions: THING_ACTIONS,
824
+ },
825
+ {
826
+ key: "shifts", label: "Shifts", dimension: "events", type: "shift", icon: "CalendarClock",
827
+ columns: [
828
+ { field: "name", label: "Shift", kind: "text", required: true },
829
+ { field: "date", label: "Date", kind: "date" },
830
+ { field: "hours", label: "Hours", kind: "number" },
831
+ ],
832
+ fields: [
833
+ { field: "name", label: "Shift", kind: "text", required: true },
834
+ { field: "date", label: "Date", kind: "date" },
835
+ { field: "start", label: "Start time", kind: "text" },
836
+ { field: "end", label: "End time", kind: "text" },
837
+ { field: "hours", label: "Hours", kind: "number" },
838
+ { field: "employee", label: "Employee", kind: "relation", options: ["employees"] },
117
839
  ],
118
840
  filters: [],
119
841
  actions: [],
120
842
  },
843
+ {
844
+ key: "timeoff", label: "Time off", dimension: "events", type: "time-off", icon: "Plane",
845
+ columns: [
846
+ { field: "name", label: "Request", kind: "text", required: true },
847
+ { field: "kind", label: "Kind", kind: "select", options: ["vacation", "sick", "personal", "unpaid"] },
848
+ { field: "status", label: "Status", kind: "select", options: ["pending", "approved", "rejected"] },
849
+ ],
850
+ fields: [
851
+ { field: "name", label: "Request", kind: "text", required: true },
852
+ { field: "kind", label: "Kind", kind: "select", options: ["vacation", "sick", "personal", "unpaid"] },
853
+ { field: "start", label: "From", kind: "date" },
854
+ { field: "end", label: "To", kind: "date" },
855
+ { field: "status", label: "Status", kind: "select", options: ["pending", "approved", "rejected"] },
856
+ { field: "employee", label: "Employee", kind: "relation", options: ["employees"] },
857
+ ],
858
+ filters: [{ field: "status", label: "Status", kind: "select", options: ["pending", "approved", "rejected"] }],
859
+ actions: [],
860
+ },
861
+ {
862
+ key: "reviews", label: "Reviews", dimension: "things", type: "review", icon: "Star",
863
+ columns: [
864
+ { field: "name", label: "Review", kind: "text", required: true },
865
+ { field: "period", label: "Period", kind: "text" },
866
+ { field: "rating", label: "Rating", kind: "number" },
867
+ ],
868
+ fields: [
869
+ { field: "name", label: "Review", kind: "text", required: true },
870
+ { field: "period", label: "Period", kind: "text" },
871
+ { field: "rating", label: "Rating (1-5)", kind: "number" },
872
+ { field: "notes", label: "Notes", kind: "markdown" },
873
+ { field: "employee", label: "Employee", kind: "relation", options: ["employees"] },
874
+ ],
875
+ filters: [],
876
+ actions: THING_ACTIONS,
877
+ },
878
+ ],
879
+ // ── ecommerce — RELATION-FREE by contract (schema-canvas.test.ts) ───────────
880
+ ecommerce: [
881
+ {
882
+ key: "products", label: "Products", dimension: "things", type: "product", icon: "Package",
883
+ columns: [
884
+ { field: "name", label: "Name", kind: "text", required: true },
885
+ { field: "price", label: "Price", kind: "currency" },
886
+ { field: "tags", label: "Tags", kind: "tags" },
887
+ ],
888
+ fields: [
889
+ { field: "name", label: "Name", kind: "text", required: true },
890
+ { field: "price", label: "Price", kind: "currency" },
891
+ { field: "description", label: "Description", kind: "markdown" },
892
+ { field: "image", label: "Image", kind: "image" },
893
+ { field: "tags", label: "Tags", kind: "tags" },
894
+ ],
895
+ filters: [{ field: "tags", label: "Tags", kind: "tags" }],
896
+ actions: ["world:create-thing", "world:update-thing", "world:remove-thing"],
897
+ },
898
+ {
899
+ key: "orders", label: "Orders", dimension: "things", type: "order", icon: "ShoppingCart",
900
+ columns: [
901
+ { field: "name", label: "Order #", kind: "text", required: true },
902
+ { field: "price", label: "Total", kind: "currency" },
903
+ { field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] },
904
+ ],
905
+ fields: [
906
+ { field: "name", label: "Order #", kind: "text", required: true },
907
+ { field: "price", label: "Total", kind: "currency" },
908
+ { field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] },
909
+ ],
910
+ filters: [{ field: "tags", label: "Status", kind: "select", options: ["pending", "paid", "shipped", "cancelled"] }],
911
+ actions: ["world:update-thing"],
912
+ },
913
+ {
914
+ key: "customers", label: "Customers", dimension: "actors", type: "customer", icon: "Users",
915
+ columns: [
916
+ { field: "name", label: "Name", kind: "text", required: true },
917
+ { field: "tags", label: "Tags", kind: "tags" },
918
+ ],
919
+ fields: [
920
+ { field: "name", label: "Name", kind: "text", required: true },
921
+ { field: "tags", label: "Tags", kind: "tags" },
922
+ ],
923
+ filters: [],
924
+ actions: ["world:create-actor", "world:update-actor"],
925
+ },
121
926
  ],
122
- roofing: [
927
+ // ── roofers — lead → inspection → estimate → job, with crews ─────────────────
928
+ roofers: [
123
929
  {
124
930
  key: "leads", label: "Leads", dimension: "things", type: "lead", icon: "PhoneCall",
125
931
  columns: [
@@ -154,6 +960,7 @@ export const TEMPLATES = {
154
960
  { field: "name", label: "Inspection", kind: "text", required: true },
155
961
  { field: "date", label: "Date", kind: "date" },
156
962
  { field: "notes", label: "Site notes", kind: "markdown" },
963
+ { field: "lead", label: "Lead", kind: "relation", options: ["leads"] },
157
964
  { field: "tags", label: "Status", kind: "select", options: ["scheduled", "complete", "cancelled"] },
158
965
  ],
159
966
  filters: [{ field: "tags", label: "Status", kind: "select", options: ["scheduled", "complete", "cancelled"] }],
@@ -171,6 +978,7 @@ export const TEMPLATES = {
171
978
  { field: "price", label: "Total", kind: "currency" },
172
979
  { field: "material", label: "Material", kind: "text" },
173
980
  { field: "warranty", label: "Warranty", kind: "text" },
981
+ { field: "lead", label: "Lead", kind: "relation", options: ["leads"] },
174
982
  { field: "tags", label: "Status", kind: "select", options: ["pending", "accepted", "rejected", "insurance-pending"] },
175
983
  ],
176
984
  filters: [{ field: "tags", label: "Status", kind: "select", options: ["pending", "accepted", "rejected", "insurance-pending"] }],
@@ -190,6 +998,7 @@ export const TEMPLATES = {
190
998
  { field: "start_date", label: "Start date", kind: "date" },
191
999
  { field: "material", label: "Material", kind: "text" },
192
1000
  { field: "squares", label: "Squares", kind: "number" },
1001
+ { field: "estimate", label: "Estimate", kind: "relation", options: ["estimates"] },
193
1002
  { field: "crew", label: "Assigned crew", kind: "relation", options: ["crews"] },
194
1003
  { field: "tags", label: "Status", kind: "select", options: ["scheduled", "active", "completed", "invoiced"] },
195
1004
  ],
@@ -210,7 +1019,8 @@ export const TEMPLATES = {
210
1019
  actions: ["world:create-actor", "world:update-actor"],
211
1020
  },
212
1021
  ],
213
- moving: [
1022
+ // ── movers — quote → survey → move, with customers ──────────────────────────
1023
+ movers: [
214
1024
  {
215
1025
  key: "quotes", label: "Quotes", dimension: "things", type: "quote", icon: "FileSearch",
216
1026
  columns: [
@@ -226,6 +1036,7 @@ export const TEMPLATES = {
226
1036
  { field: "destination", label: "Destination address", kind: "text" },
227
1037
  { field: "move_date", label: "Move date", kind: "date" },
228
1038
  { field: "home_size", label: "Home size", kind: "select", options: ["studio", "1bed", "2bed", "3bed", "4bed+"] },
1039
+ { field: "customer", label: "Customer", kind: "relation", options: ["customers"] },
229
1040
  { field: "tags", label: "Status", kind: "select", options: ["open", "survey-booked", "deposit-paid", "lost"] },
230
1041
  ],
231
1042
  filters: [
@@ -246,6 +1057,7 @@ export const TEMPLATES = {
246
1057
  { field: "date", label: "Date", kind: "date" },
247
1058
  { field: "duration", label: "Duration (min)", kind: "number" },
248
1059
  { field: "notes", label: "Notes", kind: "markdown" },
1060
+ { field: "quote", label: "Quote", kind: "relation", options: ["quotes"] },
249
1061
  { field: "tags", label: "Status", kind: "select", options: ["scheduled", "complete", "cancelled"] },
250
1062
  ],
251
1063
  filters: [{ field: "tags", label: "Status", kind: "select", options: ["scheduled", "complete", "cancelled"] }],
@@ -285,36 +1097,71 @@ export const TEMPLATES = {
285
1097
  actions: ["world:create-actor", "world:update-actor"],
286
1098
  },
287
1099
  ],
288
- service: [
289
- {
290
- key: "jobs", label: "Jobs", dimension: "things", type: "job", icon: "Wrench",
291
- columns: [
292
- { field: "name", label: "Job", kind: "text", required: true },
293
- { field: "price", label: "Value", kind: "currency" },
294
- { field: "tags", label: "Status", kind: "select", options: ["open", "in-progress", "done", "invoiced"] },
295
- ],
296
- fields: [
297
- { field: "name", label: "Job", kind: "text", required: true },
298
- { field: "price", label: "Value", kind: "currency" },
299
- { field: "description", label: "Notes", kind: "markdown" },
300
- { field: "tags", label: "Status", kind: "select", options: ["open", "in-progress", "done", "invoiced"] },
301
- ],
302
- filters: [{ field: "tags", label: "Status", kind: "select", options: ["open", "in-progress", "done", "invoiced"] }],
303
- actions: ["world:create-thing", "world:update-thing"],
304
- },
305
- {
306
- key: "clients", label: "Clients", dimension: "actors", type: "client", icon: "User",
307
- columns: [
308
- { field: "name", label: "Name", kind: "text", required: true },
309
- { field: "tags", label: "Tags", kind: "tags" },
310
- ],
311
- fields: [
312
- { field: "name", label: "Name", kind: "text", required: true },
313
- { field: "tags", label: "Tags", kind: "tags" },
314
- ],
315
- filters: [],
316
- actions: ["world:create-actor", "world:update-actor"],
317
- },
318
- ],
319
1100
  };
1101
+ // ─── Manifest → TypeQL ───────────────────────────────────────────────────────
1102
+ // Compile a resource-type manifest into a TypeDB 3.0 schema string so it can be
1103
+ // rendered in the TQL schema viewer. The mapping is idiomatic TypeDB:
1104
+ // • nouns (groups · actors · things · learning) → `entity`
1105
+ // • flows (paths · events) with relation fields → `relation` (fields = roles)
1106
+ // • every non-relation field → `attribute`
1107
+ // • relation fields on an entity → a generated binary relation
1108
+ // The output is for visualisation/teaching, not for `define` against a live DB.
1109
+ const VTYPE = {
1110
+ text: "string", markdown: "string", select: "string", image: "string",
1111
+ tags: "string[]", number: "double", currency: "double", date: "datetime",
1112
+ relation: "string",
1113
+ };
1114
+ const ident = (s) => s.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "x";
1115
+ export function manifestToTql(types, title = "Workspace types") {
1116
+ const keys = new Set(types.map(t => t.key));
1117
+ const linksTo = (kind, opt) => kind === "relation" && !!opt?.[0] && keys.has(opt[0]);
1118
+ const attrs = new Map(); // field -> value type (first wins)
1119
+ const playsBy = new Map(); // entity key -> ["rel:role", …]
1120
+ const relations = [];
1121
+ const entities = [];
1122
+ const addPlays = (k, p) => { const a = playsBy.get(k) ?? []; a.push(p); playsBy.set(k, a); };
1123
+ for (const t of types)
1124
+ for (const f of t.fields)
1125
+ if (f.kind !== "relation" && !attrs.has(f.field))
1126
+ attrs.set(f.field, VTYPE[f.kind]);
1127
+ for (const t of types) {
1128
+ const tk = ident(t.key);
1129
+ const scalars = t.fields.filter(f => f.kind !== "relation").map(f => ident(f.field));
1130
+ const rels = t.fields.filter(f => linksTo(f.kind, f.options));
1131
+ const isFlow = (t.dimension === "paths" || t.dimension === "events") && rels.length > 0;
1132
+ if (isFlow) {
1133
+ relations.push({ name: tk, relates: rels.map(f => ident(f.field)), owns: scalars });
1134
+ for (const f of rels)
1135
+ addPlays(ident(f.options[0]), `${tk}:${ident(f.field)}`);
1136
+ }
1137
+ else {
1138
+ entities.push({ key: tk, label: t.label, dim: t.dimension, owns: scalars });
1139
+ for (const f of rels) {
1140
+ const rn = `${tk}_${ident(f.field)}`;
1141
+ relations.push({ name: rn, relates: ["src", "dst"], owns: [] });
1142
+ addPlays(tk, `${rn}:src`);
1143
+ addPlays(ident(f.options[0]), `${rn}:dst`);
1144
+ }
1145
+ }
1146
+ }
1147
+ const out = [`# ${title} — generated TypeDB 3.0 schema`, "", "define", ""];
1148
+ out.push("# ── Attributes ──────────────────────────");
1149
+ for (const [name, vt] of [...attrs.keys()].sort().map(k => [k, attrs.get(k)]))
1150
+ out.push(`attribute ${ident(name)}, value ${vt};`);
1151
+ out.push("");
1152
+ out.push("# ── Entities ────────────────────────────");
1153
+ for (const e of entities) {
1154
+ const body = [...e.owns.map((o, i) => `owns ${o}${i === 0 ? " @key" : ""}`), ...(playsBy.get(e.key) ?? []).map(p => `plays ${p}`)];
1155
+ out.push(`# ${e.label} · ${e.dim}`);
1156
+ out.push(body.length ? `entity ${e.key},\n ${body.join(",\n ")};` : `entity ${e.key};`);
1157
+ }
1158
+ if (relations.length) {
1159
+ out.push("", "# ── Relations ───────────────────────────");
1160
+ for (const r of relations) {
1161
+ const body = [...r.relates.map(x => `relates ${x}`), ...r.owns.map(o => `owns ${o}`)];
1162
+ out.push(`relation ${r.name},\n ${body.join(",\n ")};`);
1163
+ }
1164
+ }
1165
+ return out.join("\n") + "\n";
1166
+ }
320
1167
  //# sourceMappingURL=templates.js.map