@synap-core/workspace-templates 0.12.0 → 0.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.
Files changed (245) hide show
  1. package/README.md +483 -0
  2. package/automations/bookmark-enrichment.automation.json +321 -0
  3. package/automations/github-pr-events.automation.json +505 -0
  4. package/automations/mail-feed.automation.json +86 -0
  5. package/automations/ship-a-change-checks-green.automation.json +106 -0
  6. package/automations/ship-a-change-checks-red.automation.json +106 -0
  7. package/automations/ship-a-change-deployed.automation.json +56 -0
  8. package/automations/ship-a-change-pr-merged.automation.json +110 -0
  9. package/automations/ship-a-change-pr-opened.automation.json +104 -0
  10. package/automations/url-bookmark-capture.automation.json +132 -0
  11. package/capabilities/agency-skills.capability.json +157 -0
  12. package/capabilities/apify.capability.json +265 -0
  13. package/capabilities/apollo.capability.json +120 -0
  14. package/capabilities/arch-backend.capability.json +97 -0
  15. package/capabilities/arch-client-intelligence.capability.json +401 -0
  16. package/capabilities/backblaze-b2.capability.json +205 -0
  17. package/capabilities/cal-com.capability.json +266 -0
  18. package/capabilities/canva.capability.json +274 -0
  19. package/capabilities/claude-code-routines.capability.json +172 -0
  20. package/capabilities/claude-managed-agents.capability.json +215 -0
  21. package/capabilities/client-comms-report.capability.json +246 -0
  22. package/capabilities/content-idea-to-production.capability.json +29 -0
  23. package/capabilities/datagouv.capability.json +315 -0
  24. package/capabilities/discord-bot.capability.json +157 -0
  25. package/capabilities/dropbox.capability.json +281 -0
  26. package/capabilities/exa.capability.json +109 -0
  27. package/capabilities/fal-ai.capability.json +83 -0
  28. package/capabilities/fal-media-lab.capability.json +158 -0
  29. package/capabilities/fireflies.capability.json +191 -0
  30. package/capabilities/freellmapi.capability.json +114 -0
  31. package/capabilities/generic-apikey.capability.json +92 -0
  32. package/capabilities/github-merge.capability.json +99 -0
  33. package/capabilities/github-read.capability.json +325 -0
  34. package/capabilities/governance-tighten-recommender.capability.json +81 -0
  35. package/capabilities/jev.capability.json +226 -0
  36. package/capabilities/lead-conversion-enrollment.capability.json +246 -0
  37. package/capabilities/mail-feed.capability.json +277 -0
  38. package/capabilities/mailgun.capability.json +52 -0
  39. package/capabilities/nango-google.capability.json +639 -0
  40. package/capabilities/notion.capability.json +417 -0
  41. package/capabilities/omniroute.capability.json +137 -0
  42. package/capabilities/posthog-analytics.capability.json +258 -0
  43. package/capabilities/postiz.capability.json +467 -0
  44. package/capabilities/proactive-digest.capability.json +190 -0
  45. package/capabilities/proton.capability.json +288 -0
  46. package/capabilities/replicate-rvm.capability.json +97 -0
  47. package/capabilities/research-methods.capability.json +57 -0
  48. package/capabilities/shopify.capability.json +395 -0
  49. package/capabilities/slack.capability.json +369 -0
  50. package/capabilities/stripe.capability.json +420 -0
  51. package/capabilities/telegram-bridge.capability.json +56 -0
  52. package/capabilities/treg.capability.json +102 -0
  53. package/capabilities/unipile-linkedin.capability.json +471 -0
  54. package/capabilities/vercel.capability.json +424 -0
  55. package/dist/define-CPS0n8BV.d.ts +1331 -0
  56. package/dist/index.d.ts +174 -900
  57. package/dist/index.js +26148 -23931
  58. package/dist/index.js.map +1 -1
  59. package/dist/wire.d.ts +222 -0
  60. package/dist/wire.js +77 -0
  61. package/dist/wire.js.map +1 -0
  62. package/package.json +27 -4
  63. package/packs/bookmark-views-pack.json +33 -0
  64. package/packs/crm-pack.json +76 -0
  65. package/packs/customer-profile.json +28 -0
  66. package/packs/deep-research.playbook.json +30 -0
  67. package/packs/event-views-pack.json +33 -0
  68. package/packs/habit-profile.json +27 -0
  69. package/packs/hr-pack.json +54 -0
  70. package/packs/invoice-profile.json +28 -0
  71. package/packs/knowledge-base-pack.json +56 -0
  72. package/packs/meeting-profile.json +29 -0
  73. package/packs/product-profile.json +30 -0
  74. package/packs/project-management-pack.json +49 -0
  75. package/packs/recipe-profile.json +29 -0
  76. package/packs/task-views-pack.json +39 -0
  77. package/retired.yaml +136 -0
  78. package/skills/app-factory-build-slice/SKILL.md +70 -0
  79. package/skills/app-factory-build-slice.catalog.json +17 -0
  80. package/skills/app-factory-design-canvas/SKILL.md +96 -0
  81. package/skills/app-factory-design-canvas.catalog.json +17 -0
  82. package/skills/app-factory-design-review/SKILL.md +99 -0
  83. package/skills/app-factory-design-review.catalog.json +18 -0
  84. package/skills/app-factory-research/SKILL.md +123 -0
  85. package/skills/app-factory-research.catalog.json +17 -0
  86. package/skills/bookmark-enrichment/SKILL.md +40 -0
  87. package/skills/carousel-format/carousel-format.skill.json +18 -0
  88. package/skills/collage-broll/collage-broll.skill.json +20 -0
  89. package/skills/content-taste/content-taste.skill.json +18 -0
  90. package/skills/creative-director/creative-director.skill.json +18 -0
  91. package/skills/design-critique/SKILL.md +135 -0
  92. package/skills/design-critique.catalog.json +12 -0
  93. package/skills/designing-with-real-components/SKILL.md +140 -0
  94. package/skills/designing-with-real-components.catalog.json +12 -0
  95. package/skills/edit-loop/edit-loop.skill.json +25 -0
  96. package/skills/facecam-edit/facecam-edit.skill.json +24 -0
  97. package/skills/mapping-the-server/SKILL.md +54 -0
  98. package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
  99. package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
  100. package/skills/open-design-edit/open-design-edit.skill.json +24 -0
  101. package/skills/reading-client-progress/SKILL.md +65 -0
  102. package/skills/routing-to-channels/SKILL.md +54 -0
  103. package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  104. package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  105. package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  106. package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  107. package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  108. package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  109. package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  110. package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  111. package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  112. package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  113. package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  114. package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  115. package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  116. package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  117. package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  118. package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  119. package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  120. package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  121. package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  122. package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  123. package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  124. package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  125. package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  126. package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  127. package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  128. package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  129. package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  130. package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  131. package/skills/video-direction/video-direction.skill.json +20 -0
  132. package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
  133. package/skills/video-toolbox/video-toolbox.skill.json +20 -0
  134. package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
  135. package/src/agency-os.yaml +122 -0
  136. package/src/agent-os-skill-slugs.test.ts +65 -0
  137. package/src/apify-social-reader.test.ts +317 -0
  138. package/src/base.yaml +85 -39
  139. package/src/blockchain-ecosystem.yaml +1 -0
  140. package/src/brand-library.yaml +525 -14
  141. package/src/builder-dev-fold.template.test.ts +113 -0
  142. package/src/builder.yaml +779 -91
  143. package/src/business-model-catalog-description.test.ts +47 -0
  144. package/src/business-model-grp.template.test.ts +214 -0
  145. package/src/business-model.yaml +296 -49
  146. package/src/catalog-safety.test.ts +198 -0
  147. package/src/catalog.template-items.test.ts +75 -0
  148. package/src/catalog.ts +66 -2
  149. package/src/concept-guard.test.ts +28 -8
  150. package/src/concept-guard.ts +2 -2
  151. package/src/content-creation.template.test.ts +52 -0
  152. package/src/content-creation.yaml +192 -0
  153. package/src/content-os.edit-loop.template.test.ts +210 -0
  154. package/src/content-os.publish-due-posts.template.test.ts +261 -0
  155. package/src/content-os.template.test.ts +322 -0
  156. package/src/content-os.video-takes.template.test.ts +338 -0
  157. package/src/content-os.work-a-take.template.test.ts +125 -0
  158. package/src/content-os.yaml +1830 -39
  159. package/src/content-studio-retired.template.test.ts +82 -0
  160. package/src/corpus-flow-validity.test.ts +4 -5
  161. package/src/crm-absorbs-outreach.template.test.ts +117 -0
  162. package/src/crm-build-plan.template.test.ts +128 -0
  163. package/src/crm-lists.template.test.ts +96 -0
  164. package/src/crm-messages-cadence.template.test.ts +164 -0
  165. package/src/crm-one-send-door.template.test.ts +65 -0
  166. package/src/crm.yaml +1456 -181
  167. package/src/decisions-wave.template.test.ts +186 -0
  168. package/src/define.ts +130 -17
  169. package/src/design-to-implementation.template.test.ts +94 -0
  170. package/src/domain-template-type.test.ts +75 -0
  171. package/src/ecosystem.yaml +27 -66
  172. package/src/enterprise-os.yaml +10 -134
  173. package/src/facecam-edit.audio-studio.test.ts +188 -0
  174. package/src/fal-generate-image.test.ts +200 -0
  175. package/src/fal-media-lab-pod-files.test.ts +180 -0
  176. package/src/finance.yaml +254 -1
  177. package/src/foundation.yaml +78 -38
  178. package/src/frame-host-modules.d.mts +6 -0
  179. package/src/frame-host-modules.mjs +58 -0
  180. package/src/generate.mjs +9 -3
  181. package/src/github-merge.test.ts +210 -0
  182. package/src/grants.yaml +46 -17
  183. package/src/hr.yaml +39 -6
  184. package/src/index.ts +36 -1
  185. package/src/intent.test.ts +161 -0
  186. package/src/internal-runbook.yaml +19 -1
  187. package/src/jev.test.ts +356 -0
  188. package/src/legal.yaml +33 -1
  189. package/src/life-os.yaml +51 -240
  190. package/src/lifecycle-coverage.test.ts +124 -0
  191. package/src/lifecycle-declarations.test.ts +252 -0
  192. package/src/lifecycle-presets.test.ts +468 -0
  193. package/src/lifecycle-presets.ts +494 -0
  194. package/src/lifecycle-stuck.test.ts +46 -0
  195. package/src/lifecycle-waits.parity.test.ts +319 -0
  196. package/src/marketing.yaml +108 -4
  197. package/src/meetings-knowledge.yaml +18 -0
  198. package/src/networking.yaml +23 -2
  199. package/src/operations.yaml +304 -4
  200. package/src/pipeline-views.template.test.ts +88 -0
  201. package/src/playbook-goal-placeholders.test.ts +57 -0
  202. package/src/postiz-contract.test.ts +134 -0
  203. package/src/presentation-declarations.test.ts +174 -0
  204. package/src/process-contract.validate.test.ts +352 -0
  205. package/src/project-management.yaml +39 -740
  206. package/src/publish-body.d.mts +29 -0
  207. package/src/publish-body.mjs +74 -0
  208. package/src/publish-body.test.ts +74 -0
  209. package/src/publish-official-lib.test.ts +176 -0
  210. package/src/replicate-rvm.test.ts +504 -0
  211. package/src/research-base.yaml +119 -20
  212. package/src/research-decision.yaml +60 -3
  213. package/src/reserved-profile-slugs.test.ts +83 -0
  214. package/src/retired-goal-kinds.test.ts +58 -0
  215. package/src/role-reference-and-step-domain.test.ts +192 -0
  216. package/src/seed-obligation.test.ts +1 -6
  217. package/src/send-message-maps.test.ts +227 -0
  218. package/src/shared-role-ssot.test.ts +40 -2
  219. package/src/ship-a-change.template.test.ts +177 -0
  220. package/src/skills.test.ts +170 -0
  221. package/src/social.yaml +85 -27
  222. package/src/space-packages.freshness.test.ts +97 -0
  223. package/src/space-packages.generated.ts +40 -0
  224. package/src/space-packages.test.ts +125 -0
  225. package/src/space-packages.ts +73 -0
  226. package/src/start.test.ts +188 -0
  227. package/src/task-intents.test.ts +293 -0
  228. package/src/templates.ts +20851 -21496
  229. package/src/types.ts +443 -60
  230. package/src/unipile-publish-post.test.ts +162 -0
  231. package/src/validate-flow.drift.test.ts +2 -3
  232. package/src/validate-flow.ts +22 -2
  233. package/src/validate-onboarding.test.ts +59 -0
  234. package/src/validate.test.ts +71 -4
  235. package/src/validate.ts +1335 -7
  236. package/src/wire-core.ts +231 -0
  237. package/src/wire.test.ts +159 -0
  238. package/src/wire.ts +107 -0
  239. package/src/agent-fleet.yaml +0 -1645
  240. package/src/business-developer.template.test.ts +0 -351
  241. package/src/business-developer.yaml +0 -860
  242. package/src/content-studio.yaml +0 -361
  243. package/src/dev-dashboard.yaml +0 -990
  244. package/src/outreach-comms.yaml +0 -486
  245. package/src/the-arch.yaml +0 -201
