@synap-core/workspace-templates 0.8.1 → 0.9.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synap-core/workspace-templates",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "description": "Canonical workspace definitions — YAML source + typed exports for browser, backend, CLI, and marketplace",
@@ -17,13 +17,13 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "type-check": "tsc --noEmit",
20
- "build:yaml": "node build.mjs",
20
+ "build:yaml": "node --experimental-strip-types build.mjs",
21
21
  "check:converters": "node scripts/check-converters.mjs",
22
22
  "check:concepts": "node scripts/check-concepts.mjs",
23
- "build": "node build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs",
23
+ "build": "node --experimental-strip-types build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs",
24
24
  "provision": "node scripts/provision.mjs",
25
25
  "eval": "node scripts/eval-standard.mjs",
26
- "prepublishOnly": "node build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs"
26
+ "prepublishOnly": "node --experimental-strip-types build.mjs && tsup && node scripts/check-converters.mjs && node scripts/check-concepts.mjs"
27
27
  },
28
28
  "dependencies": {
29
29
  "zod": "^3.0.0",
package/src/builder.yaml CHANGED
@@ -1290,7 +1290,7 @@ views:
1290
1290
  scopeProfileSlug: devplane_deployment
1291
1291
  startField: deployedAt
1292
1292
  config:
1293
- dateField: deployedAt
1293
+ dateField: properties.deployedAt
1294
1294
  colorBy: deployStatus
1295
1295
  description: "Deployment events on a calendar so release cadence is visible at a glance."
1296
1296
  - name: Packages
@@ -130,6 +130,8 @@ describe("Business Developer workspace overlay", () => {
130
130
  })),
131
131
  ).toEqual([
132
132
  { sourceProfileSlug: "deal", targetProfileSlug: "crm-commission-entitlement", type: "crm_commission_for_deal" },
133
+ { sourceProfileSlug: "person", targetProfileSlug: "campaign", type: "member_of" },
134
+ { sourceProfileSlug: "company", targetProfileSlug: "campaign", type: "member_of" },
133
135
  ]);
134
136
  });
135
137
 
@@ -280,6 +280,79 @@ profiles:
280
280
  valueType: string
281
281
  inputType: textarea
282
282
 
283
+ # ── Campaign (reused kind — SSOT is marketing.yaml) ───────────────────────
284
+ # NOT a new kind and NOT a CRM fork of one: this RE-DECLARES the
285
+ # marketing-campaign template's `campaign` so the CRM can attach people and
286
+ # companies to the campaigns that sourced them (`member_of`, below). Keeping
287
+ # `semanticSlug: marketing.campaign` is what makes it the SAME concept rather
288
+ # than a third one — the concept guard only errors when two DIFFERENT slugs
289
+ # claim one semanticSlug. `content-os.yaml` re-declares it exactly this way;
290
+ # this follows that precedent.
291
+ #
292
+ # It must live in THIS file, not in `crm.yaml` or `marketing.yaml`:
293
+ # `toWorkspaceDefinition` does NOT flatten dependencies, so only
294
+ # business-developer's OWN profiles/entityLinks reach an existing CRM
295
+ # workspace through `reconcileFromDefinition`. It is also what lets the
296
+ # `member_of` links below resolve — an entityLink endpoint must be an own
297
+ # profile, a SYSTEM_PROFILE, or dependency vocab, and `campaign` is none of
298
+ # the latter two.
299
+ #
300
+ # Scope is OMITTED (= workspace) to MATCH marketing's own declaration: the
301
+ # promotion to pod-wide is the resolver's job (resolve-profile-for-apply.ts
302
+ # same-actor workspace→shared branch), not ours to assert. Body kept verbatim
303
+ # so no property drift.
304
+ - slug: campaign
305
+ displayName: Campaign
306
+ icon: megaphone
307
+ color: pink
308
+ description: "A marketing campaign — email, social, event, content, or partnership"
309
+ semanticSlug: marketing.campaign
310
+ properties:
311
+ - slug: campaign-status
312
+ label: Status
313
+ valueType: string
314
+ inputType: select
315
+ enumValues: [planning, active, paused, completed, cancelled]
316
+ constraints: { defaultValue: planning }
317
+ - slug: campaign-channel
318
+ label: Channel
319
+ valueType: string
320
+ inputType: select
321
+ enumValues:
322
+ [email, social, event, content, partnership, paid, organic, other]
323
+ - slug: campaign-goal
324
+ label: Goal
325
+ valueType: string
326
+ inputType: textarea
327
+ - slug: campaign-target-audience
328
+ label: Target Audience
329
+ valueType: string
330
+ inputType: textarea
331
+ - slug: campaign-start-date
332
+ label: Start Date
333
+ valueType: date
334
+ inputType: date
335
+ - slug: campaign-end-date
336
+ label: End Date
337
+ valueType: date
338
+ inputType: date
339
+ - slug: campaign-budget
340
+ label: Budget
341
+ valueType: number
342
+ inputType: number
343
+ - slug: campaign-lead-count
344
+ label: Lead Count
345
+ valueType: number
346
+ inputType: number
347
+ - slug: campaign-conversion-rate
348
+ label: Conversion Rate (%)
349
+ valueType: number
350
+ inputType: number
351
+ - slug: campaign-notes
352
+ label: Notes
353
+ valueType: string
354
+ inputType: textarea
355
+
283
356
  entityLinks:
