@synap-core/workspace-templates 0.6.0 → 0.6.2

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.
@@ -0,0 +1,567 @@
1
+ # Content OS workspace template
2
+ # The editorial engine — turn thinking into a published content system.
3
+ # View-driven workspace (app: stored / entity-detail), modeled on marketing.yaml.
4
+ #
5
+ # This is the EDITORIAL half that used to be absorbed inside content-studio.yaml.
6
+ # content-studio stays video/render-only; Content OS owns the publishing model:
7
+ # posts, pillars, platforms, and newsletters.
8
+
9
+ meta:
10
+ slug: content-os
11
+ name: Content OS
12
+ description: "Your editorial operating system — pillars, posts, platforms, and newsletters. Turn raw thinking into a published content engine, all connected to your campaigns and brand voice."
13
+ icon: pen-tool
14
+ color: "#6366F1"
15
+ tags: [content, editorial, publishing, creator, newsletter]
16
+ isPublic: true
17
+
18
+ # The editorial `post`/`newsletter` kinds REFERENCE profiles owned elsewhere:
19
+ # • `campaign` — the marketing-campaign template's kind. It ships pod-wide as a
20
+ # workspace-scoped `kind`; requiring marketing-campaign makes the resolver
21
+ # install it FIRST (deps-first), so its full property body is always the SSOT
22
+ # base. This template re-declares `campaign` below with the SAME body so the
23
+ # apply resolver REUSES + promotes marketing's row (same-user workspace→shared
24
+ # promotion in resolve-profile-for-apply.ts) rather than minting a duplicate.
25
+ # • `sponsor` — foundation's pod-wide `scope: shared` role. Requiring foundation
26
+ # guarantees its SSOT body is the base; this template re-declares `sponsor`
27
+ # below with `scope: shared` (identical to grants.yaml's shared-role reuse) so
28
+ # provisioning dedups to the ONE pod-wide row and grants THIS workspace access.
29
+ # Both are `require` (not `compose`): each dependency is a real standalone
30
+ # workspace, and this template creates its OWN workspace — it does not overlay onto
31
+ # theirs.
32
+ dependencies:
33
+ - slug: foundation
34
+ kind: workspace
35
+ relation: require
36
+ reason: "Provides the pod-wide sponsor role vocabulary this workspace's posts/newsletters reference."
37
+ - slug: marketing-campaign
38
+ kind: workspace
39
+ relation: require
40
+ reason: "Provides the campaign kind (and its SSOT property body) this workspace's posts/newsletters link to."
41
+
42
+ workspace:
43
+ name: Content OS
44
+ description: >
45
+ The editorial operating system — a view-driven workspace over your
46
+ content model. Organize by pillar, track every post from Idea to
47
+ Published, run your platforms and newsletters, and keep it all wired
48
+ to the campaigns and brand voice that give it context.
49
+ proposalId: content-os-v1
50
+ subtype: content-os
51
+ visibility: pod_visible
52
+ capabilities:
53
+ - content.editorial
54
+ - content.pillars
55
+ - content.calendar
56
+ - content.newsletter
57
+ sourceRoles:
58
+ brand: consumer
59
+ marketing: consumer
60
+ content: provider
61
+
62
+ profiles:
63
+ - slug: post
64
+ displayName: Post
65
+ icon: file-edit
66
+ color: "#6366F1"
67
+ description: "Your atomic content unit. One idea, multiple platform formats — tracked from spark to publication."
68
+ scope: WORKSPACE
69
+ properties:
70
+ - slug: post-status
71
+ label: Status
72
+ valueType: string
73
+ inputType: select
74
+ enumValues: ["Idea", "Briefed", "In Draft", "Review", "Scheduled", "Published", "Archived"]
75
+ - slug: post-format
76
+ label: Format
77
+ valueType: string
78
+ inputType: select
79
+ enumValues: ["Article", "Thread", "Short Video", "Long Video", "Carousel", "Image Post", "Story", "Newsletter Section", "Podcast Episode", "Other"]
80
+ - slug: post-platform
81
+ label: Platform
82
+ valueType: string
83
+ inputType: select
84
+ enumValues: ["LinkedIn", "Instagram", "TikTok", "YouTube", "Twitter / X", "Threads", "Bluesky", "Newsletter", "Podcast", "Blog", "Other"]
85
+ - slug: post-pillar
86
+ label: Pillar
87
+ valueType: entity_id
88
+ targetProfileSlug: pillar
89
+ inputType: entity_select
90
+ - slug: post-campaign
91
+ label: Campaign
92
+ valueType: entity_id
93
+ targetProfileSlug: campaign
94
+ inputType: entity_select
95
+ - slug: post-sponsor
96
+ label: Sponsor
97
+ valueType: entity_id
98
+ targetProfileSlug: sponsor
99
+ inputType: entity_select
100
+ - slug: publish-date
101
+ label: Publish Date
102
+ valueType: date
103
+ - slug: post-hook
104
+ label: "Hook / Headline"
105
+ valueType: string
106
+ inputType: text
107
+ placeholder: "Opening line or headline that stops the scroll"
108
+ - slug: post-cta
109
+ label: CTA
110
+ valueType: string
111
+ inputType: text
112
+ placeholder: "What should the audience do?"
113
+ - slug: post-tags
114
+ label: Tags
115
+ valueType: array
116
+ - slug: engagement-score
117
+ label: Engagement Score
118
+ valueType: number
119
+ inputType: number
120
+ placeholder: "Numeric performance metric (likes + comments + shares)"
121
+ - slug: post-reach
122
+ label: "Reach / Views"
123
+ valueType: number
124
+ inputType: number
125
+ - slug: repurposed-from
126
+ label: Repurposed From
127
+ valueType: string
128
+ inputType: text
129
+ placeholder: "Source post or content this was adapted from"
130
+ - slug: post-url
131
+ label: Published URL
132
+ valueType: string
133
+ inputType: text
134
+ placeholder: "https://..."
135
+ constraints: { format: url }
136
+
137
+ - slug: pillar
138
+ displayName: Pillar
139
+ icon: columns
140
+ color: "#8B5CF6"
141
+ description: "A recurring content theme — the lens through which your audience knows you. The organizing principle of your brand."
142
+ scope: WORKSPACE
143
+ properties:
144
+ - slug: pillar-status
145
+ label: Status
146
+ valueType: string
147
+ inputType: select
148
+ enumValues: ["Active", "Developing", "Paused", "Retired"]
149
+ - slug: pillar-audience
150
+ label: Audience Segment
151
+ valueType: string
152
+ inputType: text
153
+ placeholder: "Who does this pillar speak to?"
154
+ - slug: pillar-goal
155
+ label: Audience Goal
156
+ valueType: string
157
+ inputType: text
158
+ placeholder: "What should the audience think, feel, or do after consuming this pillar?"
159
+ - slug: pillar-formats
160
+ label: Primary Formats
161
+ valueType: string
162
+ inputType: select
163
+ enumValues: ["Articles + Threads", "Short Video", "Long Video", "Newsletter", "Podcast", "Carousel", "Mixed"]
164
+ - slug: post-count-pillar
165
+ label: Total Posts Published
166
+ valueType: number
167
+ inputType: number
168
+ - slug: avg-engagement
169
+ label: Avg Engagement Score
170
+ valueType: number
171
+ inputType: number
172
+ - slug: top-post
173
+ label: Top Performing Post
174
+ valueType: string
175
+ inputType: text
176
+ placeholder: "Title of best performing post in this pillar"
177
+ - slug: posting-frequency
178
+ label: Target Frequency
179
+ valueType: string
180
+ inputType: select
181
+ enumValues: ["Daily", "3x / week", "2x / week", "Weekly", "Bi-weekly", "Monthly", "Ad hoc"]
182
+ - slug: pillar-tags
183
+ label: Tags
184
+ valueType: array
185
+
186
+ - slug: platform
187
+ displayName: Platform
188
+ icon: share-2
189
+ color: "#06B6D4"
190
+ description: "A social account, newsletter publication, or podcast channel. Each platform has its own audience metrics and publishing rhythm."
191
+ scope: WORKSPACE
192
+ properties:
193
+ - slug: platform-type
194
+ label: Type
195
+ valueType: string
196
+ inputType: select
197
+ enumValues: ["LinkedIn", "Instagram", "TikTok", "YouTube", "Twitter / X", "Threads", "Bluesky", "Newsletter", "Podcast", "Blog / Website", "Other"]
198
+ - slug: platform-status
199
+ label: Status
200
+ valueType: string
201
+ inputType: select
202
+ enumValues: ["Active", "Building", "Maintenance Mode", "Inactive"]
203
+ - slug: platform-url
204
+ label: Profile URL
205
+ valueType: string
206
+ inputType: text
207
+ placeholder: "https://..."
208
+ constraints: { format: url }
209
+ - slug: follower-count
210
+ label: "Follower / Subscriber Count"
211
+ valueType: number
212
+ inputType: number
213
+ - slug: follower-target
214
+ label: Target Count
215
+ valueType: number
216
+ inputType: number
217
+ - slug: avg-engagement-rate
218
+ label: "Avg Engagement Rate (%)"
219
+ valueType: number
220
+ inputType: number
221
+ - slug: platform-posting-freq
222
+ label: Posting Frequency
223
+ valueType: string
224
+ inputType: select
225
+ enumValues: ["Daily", "3x / week", "2x / week", "Weekly", "Bi-weekly", "Monthly", "As Needed"]
226
+ - slug: last-post-date
227
+ label: Last Post Date
228
+ valueType: date
229
+ - slug: platform-notes
230
+ label: Notes
231
+ valueType: string
232
+ inputType: text
233
+ placeholder: "What works here? Tone, format, best times..."
234
+ - slug: platform-tags
235
+ label: Tags
236
+ valueType: array
237
+
238
+ - slug: newsletter
239
+ displayName: Newsletter
240
+ icon: mail
241
+ color: "#10B981"
242
+ description: "A newsletter issue — your direct channel to an owned audience. No algorithm, no platform fee, no intermediary."
243
+ scope: WORKSPACE
244
+ properties:
245
+ - slug: newsletter-status
246
+ label: Status
247
+ valueType: string
248
+ inputType: select
249
+ enumValues: ["Idea", "Drafting", "Editing", "Scheduled", "Sent", "Archived"]
250
+ - slug: newsletter-series
251
+ label: Series
252
+ valueType: string
253
+ inputType: text
254
+ placeholder: "Which newsletter series is this issue part of?"
255
+ - slug: issue-number
256
+ label: Issue Number
257
+ valueType: number
258
+ inputType: number
259
+ - slug: subject-line
260
+ label: Subject Line
261
+ valueType: string
262
+ inputType: text
263
+ placeholder: "The subject line — your most important copy"
264
+ - slug: preview-text
265
+ label: Preview Text
266
+ valueType: string
267
+ inputType: text
268
+ placeholder: "45-character preview shown in inbox (after subject)"
269
+ - slug: send-date
270
+ label: Send Date
271
+ valueType: date
272
+ - slug: subscriber-count
273
+ label: Subscriber Count at Send
274
+ valueType: number
275
+ inputType: number
276
+ - slug: open-rate
277
+ label: "Open Rate (%)"
278
+ valueType: number
279
+ inputType: number
280
+ - slug: click-rate
281
+ label: "Click Rate (%)"
282
+ valueType: number
283
+ inputType: number
284
+ - slug: newsletter-sponsor
285
+ label: Sponsor
286
+ valueType: entity_id
287
+ targetProfileSlug: sponsor
288
+ inputType: entity_select
289
+ - slug: newsletter-campaign
290
+ label: Campaign
291
+ valueType: entity_id
292
+ targetProfileSlug: campaign
293
+ inputType: entity_select
294
+ - slug: newsletter-pillar
295
+ label: Primary Pillar
296
+ valueType: entity_id
297
+ targetProfileSlug: pillar
298
+ inputType: entity_select
299
+ - slug: newsletter-tags
300
+ label: Tags
301
+ valueType: array
302
+
303
+ # ── Campaign (reused kind — SSOT is marketing.yaml) ───────────────────────
304
+ # NOT a new kind: this REFERENCES the marketing-campaign template's `campaign`.
305
+ # The `require: marketing-campaign` dependency installs it FIRST, so at apply
306
+ # the resolver finds marketing's workspace-scoped `campaign`, and — because it
307
+ # belongs to the same user — PROMOTES it to `shared` and grants THIS workspace
308
+ # access (resolve-profile-for-apply.ts: the same-actor workspace→shared branch),
309
+ # instead of minting a duplicate. Scope is OMITTED (= workspace) to MATCH
310
+ # marketing's own declaration: the promotion to pod-wide is the resolver's job,
311
+ # not ours to assert — and on the standalone-create fallback a workspace scope
312
+ # never usurps the pod-wide SSOT seat. Body kept verbatim so no property drift.
313
+ - slug: campaign
314
+ displayName: Campaign
315
+ icon: megaphone
316
+ color: pink
317
+ description: "A marketing campaign — email, social, event, content, or partnership"
318
+ semanticSlug: marketing.campaign
319
+ properties:
320
+ - slug: campaign-status
321
+ label: Status
322
+ valueType: string
323
+ inputType: select
324
+ enumValues: [planning, active, paused, completed, cancelled]
325
+ constraints: { defaultValue: planning }
326
+ - slug: campaign-channel
327
+ label: Channel
328
+ valueType: string
329
+ inputType: select
330
+ enumValues: [email, social, event, content, partnership, paid, organic, other]
331
+ - slug: campaign-goal
332
+ label: Goal
333
+ valueType: string
334
+ inputType: textarea
335
+ - slug: campaign-target-audience
336
+ label: Target Audience
337
+ valueType: string
338
+ inputType: textarea
339
+ - slug: campaign-start-date
340
+ label: Start Date
341
+ valueType: date
342
+ inputType: date
343
+ - slug: campaign-end-date
344
+ label: End Date
345
+ valueType: date
346
+ inputType: date
347
+ - slug: campaign-budget
348
+ label: Budget
349
+ valueType: number
350
+ inputType: number
351
+ - slug: campaign-lead-count
352
+ label: Lead Count
353
+ valueType: number
354
+ inputType: number
355
+ - slug: campaign-conversion-rate
356
+ label: Conversion Rate (%)
357
+ valueType: number
358
+ inputType: number
359
+ - slug: campaign-notes
360
+ label: Notes
361
+ valueType: string
362
+ inputType: textarea
363
+
364
+ # ── Sponsor (shared ecosystem role — SSOT is foundation.yaml) ─────────────
365
+ # `scope: shared` dedups to the ONE pod-wide `sponsor` role seeded by
366
+ # foundation; provisioning grants THIS workspace access to it (SHARED profiles
367
+ # are grant-gated, so the reference is required — dropping it would leave the
368
+ # role invisible here). Shape matches foundation verbatim, so no property drift.
369
+ - slug: sponsor
370
+ displayName: Sponsor
371
+ icon: hand-coins
372
+ color: amber
373
+ description: A backer or sponsor of a program, round, or event.
374
+ scope: shared
375
+ profileKind: role
376
+ applicableKinds:
377
+ - person
378
+ - company
379
+ properties:
380
+ - slug: sponsorStatus
381
+ label: Status
382
+ valueType: string
383
+ inputType: select
384
+ enumValues:
385
+ - prospect
386
+ - active
387
+ - paused
388
+ - slug: sponsorType
389
+ label: Sponsor Type
390
+ valueType: string
391
+ inputType: text
392
+ - slug: notes
393
+ label: Notes
394
+ valueType: string
395
+ inputType: textarea
396
+
397
+ # CURATED views — the editorial spine
398
+ views:
399
+ - name: Posts
400
+ slug: content-posts
401
+ type: kanban
402
+ scopeProfileSlug: post
403
+ groupBy: post-status
404
+ description: Content posts by status.
405
+ - name: Pillars
406
+ slug: content-pillars
407
+ type: list
408
+ scopeProfileSlug: pillar
409
+ description: Content pillars — the recurring themes.
410
+ - name: Platforms
411
+ slug: content-platforms
412
+ type: table
413
+ scopeProfileSlug: platform
414
+ description: Channels and their audience metrics.
415
+ - name: Newsletters
416
+ slug: content-newsletters
417
+ type: kanban
418
+ scopeProfileSlug: newsletter
419
+ groupBy: newsletter-status
420
+ description: Newsletter issues by status.
421
+
422
+ # App reference — view-driven, no custom app (mirrors marketing.yaml)
423
+ app:
424
+ kind: stored
425
+ cellKey: entity-detail
426
+
427
+ # Layout config for browser sidebar
428
+ layout:
429
+ pinnedApps: [dashboard, data]
430
+ defaultView: Posts
431
+ defaultApp: null
432
+ sidebarItems:
433
+ - kind: view
434
+ viewName: Posts
435
+ label: Posts
436
+ icon: file-edit
437
+ - kind: view
438
+ viewName: Pillars
439
+ label: Pillars
440
+ icon: columns
441
+ - kind: view
442
+ viewName: Platforms
443
+ label: Platforms
444
+ icon: share-2
445
+ - kind: view
446
+ viewName: Newsletters
447
+ label: Newsletters
448
+ icon: mail
449
+
450
+ # Bento dashboard
451
+ bento:
452
+ viewName: Posts
453
+ blocks:
454
+ - kind: view
455
+ viewName: Pillars
456
+ pos: { x: 0, y: 0, w: 6, h: 5 }
457
+ - kind: view
458
+ viewName: Newsletters
459
+ pos: { x: 6, y: 0, w: 6, h: 5 }
460
+
461
+ # Editorial graph: connect posts to campaign / pillar / sponsor
462
+ entityLinks:
463
+ - sourceProfileSlug: post
464
+ targetProfileSlug: campaign
465
+ type: part_of_campaign
466
+ label: Campaign
467
+ - sourceProfileSlug: post
468
+ targetProfileSlug: pillar
469
+ type: belongs_to_pillar
470
+ label: Pillar
471
+ - sourceProfileSlug: post
472
+ targetProfileSlug: sponsor
473
+ type: sponsored_by
474
+ label: Sponsor
475
+
476
+ # Playbooks — session templates seeded with the workspace.
477
+ # `subjectProfile` binds this playbook to the `post` kind so the Capture → Session
478
+ # matcher (playbooks.subject_profile->>'profileSlug') can offer it when a post is
479
+ # captured. The captured post is passed as the `post` entity param and substituted
480
+ # into the goalTemplate; the agent carries it from raw idea to a scheduled draft.
481
+ playbooks:
482
+ - name: Produce Content
483
+ description: Take a captured post idea and carry it from spark to a scheduled, platform-ready draft.
484
+ goalTemplate: >-
485
+ Produce content for {platform} from this idea ({post}). Take the raw idea,
486
+ shape it into a {format} draft with a scroll-stopping hook and a clear CTA,
487
+ keep it aligned to the post's pillar and the operator's brand voice, then
488
+ move it to Scheduled with a publish date.
489
+ subjectProfile:
490
+ profileSlug: post
491
+ params:
492
+ - { name: post, type: entity, label: Post, required: true }
493
+ - { name: platform, type: text, label: Platform, required: true }
494
+ - { name: format, type: text, label: Format }
495
+ executor: is-agent
496
+ status: active
497
+
498
+ onboarding:
499
+ goal: >
500
+ Stand up the editorial engine — the pillars you're known for, the platforms
501
+ you publish on, the cadence you can actually sustain, and a first post placed
502
+ in the system — so the workspace mirrors how you turn thinking into published
503
+ content, not a blank calendar.
504
+ framing: >
505
+ THE EDITOR-IN-CHIEF: Act as a seasoned content strategist who has grown owned
506
+ audiences from zero. Anchor everything on PILLARS — the two-to-four recurring
507
+ themes an audience comes to you for — because pillars are what make a content
508
+ engine compound instead of drift. Pull voice and identity from Brand and the
509
+ target audience from Foundation so every piece lands on-strategy. Push for a
510
+ cadence they can hold for a year, not a heroic month. Be concrete: real
511
+ platforms, real formats, a real first post — a fuzzy "I'll post more" helps
512
+ no one.
513
+ expertise:
514
+ starters:
515
+ - >
516
+ Pillars before posts. A creator known for 2–4 clear themes compounds; one
517
+ posting about everything stays noise. Propose naming the pillars FIRST —
518
+ each with the audience it speaks to and the transformation it delivers —
519
+ then hang every post off one of them.
520
+ - >
521
+ Cadence you can sustain beats cadence that impresses. A weekly post held
522
+ for a year outperforms daily-for-three-weeks-then-silence. Propose a
523
+ realistic per-platform frequency and let the calendar enforce it, rather
524
+ than an ambitious number that collapses.
525
+ - >
526
+ One idea, many formats — design for repurposing from the start. A single
527
+ strong thought becomes a thread, a short video, a newsletter section, and
528
+ a carousel. Propose the repurpose chain up front so one capture yields a
529
+ week of output, tracked via `repurposed-from`.
530
+ blindSpots:
531
+ - >
532
+ No pillars = brand drift. If they list a dozen post ideas but can't name
533
+ the 2–4 themes underneath, that's the gap — surface it before filling the
534
+ calendar, because unpillared posting never builds an identity.
535
+ - >
536
+ The owned-audience channel is the one most creators under-invest in. If
537
+ they're all-in on algorithmic platforms with no newsletter or list, name
538
+ it: the newsletter is the audience they actually own when a platform
539
+ changes the rules.
540
+ - >
541
+ Chasing every platform at once guarantees all of them stay shallow. If
542
+ they name five channels, push back: which ONE or two get real cadence and
543
+ attention now, and which are experiments?
544
+ bar: >
545
+ The workspace reads like a running publication: a handful of named pillars
546
+ each tied to an audience, the platforms actually committed to with a
547
+ sustainable cadence, an owned newsletter channel, and a first post placed in
548
+ a pillar — so a strategist could see at a glance what the brand is about,
549
+ where it publishes, and what ships next.
550
+ collect:
551
+ - profileSlug: pillar
552
+ what: The recurring themes you're known for — the 2–4 lenses your audience comes to you for, each with the segment it speaks to and the goal it serves.
553
+ cardinality: few
554
+ keyFields: [pillar-status, pillar-audience, pillar-formats]
555
+ - profileSlug: platform
556
+ what: The channels you actually publish on — the social accounts, newsletter, or podcast — with their status and the cadence you can hold.
557
+ cardinality: several
558
+ keyFields: [platform-type, platform-status, platform-posting-freq]
559
+ - profileSlug: post
560
+ what: A first post or two already in motion, each placed on a pillar and a platform at its true status (Idea through Published).
561
+ cardinality: few
562
+ keyFields: [post-status, post-format, post-platform]
563
+ openingQuestions:
564
+ - "What are the 2–4 themes your audience comes to you for — the pillars everything you make ties back to?"
565
+ - "Which platforms do you actually publish on, and what cadence can you genuinely sustain on each?"
566
+ - "Do you run (or want) a newsletter — the audience you own outright — and what's the first post you'd place in the system?"
567
+ doneWhen: at least one pillar + one platform with a cadence + a first post placed on a pillar.