package/src/hr.yaml CHANGED
@@ -13,6 +13,12 @@ meta:
13
13
  color: "#E8663D"
14
14
  tags: [hr, people, hiring, team]
15
15
  isPublic: true
16
+ # What this space PRODUCES (homes): the purpose word, then its kinds/roles.
17
+ intent:
18
+ label: Team
19
+ produces:
20
+ - profile: candidate
21
+ - profile: review
16
22
 
17
23
  workspace:
18
24
  name: People
@@ -20,7 +26,10 @@ workspace:
20
26
  proposalId: hr-v1
21
27
  subtype: hr
22
28
  visibility: members
23
- workspaceType: operational
29
+ # A DOMAIN workspace that holds entities — NOT `operational`. `operational`
30
+ # marks an admin/system surface: `isDomainHomeWorkspace` refuses filing
31
+ # entities into it and excludes it from auto-placement (2026-09-25).
32
+ workspaceType: personal
24
33
  capabilities:
25
34
  - hr.team
26
35
  - hr.hiring
@@ -153,6 +162,17 @@ profiles:
153
162
  profileKind: role
154
163
  applicableKinds:
155
164
  - person
165
+ # The ONE lifecycle (work model L1): every option's category.
166
+ lifecycle:
167
+ property: stage
168
+ options:
169
+ - { key: applied, category: open }
170
+ - { key: screening, category: active }
171
+ - { key: interview, category: active }
172
+ - { key: offer, category: active }
173
+ - { key: hired, category: done }
174
+ - { key: rejected, category: lost }
175
+ - { key: withdrawn, category: lost }
156
176
  properties:
157
177
  - slug: stage
158
178
  label: Stage
@@ -195,6 +215,16 @@ profiles:
195
215
  icon: clipboard-check
196
216
  color: violet
197
217
  description: "A performance review within a company review cycle."
218
+ # The ONE lifecycle (work model L1): every option's category.
219
+ lifecycle:
220
+ property: status
221
+ options:
222
+ - { key: not-started, category: open }
223
+ - { key: self-review, category: active }
224
+ - { key: manager-review, category: active }
225
+ - { key: calibration, category: active }
226
+ - { key: shared, category: active }
227
+ - { key: complete, category: done }
198
228
  properties:
199
229
  - slug: cycle
200
230
  label: Cycle
@@ -315,13 +345,13 @@ views:
315
345
  ]
316
346
  - name: Hiring Pipeline
317
347
  slug: hiring-pipeline
318
- type: kanban
348
+ # The ONE board (work model L3, D3): columns ARE the candidate role's
349
+ # declared lifecycle (`stage`), moves go through the one mover.
350
+ type: pipeline
319
351
  scopeProfileSlug: candidate
320
- groupBy: stage
321
- description: Candidates moving through the pipeline, by stage.
322
352
  config:
323
- groupBy: stage
324
- cardFields: [source, rating, email]
353
+ profileSlug: candidate
354
+ description: Candidates moving through the pipeline, by stage.
325
355
  - name: Open Roles