284
357
  # The existing generic `linked_to_deal` relation remains the deal ↔
285
358
  # counterparty connection. This overlay adds only the new auditable
@@ -288,6 +361,17 @@ entityLinks:
288
361
  targetProfileSlug: crm-commission-entitlement
289
362
  type: crm_commission_for_deal
290
363
  label: Commission entitlement
364
+ # Campaign membership — the sourcing edge between a counterparty and the
365
+ # campaign it came from. Same predicate for both endpoint kinds, mirroring
366
+ # CRM's own `linked_to_deal` person/company pair.
367
+ - sourceProfileSlug: person
368
+ targetProfileSlug: campaign
369
+ type: member_of
370
+ label: Member of
371
+ - sourceProfileSlug: company
372
+ targetProfileSlug: campaign
373
+ type: member_of
374
+ label: Member of
291
375
 
292
376
  # These are guided, proposal-first sessions — not background automations. They
293
377
  # never create a deal merely because a lead exists, and never convert a lead to
@@ -20,11 +20,22 @@ meta:
20
20
  # (identity, colors, fonts, assets, voice guide, rules, templates, components).
21
21
  # This overlay adds the STRATEGIC MESSAGING layer (positioning, narrative,
22
22
  # audience, differentiator) on top, so there is ONE brand vocabulary, not two.
23
+ #
24
+ # `require: foundation` alongside the `compose` is legal: the resolver caps
25
+ # COMPOSE deps at one per template (assertSingleComposeDep) and iterates the rest
26
+ # by relation — it does not cap the dependency list. It is also necessary: the
27
+ # four messaging kinds below are foundation's, not brand-library's, and a
28
+ # `scope: shared` reuse only dedups to the SSOT row if the owner is guaranteed to
29
+ # install first, which is exactly what a `require` buys (deps-first ordering).
23
30
  dependencies:
24
31
  - slug: brand-library
25
32
  kind: workspace
26
33
  relation: compose
27
34
  reason: "Layers positioning, narrative, and audience onto your Brand Library."
35
+ - slug: foundation
36
+ kind: workspace
37
+ relation: require
38
+ reason: "Owns the pod-wide positioning, narrative, audience, and differentiator vocabulary this workspace layers onto the brand."
28
39
 
29
40
  workspace:
30
41
  # Composes onto Brand Library: name + subtype MATCH the base (overlay contract),
@@ -40,13 +51,26 @@ workspace:
40
51
 
41
52
  profiles:
42
53
  # ═══════════════════════════════════════════════════════════════════════════
43
- # BRAND KINDS — verbatim from brand-library.yaml
54
+ # MESSAGING KINDS — verbatim from foundation.yaml (NOT brand-library)
55
+ #
56
+ # This block long carried a comment claiming these came from brand-library.
57
+ # They do not: brand-library declares none of them; `positioning`, `narrative`,
58
+ # `audience`, and `differentiator` are foundation's (foundation.yaml:138, :157,
59
+ # :89, :174). The wrong attribution had a real cost — with no `require:
60
+ # foundation` and no `scope: shared`, these four re-declarations minted a
61
+ # SECOND set of rows and the pod ended up with two positionings that could
62
+ # disagree. Both halves of the fix are now in place: the `require` above makes
63
+ # foundation install first (so its body is the SSOT base), and `scope: shared`
64
+ # makes provisioning dedup to that ONE pod-wide row and grant this workspace
65
+ # access instead of forking. Bodies stay verbatim so there is no property drift
66
+ # on the shared row.
44
67
  # ═══════════════════════════════════════════════════════════════════════════
45
68
  - slug: positioning
46
69
  displayName: Positioning
47
70
  icon: crosshair
48
71
  color: cyan
49
72
  description: "Market category, who we stand against, and why we win."
73
+ scope: shared
50
74
  properties:
51
75
  - slug: category
52
76
  label: Category
@@ -66,6 +90,7 @@ profiles:
66
90
  icon: book-open
67
91
  color: amber
68
92
  description: "The origin -> insight -> mission story arc."
93
+ scope: shared
69
94
  properties:
70
95
  - slug: arc
71
96
  label: Story arc
@@ -83,6 +108,7 @@ profiles:
83
108
  icon: users
84
109
  color: blue
85
110
  description: "An ideal customer profile — who we are built for."
111
+ scope: shared
86
112
  properties:
87
113
  - slug: segment
88
114
  label: Segment
@@ -106,6 +132,7 @@ profiles:
106
132
  icon: sparkles
107
133
  color: green
108
134
  description: "One thing only we do, believe, or refuse."
135
+ scope: shared
109
136
  properties:
110
137
  - slug: statement
111
138
  label: Statement