326
356
  slug: open-roles
327
357
  type: table
@@ -430,10 +460,12 @@ onboarding:
430
460
  30/60/90 plan rather than ambiguity.
431
461
  collect:
432
462
  - profileSlug: team-member
463
+ min: 1
433
464
  what: The people currently on the team, with their role, department, and status.
434
465
  cardinality: several
435
466
  keyFields: [role-title, department, status]
436
467
  - profileSlug: role
468
+ min: 1
437
469
  what: The positions you're hiring for right now — open reqs with their hiring bar.
438
470
  cardinality: few
439
471
  keyFields: [status, department, seniority]
@@ -442,6 +474,7 @@ onboarding:
442
474
  cardinality: few
443
475
  keyFields: [stage, source]
444
476
  - profileSlug: review
477
+ min: 1
445
478
  what: The current review cycle and how you run performance (cadence + rubric).
446
479
  cardinality: one
447
480
  keyFields: [cycle, status]
package/src/index.ts CHANGED
@@ -50,13 +50,26 @@ export type {
50
50
  TemplatePlaybookParam,
51
51
  TemplateAutomation,
52
52
  TemplateActionPlacement,
53
+ TemplateIntent,
54
+ TemplateSpacePackage,
55
+ TemplateSpacePackageCell,
53
56
  } from "./types.js";
54
57
 
58
+ // A template's space package: its built frame modules (the base layer of the
59
+ // space's own experience, installed as `cell:<slug>:<key>`).
60
+ export { spacePackageCells } from "./space-packages.js";
61
+ export type {
62
+ BuiltSpacePackage,
63
+ BuiltSpacePackageCell,
64
+ SpacePackageCellWire,
65
+ } from "./space-packages.js";
66
+
55
67
  // The dependency-kind vocabulary as a VALUE — so consumers (marketplace,
56
68
  // onboarding, backend, hub zod, browser, CP zod) can `z.enum(PACKAGE_DEPENDENCY_KINDS)`
57
69
  // / derive their type from it instead of hand-copying the list. Six mirrors
58
70
  // exist today; this is the door that lets them stop being mirrors.
59
- export { PACKAGE_DEPENDENCY_KINDS } from "./types.js";
71
+ export { PACKAGE_DEPENDENCY_KINDS, INTENT_LABEL_MAX, START_VIEWS_MAX, START_EXAMPLE_RANGE, START_STARTERS_RANGE } from "./types.js";
72
+ export type { TemplateStart } from "./types.js";
60
73
 
61
74
  export {
62
75
  WORKSPACE_TEMPLATES,
@@ -73,6 +86,9 @@ export {
73
86
  assembleCatalog,
74
87
  computeTemplateUpdates,
75
88
  isHashVersion,
89
+ derivePlaybookItems,
90
+ TRACK_TEMPLATE_ITEM_KIND,
91
+ WORK_TEMPLATE_ITEM_KIND,
76
92
  } from "./catalog.js";
77
93
  export type {
78
94
  MergeCatalogInput,
@@ -133,6 +149,11 @@ export {
133
149
  SYSTEM_PROFILES,
134
150
  REQUIRED_META_FIELDS,
135
151
  REQUIRED_WORKSPACE_FIELDS,
152
+ lifecycleOptionWaitsProblems,
153
+ LIFECYCLE_OPTION_FIELDS,
154
+ LIFECYCLE_OPTION_WAITS_FIELDS,
155
+ LIFECYCLE_WAITS_ON,
156
+ LIFECYCLE_WAIT_KINDS,
136
157
  } from "./validate.js";
137
158
  export type {
138
159
  ValidationError,
@@ -141,6 +162,18 @@ export type {
141
162
  TemplateConverters,
142
163
  } from "./validate.js";
143
164
 
165
+ // The ten base lifecycles as template-time presets (`lifecycle.adopts`) and the
166
+ // ONE compiler that turns them into the flat declaration the pod stores. The
167
+ // catalog loader (scripts/catalog-lib.mjs) compiles authored YAML through it.
168
+ export { BASE_LIFECYCLES, resolveLifecyclePresets } from "./lifecycle-presets.js";
169
+ export type {
170
+ BaseLifecycleName,
171
+ TemplateLifecycleAdoption,
172
+ TemplateLifecycleStepAdd,
173
+ LifecyclePresetProblem,
174
+ LifecyclePresetResolution,
175
+ } from "./lifecycle-presets.js";
176
+
144
177
  // The pure, DB-free automation-flow node-contract validator. `validateTemplate`
145
178
  // runs it over every `automations[].flow` so broken flows (unknown node type,
146
179
  // dangling edge, cycle, typo'd channelType, verb-less capability node) fail at
@@ -165,6 +198,7 @@ export {
165
198
  toPackageDefinition,
166
199
  splitBentoBlocks,
167
200
  normalizeTemplateLayout,
201
+ toAutomationWire,
168
202
  } from "./define.js";
169
203
  export type {
170
204
  WorkspaceDefinitionInput,
@@ -173,4 +207,5 @@ export type {
173
207
  SplitBentoBlocks,
174
208
  BentoWidgetBlockWire,
175
209
  BentoViewBlockWire,
210
+ PackageAutomationWire,
176
211
  } from "./define.js";
@@ -0,0 +1,161 @@
1
+ /**
2
+ * `meta.intent` — what a space PRODUCES (object-first homes H0).
3
+ *
4
+ * Pinned:
5
+ * - each `intent-*` rule fires on a minimal broken template and stays silent
6
+ * on the fixed one (label length, unknown profile, profile without a
7
+ * lifecycle, duplicate, shape);
8
+ * - a produced profile may come from the dependency closure, and its
9
+ * lifecycle may be declared there (a role foundation owns);
10
+ * - (the catalog header half lives in `schema/intent-header.test.ts`);
11
+ * - BOTH install doors carry it (top-level `intent` on both), driven through
12
+ * the real converters.
13
+ */
14
+ import { describe, expect, it } from "vitest";
15
+ import { validateTemplate } from "./validate.js";
16
+ import { toPackageDefinition, toWorkspaceDefinition } from "./define.js";
17
+ import type { TemplateIntent, TemplateProfile, WorkspaceYaml } from "./types.js";
18
+ import { INTENT_LABEL_MAX } from "./types.js";
19
+ import { readYamlTemplates } from "./generate.mjs";
20
+
21
+ const lifecycled = (slug: string): TemplateProfile => ({
22
+ slug,
23
+ displayName: slug,
24
+ properties: [{ slug: "stage", label: "Stage", valueType: "string", inputType: "select", enumValues: ["a", "b"] }],
25
+ lifecycle: { property: "stage", options: [{ key: "a", category: "open" }, { key: "b", category: "done" }] },
26
+ } as TemplateProfile);
27
+ const plain = (slug: string): TemplateProfile => ({ slug, displayName: slug, properties: [] } as unknown as TemplateProfile);
28
+
29
+ const make = (slug: string, profiles: TemplateProfile[], intent?: TemplateIntent, deps: string[] = []): WorkspaceYaml =>
30
+ ({
31
+ meta: { slug, name: slug, description: "d", icon: "box", color: "blue", ...(intent ? { intent } : {}) },
32
+ workspace: { name: slug, description: "d" },
33
+ profiles,
34
+ dependencies: deps.map((d) => ({ slug: d, relation: "require" as const })),
35
+ }) as WorkspaceYaml;
36
+
37
+ const base = make("base-dep", [lifecycled("role-x"), plain("bare-dep")]);
38
+ const resolveDep = (s: string) => (s === "base-dep" ? base : undefined);
39
+ const intentErrors = (t: WorkspaceYaml) =>
40
+ validateTemplate(t, { resolveDep }).errors.filter((e) => e.rule.startsWith("intent-"));
41
+
42
+ describe("meta.intent — validator rules", () => {
43
+ it("a well-formed intent (own kind + a dependency's role) passes", () => {
44
+ const t = make("t", [lifecycled("thing")], { label: "Product", produces: [{ profile: "thing" }, { profile: "role-x" }] }, ["base-dep"]);
45
+ expect(intentErrors(t)).toEqual([]);
46
+ });
47
+
48
+ it("a library (label only, or empty produces) passes", () => {
49
+ expect(intentErrors(make("t", [plain("x")], { label: "Library" }))).toEqual([]);
50
+ expect(intentErrors(make("t", [plain("x")], { label: "Library", produces: [] }))).toEqual([]);
51
+ });
52
+
53
+ it("intent-label: empty or longer than the max is refused", () => {
54
+ expect(intentErrors(make("t", [plain("x")], { label: "" })).map((e) => e.rule)).toEqual(["intent-label"]);
55
+ const long = "x".repeat(INTENT_LABEL_MAX + 1);
56
+ expect(intentErrors(make("t", [plain("x")], { label: long })).map((e) => e.rule)).toEqual(["intent-label"]);
57
+ expect(intentErrors(make("t", [plain("x")], { label: "x".repeat(INTENT_LABEL_MAX) }))).toEqual([]);
58
+ });
59
+
60
+ it("intent-profile-unknown: a profile neither own nor in the closure", () => {
61
+ const errs = intentErrors(make("t", [lifecycled("thing")], { label: "P", produces: [{ profile: "ghost" }] }));
62
+ expect(errs.map((e) => e.rule)).toEqual(["intent-profile-unknown"]);
63
+ expect(errs[0]!.message).toContain('"ghost"');
64
+ });
65
+
66
+ it("intent-profile-unknown: a dependency's profile is NOT reachable without the dependency", () => {
67
+ const errs = intentErrors(make("t", [lifecycled("thing")], { label: "P", produces: [{ profile: "role-x" }] }));
68
+ expect(errs.map((e) => e.rule)).toEqual(["intent-profile-unknown"]);
69
+ });
70
+
71
+ it("intent-no-lifecycle: names the produced profile that declares none", () => {
72
+ const own = intentErrors(make("t", [plain("thing")], { label: "P", produces: [{ profile: "thing" }] }));
73
+ expect(own.map((e) => e.rule)).toEqual(["intent-no-lifecycle"]);
74
+ expect(own[0]!.message).toContain('"thing"');
75
+ const dep = intentErrors(make("t", [lifecycled("thing")], { label: "P", produces: [{ profile: "bare-dep" }] }, ["base-dep"]));
76
+ expect(dep.map((e) => e.rule)).toEqual(["intent-no-lifecycle"]);
77
+ });
78
+
79
+ it("a re-declaration without the lifecycle still counts the dependency's declaration", () => {
80
+ const t = make("t", [plain("role-x")], { label: "P", produces: [{ profile: "role-x" }] }, ["base-dep"]);
81
+ expect(intentErrors(t)).toEqual([]);
82
+ });
83
+
84
+ it("intent-duplicate and intent-shape", () => {
85
+ const dup = intentErrors(make("t", [lifecycled("thing")], { label: "P", produces: [{ profile: "thing" }, { profile: "thing" }] }));
86
+ expect(dup.map((e) => e.rule)).toEqual(["intent-duplicate"]);
87
+ const shape = intentErrors(make("t", [lifecycled("thing")], { label: "P", produces: "thing" } as unknown as TemplateIntent));
88
+ expect(shape.map((e) => e.rule)).toEqual(["intent-shape"]);
89
+ });
90
+
91
+ it("intent-no-workspace: an overlay or a pack declares no intent", () => {
92
+ const overlay = make("t", [lifecycled("thing")], { label: "P" });
93
+ overlay.dependencies = [{ slug: "base-dep", relation: "compose" }];
94
+ expect(intentErrors(overlay).map((e) => e.rule)).toEqual(["intent-no-workspace"]);
95
+ const pack = make("t", [lifecycled("thing")], { label: "P" });
96
+ pack.meta.tags = ["suite"];
97
+ expect(intentErrors(pack).map((e) => e.rule)).toEqual(["intent-no-workspace"]);
98
+ });
99
+
100
+ it("an unresolvable dependency does not fabricate an unknown-profile error", () => {
101
+ const t = make("t", [lifecycled("thing")], { label: "P", produces: [{ profile: "elsewhere" }] }, ["not-in-corpus"]);
102
+ expect(intentErrors(t)).toEqual([]);
103
+ });
104
+ });
105
+
106
+ describe("meta.intent — both install doors carry it", () => {
107
+ const intent = { label: "Product", produces: [{ profile: "thing" }] };
108
+ const t = make("t", [lifecycled("thing")], intent);
109
+
110
+ it("package door: top-level intent, never in _meta (the CP rebuilds _meta on serve)", () => {
111
+ const def = toPackageDefinition(t);
112
+ expect(def.intent).toEqual(intent);
113
+ expect("intent" in def._meta).toBe(false);
114
+ });
115
+
116
+ it("workspace door: intent", () => {
117
+ expect(toWorkspaceDefinition(t).definition.intent).toEqual(intent);
118
+ });
119
+
120
+ it("no intent → no field on either door", () => {
121
+ const none = make("t", [lifecycled("thing")]);
122
+ expect("intent" in toPackageDefinition(none)).toBe(false);
123
+ expect(toWorkspaceDefinition(none).definition.intent).toBeUndefined();
124
+ });
125
+ });
126
+
127
+ describe("meta.intent — the catalog", () => {
128
+ const corpus = readYamlTemplates().map((t) => t.data as WorkspaceYaml);
129
+ const table = Object.fromEntries(
130
+ corpus
131
+ .filter((t) => t.meta.intent)
132
+ .map((t) => [t.meta.slug, [t.meta.intent!.label, ...(t.meta.intent!.produces ?? []).map((p) => p.profile)]])
133
+ );
134
+
135
+ it("the whole corpus validates with zero intent-* errors", () => {
136
+ const errs = corpus.flatMap((t) =>
137
+ validateTemplate(t, { corpus }).errors.filter((e) => e.rule.startsWith("intent-")).map((e) => `${t.meta.slug} ${e.rule}: ${e.message}`)
138
+ );
139
+ expect(errs).toEqual([]);
140
+ });
141
+
142
+ it("the founder's table (homes plan §1), in produces order", () => {
143
+ expect(table["builder-workspace"]).toEqual(["Product", "devplane_feature", "devplane_story", "ui-design"]);
144
+ // outreach-touch joined when CRM absorbed Outreach & Comms (founder decision 2026-10-23):
145
+ // the messages drafted for a person to send are part of the commercial job.
146
+ expect(table["crm"]).toEqual(["Sales", "lead", "client", "deal", "campaign", "outreach-touch"]);
147
+ expect(table["operations"]).toEqual(["Delivery", "engagement", "deliverable"]);
148
+ expect(table["content-os"]).toEqual(["Content", "post", "newsletter", "campaign"]);
149
+ expect(table["finance"]).toEqual(["Finance", "invoice", "expense"]);
150
+ expect(table["research-base"]).toEqual(["Research", "hypothesis", "finding"]);
151
+ });
152
+
153
+ it("libraries carry a label only; overlays and packs carry none", () => {
154
+ for (const slug of ["brand-library", "foundation", "ecosystem"]) expect(table[slug], slug).toHaveLength(1);
155
+ for (const t of corpus) {
156
+ const ownsNone =
157
+ (t.dependencies ?? []).some((d) => d.relation === "compose") || (t.meta.tags ?? []).includes("suite");
158
+ if (ownsNone) expect(t.meta.intent, t.meta.slug).toBeUndefined();
159
+ }
160
+ });
161
+ });
@@ -14,6 +14,9 @@ meta:
14
14
  color: "#64748B"
15
15
  tags: [operations, sops, vendors, workflows]
16
16
  isPublic: true
17
+ # A LIBRARY: it produces nothing a home counts (homes).
18
+ intent:
19
+ label: Runbook
17
20
 
18
21
  workspace:
19
22
  name: Internal Runbook
@@ -26,7 +29,10 @@ workspace:
26
29
  # operations), so it must own its own identity.
27
30
  subtype: internal-runbook
28
31
  visibility: members
29
- workspaceType: operational
32
+ # A DOMAIN workspace that holds entities — NOT `operational`. `operational`
33
+ # marks an admin/system surface: `isDomainHomeWorkspace` refuses filing
34
+ # entities into it and excludes it from auto-placement (2026-09-25).
35
+ workspaceType: personal
30
36
  capabilities:
31
37
  - operations.sops
32
38
  - operations.vendors
@@ -90,6 +96,15 @@ profiles:
90
96
  color: "#0EA5E9"
91
97
  description: "A recurring business process — a repeatable flow with an owner, a cadence, and an input/output."
92
98
  scope: WORKSPACE
99
+ # The ONE lifecycle (work model L1): every option's category.
100
+ lifecycle:
101
+ property: workflow-status
102
+ options:
103
+ - { key: Backlog, category: open }
104
+ - { key: Active, category: active }
105
+ - { key: Blocked, category: paused }
106
+ - { key: Optimizing, category: active }
107
+ - { key: Retired, category: lost }
93
108
  properties:
94
109
  - slug: workflow-status
95
110
  label: Status
@@ -423,14 +438,17 @@ onboarding:
423
438
  person's head.
424
439
  collect:
425
440
  - profileSlug: sop
441
+ min: 1
426
442
  what: The core documented procedures / runbooks — the repeatable ways of doing key things.
427
443
  cardinality: several
428
444
  keyFields: [title, category, owner, sop-status]
429
445
  - profileSlug: workflow
446
+ min: 1
430
447
  what: The recurring processes that run the business, each with an owner and a cadence.
431
448
  cardinality: several
432
449
  keyFields: [title, workflow-status, frequency, owner]
433
450
  - profileSlug: vendor
451
+ min: 1
434
452
  what: The suppliers and service providers the company pays and depends on.
435
453
  cardinality: few
436
454
  keyFields: [title, vendor-category, monthly-cost, owner]