@synap-core/workspace-templates 0.5.0 → 0.6.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.
@@ -1,24 +1,63 @@
1
1
  # Content OS workspace template
2
- # Creator Operations / publishing hub — campaigns, posts, pillars, platforms,
3
- # newsletters, and brand deals in one relational workspace.
4
- # Distinct from content-studio.yaml (video render pipeline) — both are kept.
5
- # AI-generation-friendly YAML. Validated against types.ts at build time.
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.
6
8
 
7
9
  meta:
8
10
  slug: content-os
9
11
  name: Content OS
10
- description: "A sovereign content operating system. Plan campaigns, manage posts across platforms, track brand deals, and own your newsletter audience — all in one relational workspace you control forever."
11
- icon: file-edit
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
12
14
  color: "#6366F1"
13
- tags: [content, creator, social-media, newsletter, campaigns]
15
+ tags: [content, editorial, publishing, creator, newsletter]
14
16
  isPublic: true
15
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
+
16
42
  workspace:
17
- name: "Content OS — Creator Operations Hub"
18
- description: "A sovereign content operating system. Plan campaigns, manage posts across platforms, track brand deals, and own your newsletter audience — all in one relational workspace you control forever."
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.
19
49
  proposalId: content-os-v1
20
- subtype: content-studio
21
- visibility: members
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
22
61
 
23
62
  profiles:
24
63
  - slug: post
@@ -46,12 +85,18 @@ profiles:
46
85
  - slug: post-pillar
47
86
  label: Pillar
48
87
  valueType: entity_id
88
+ targetProfileSlug: pillar
89
+ inputType: entity_select
49
90
  - slug: post-campaign
50
91
  label: Campaign
51
92
  valueType: entity_id
93
+ targetProfileSlug: campaign
94
+ inputType: entity_select
52
95
  - slug: post-sponsor
53
96
  label: Sponsor
54
97
  valueType: entity_id
98
+ targetProfileSlug: sponsor
99
+ inputType: entity_select
55
100
  - slug: publish-date
56
101
  label: Publish Date
57
102
  valueType: date
@@ -89,64 +134,6 @@ profiles:
89
134
  placeholder: "https://..."
90
135
  constraints: { format: url }
91
136
 
92
- - slug: campaign
93
- displayName: Campaign
94
- icon: target
95
- color: "#F59E0B"
96
- description: "A grouped content initiative — product launch, editorial series, seasonal push, or challenge."
97
- scope: WORKSPACE
98
- properties:
99
- - slug: campaign-status
100
- label: Status
101
- valueType: string
102
- inputType: select
103
- enumValues: ["Planned", "Active", "Completed", "Paused", "Cancelled"]
104
- - slug: campaign-type
105
- label: Type
106
- valueType: string
107
- inputType: select
108
- enumValues: ["Product Launch", "Editorial Series", "Seasonal", "Brand Partnership", "Challenge", "Evergreen", "Awareness", "Other"]
109
- - slug: campaign-pillar
110
- label: Primary Pillar
111
- valueType: entity_id
112
- - slug: campaign-start
113
- label: Start Date
114
- valueType: date
115
- - slug: campaign-end
116
- label: End Date
117
- valueType: date
118
- - slug: campaign-goal
119
- label: Goal
120
- valueType: string
121
- inputType: text
122
- placeholder: "What does success look like? Followers, sign-ups, revenue..."
123
- - slug: campaign-kpi
124
- label: Primary KPI
125
- valueType: string
126
- inputType: select
127
- enumValues: ["Follower Growth", "Newsletter Sign-ups", "Engagement Rate", "Revenue", "Reach", "Brand Deals Closed", "Link Clicks", "Other"]
128
- - slug: kpi-target
129
- label: KPI Target
130
- valueType: number
131
- inputType: number
132
- placeholder: "Numeric target for the primary KPI"
133
- - slug: kpi-result
134
- label: KPI Result
135
- valueType: number
136
- inputType: number
137
- placeholder: "Fill when complete"
138
- - slug: campaign-budget
139
- label: "Budget (€)"
140
- valueType: number
141
- inputType: number
142
- - slug: post-count
143
- label: Post Count
144
- valueType: number
145
- inputType: number
146
- - slug: campaign-tags
147
- label: Tags
148
- valueType: array
149
-
150
137
  - slug: pillar
151
138
  displayName: Pillar
152
139
  icon: columns
@@ -297,768 +284,262 @@ profiles:
297
284
  - slug: newsletter-sponsor
298
285
  label: Sponsor
299
286
  valueType: entity_id
287
+ targetProfileSlug: sponsor
288
+ inputType: entity_select
300
289
  - slug: newsletter-campaign
301
290
  label: Campaign
302
291
  valueType: entity_id
292
+ targetProfileSlug: campaign
293
+ inputType: entity_select
303
294
  - slug: newsletter-pillar
304
295
  label: Primary Pillar
305
296
  valueType: entity_id
297
+ targetProfileSlug: pillar
298
+ inputType: entity_select
306
299
  - slug: newsletter-tags
307
300
  label: Tags
308
301
  valueType: array
309
302
 
310
- - slug: sponsor
311
- displayName: Sponsor
312
- icon: briefcase
313
- color: "#F43F5E"
314
- description: "A brand deal or sponsorship — from first contact to final payment. Track deliverables, deadlines, and performance in one place."
315
- scope: WORKSPACE
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
316
319
  properties:
317
- - slug: deal-status
318
- label: Deal Status
320
+ - slug: campaign-status
321
+ label: Status
319
322
  valueType: string
320
323
  inputType: select
321
- enumValues: ["Prospecting", "Outreach Sent", "In Negotiation", "Contract Signed", "Active", "Deliverables Pending", "Completed", "Invoiced", "Paid", "Declined"]
322
- - slug: sponsor-type
323
- label: Deal Type
324
+ enumValues: [planning, active, paused, completed, cancelled]
325
+ constraints: { defaultValue: planning }
326
+ - slug: campaign-channel
327
+ label: Channel
324
328
  valueType: string
325
329
  inputType: select
326
- enumValues: ["Newsletter Sponsorship", "Social Post", "Podcast Ad", "Video Integration", "Affiliate", "Product Review", "Ambassador", "Event", "Other"]
327
- - slug: contact-name
328
- label: Contact Name
330
+ enumValues: [email, social, event, content, partnership, paid, organic, other]
331
+ - slug: campaign-goal
332
+ label: Goal
329
333
  valueType: string
330
- inputType: text
331
- placeholder: "Brand contact / campaign manager"
332
- - slug: contact-email
333
- label: Contact Email
334
+ inputType: textarea
335
+ - slug: campaign-target-audience
336
+ label: Target Audience
334
337
  valueType: string
335
- inputType: text
336
- placeholder: "contact@brand.com"
337
- constraints: { format: email }
338
- - slug: deal-value
339
- label: "Deal Value (€)"
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
340
353
  valueType: number
341
354
  inputType: number
342
- - slug: payment-status
343
- label: Payment Status
355
+ - slug: campaign-conversion-rate
356
+ label: Conversion Rate (%)
357
+ valueType: number
358
+ inputType: number
359
+ - slug: campaign-notes
360
+ label: Notes
344
361
  valueType: string
345
- inputType: select
346
- enumValues: ["Not Yet Due", "Invoice Sent", "Paid", "Overdue", "Disputed"]
347
- - slug: contract-date
348
- label: Contract Date
349
- valueType: date
350
- - slug: deliverable-deadline
351
- label: Deliverable Deadline
352
- valueType: date
353
- - slug: exclusivity-end
354
- label: Exclusivity End Date
355
- valueType: date
356
- - slug: deliverables
357
- label: Deliverables
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
358
382
  valueType: string
359
- inputType: text
360
- placeholder: "What exactly was promised? (2 posts, 1 newsletter mention, story, etc.)"
361
- - slug: talking-points
362
- label: Key Talking Points
383
+ inputType: select
384
+ enumValues:
385
+ - prospect
386
+ - active
387
+ - paused
388
+ - slug: sponsorType
389
+ label: Sponsor Type
363
390
  valueType: string
364
391
  inputType: text
365
- placeholder: "Brand messages, required mentions, do-not-say list"
366
- - slug: performance-notes
367
- label: Performance Notes
392
+ - slug: notes
393
+ label: Notes
368
394
  valueType: string
369
- inputType: text
370
- placeholder: "What results did you report to the brand?"
371
- - slug: sponsor-tags
372
- label: Tags
373
- valueType: array
395
+ inputType: textarea
374
396
 
397
+ # CURATED views — the editorial spine
375
398
  views:
376
- - name: Content Calendar
377
- type: calendar
378
- scopeProfileSlug: post
379
- config:
380
- dateField: publish-date
381
- colorField: post-platform
382
- - name: Post Pipeline
399
+ - name: Posts
400
+ slug: content-posts
383
401
  type: kanban
384
402
  scopeProfileSlug: post
385
- config:
386
- groupByField: post-status
387
- cardFields: [post-format, post-platform, post-pillar, publish-date]
388
- - name: All Posts
389
- type: table
390
- scopeProfileSlug: post
391
- config:
392
- visibleColumns: [post-status, post-format, post-platform, post-pillar, publish-date, engagement-score, post-reach]
393
- defaultSort: { field: publish-date, direction: desc }
394
- rowHeight: default
395
- - name: Ideas Inbox
396
- type: kanban
397
- scopeProfileSlug: post
398
- config:
399
- groupByField: post-format
400
- cardFields: [post-format, post-status, post-hook, post-tags]
401
- - name: Repurposing Queue
402
- type: table
403
- scopeProfileSlug: post
404
- config:
405
- visibleColumns: [post-status, post-format, post-platform, post-pillar, repurposed-from, publish-date]
406
- defaultSort: { field: post-platform, direction: asc }
407
- rowHeight: default
408
- - name: Content Graph
409
- type: graph
410
- scopeProfileSlugs: [post, campaign, pillar, newsletter, sponsor]
411
- config:
412
- layout: force
413
- showRelations: true
414
- - name: Campaigns
415
- type: kanban
416
- scopeProfileSlug: campaign
417
- config:
418
- groupByField: campaign-status
419
- cardFields: [campaign-type, campaign-pillar, campaign-start, campaign-end, campaign-kpi]
420
- - name: Campaign Timeline
421
- type: timeline
422
- scopeProfileSlug: campaign
423
- config:
424
- timeField: campaign-end
425
- groupByField: campaign-status
403
+ groupBy: post-status
404
+ description: Content posts by status.
426
405
  - name: Pillars
427
- type: table
406
+ slug: content-pillars
407
+ type: list
428
408
  scopeProfileSlug: pillar
429
- config:
430
- visibleColumns: [pillar-status, pillar-audience, pillar-goal, pillar-formats, posting-frequency, post-count-pillar, avg-engagement]
431
- defaultSort: { field: pillar-status, direction: asc }
432
- rowHeight: default
409
+ description: Content pillars — the recurring themes.
433
410
  - name: Platforms
411
+ slug: content-platforms
434
412
  type: table
435
413
  scopeProfileSlug: platform
436
- config:
437
- visibleColumns: [platform-type, platform-status, follower-count, follower-target, avg-engagement-rate, platform-posting-freq, last-post-date]
438
- defaultSort: { field: follower-count, direction: desc }
439
- rowHeight: default
440
- - name: Newsletter Issues
441
- type: table
442
- scopeProfileSlug: newsletter
443
- config:
444
- visibleColumns: [newsletter-status, newsletter-series, issue-number, subject-line, send-date, subscriber-count, open-rate, click-rate, newsletter-sponsor]
445
- defaultSort: { field: send-date, direction: desc }
446
- rowHeight: default
447
- - name: Newsletter Pipeline
414
+ description: Channels and their audience metrics.
415
+ - name: Newsletters
416
+ slug: content-newsletters
448
417
  type: kanban
449
418
  scopeProfileSlug: newsletter
450
- config:
451
- groupByField: newsletter-status
452
- cardFields: [newsletter-series, issue-number, subject-line, send-date, newsletter-sponsor]
453
- - name: Newsletter Calendar
454
- type: calendar
455
- scopeProfileSlug: newsletter
456
- config:
457
- dateField: send-date
458
- colorField: newsletter-status
459
- - name: Sponsor Tracker
460
- type: kanban
461
- scopeProfileSlug: sponsor
462
- config:
463
- groupByField: deal-status
464
- cardFields: [sponsor-type, deal-value, deliverable-deadline, payment-status]
465
- - name: Sponsors Table
466
- type: table
467
- scopeProfileSlug: sponsor
468
- config:
469
- visibleColumns: [deal-status, sponsor-type, contact-name, deal-value, payment-status, deliverable-deadline, exclusivity-end]
470
- defaultSort: { field: deliverable-deadline, direction: asc }
471
- rowHeight: default
472
- - name: Sponsor Deadlines
473
- type: calendar
474
- scopeProfileSlug: sponsor
475
- config:
476
- dateField: deliverable-deadline
477
- colorField: payment-status
478
- - name: Post
479
- type: bento
480
- scopeProfileSlug: post
481
- config:
482
- layout: bento
483
- blocks:
484
- - id: post-header
485
- kind: widget
486
- widgetType: section-header
487
- pos: { x: 0, y: 0, w: 12, h: 2 }
488
- config: { title: Posts, icon: FileEdit, color: "#6366F1", profileSlug: post }
489
- - id: post-count
490
- kind: widget
491
- widgetType: stat-card
492
- pos: { x: 0, y: 2, w: 3, h: 3 }
493
- config: { label: Total Posts, aggregation: count, profileSlug: post, color: "#6366F1" }
494
- - id: post-pipeline
495
- kind: view
496
- viewName: Post Pipeline
497
- pos: { x: 0, y: 9, w: 12, h: 8 }
498
- - id: post-cal
499
- kind: view
500
- viewName: Content Calendar
501
- pos: { x: 0, y: 19, w: 12, h: 8 }
502
- - id: post-published
503
- kind: widget
504
- widgetType: entity-count
505
- pos: { x: 3, y: 2, w: 3, h: 3 }
506
- config: { profileSlug: post, label: Published, icon: Globe, filter: { status: published }, color: "#10B981" }
507
- - id: post-drafts
508
- kind: widget
509
- widgetType: entity-count
510
- pos: { x: 6, y: 2, w: 3, h: 3 }
511
- config: { profileSlug: post, label: Drafts, icon: FileEdit, filter: { status: draft }, color: "#6B7280" }
512
- - id: post-scheduled
513
- kind: widget
514
- widgetType: entity-count
515
- pos: { x: 9, y: 2, w: 3, h: 3 }
516
- config: { profileSlug: post, label: Scheduled, icon: Clock, filter: { status: scheduled }, color: "#F59E0B" }
517
- - id: post-spotlight
518
- kind: widget
519
- widgetType: entity-spotlight
520
- pos: { x: 0, y: 5, w: 5, h: 4 }
521
- config: { profileSlug: post, seed: daily, layout: detail, subtitleField: platform, color: "#EC4899" }
522
- - id: post-gallery
523
- kind: widget
524
- widgetType: entity-gallery
525
- pos: { x: 5, y: 5, w: 7, h: 4 }
526
- config: { profileSlug: post, coverField: thumbnail-url, title: Recent Posts, limit: 6 }
527
- breakpoints:
528
- lg: { cols: 12, rowHeight: 60, gap: 12 }
529
- md: { cols: 10, rowHeight: 60, gap: 12 }
530
- sm: { cols: 6, rowHeight: 60, gap: 12 }
531
- xs: { cols: 4, rowHeight: 60, gap: 12 }
532
- xxs: { cols: 2, rowHeight: 60, gap: 12 }
533
- - name: Campaign
534
- type: bento
535
- scopeProfileSlug: campaign
536
- config:
537
- layout: bento
538
- blocks:
539
- - id: campaign-header
540
- kind: widget
541
- widgetType: section-header
542
- pos: { x: 0, y: 0, w: 12, h: 2 }
543
- config: { title: Campaigns, icon: Target, color: "#F59E0B", profileSlug: campaign }
544
- - id: campaign-count
545
- kind: widget
546
- widgetType: stat-card
547
- pos: { x: 0, y: 2, w: 3, h: 3 }
548
- config: { label: Total Campaigns, aggregation: count, profileSlug: campaign, color: "#F59E0B" }
549
- - id: campaign-board
550
- kind: view
551
- viewName: Campaigns
552
- pos: { x: 0, y: 9, w: 12, h: 8 }
553
- - id: campaign-timeline
554
- kind: view
555
- viewName: Campaign Timeline
556
- pos: { x: 0, y: 19, w: 12, h: 8 }
557
- - id: campaign-active
558
- kind: widget
559
- widgetType: entity-count
560
- pos: { x: 3, y: 2, w: 3, h: 3 }
561
- config: { profileSlug: campaign, label: Active, icon: Play, filter: { status: active }, color: "#10B981" }
562
- - id: campaign-posts
563
- kind: widget
564
- widgetType: entity-count
565
- pos: { x: 6, y: 2, w: 3, h: 3 }
566
- config: { profileSlug: post, label: Campaign Posts, icon: FileText, color: "#EC4899" }
567
- - id: campaign-spotlight
568
- kind: widget
569
- widgetType: entity-spotlight
570
- pos: { x: 0, y: 5, w: 5, h: 4 }
571
- config: { profileSlug: campaign, seed: daily, layout: detail, subtitleField: status, color: "#8B5CF6" }
572
- - id: campaign-post-list
573
- kind: widget
574
- widgetType: entity-list
575
- pos: { x: 5, y: 5, w: 7, h: 4 }
576
- config: { profileSlug: post, title: Campaign Posts, limit: 6, variant: compact }
577
- breakpoints:
578
- lg: { cols: 12, rowHeight: 60, gap: 12 }
579
- md: { cols: 10, rowHeight: 60, gap: 12 }
580
- sm: { cols: 6, rowHeight: 60, gap: 12 }
581
- xs: { cols: 4, rowHeight: 60, gap: 12 }
582
- xxs: { cols: 2, rowHeight: 60, gap: 12 }
583
- - name: Newsletter
584
- type: bento
585
- scopeProfileSlug: newsletter
586
- config:
587
- layout: bento
588
- blocks:
589
- - id: nl-header
590
- kind: widget
591
- widgetType: section-header
592
- pos: { x: 0, y: 0, w: 12, h: 2 }
593
- config: { title: Newsletters, icon: Mail, color: "#10B981", profileSlug: newsletter }
594
- - id: nl-count
595
- kind: widget
596
- widgetType: stat-card
597
- pos: { x: 0, y: 2, w: 3, h: 3 }
598
- config: { label: Total Issues, aggregation: count, profileSlug: newsletter, color: "#10B981" }
599
- - id: nl-pipeline
600
- kind: view
601
- viewName: Newsletter Pipeline
602
- pos: { x: 0, y: 5, w: 12, h: 10 }
603
- - id: nl-table
604
- kind: view
605
- viewName: Newsletter Issues
606
- pos: { x: 0, y: 15, w: 12, h: 8 }
607
- breakpoints:
608
- lg: { cols: 12, rowHeight: 60, gap: 12 }
609
- md: { cols: 10, rowHeight: 60, gap: 12 }
610
- sm: { cols: 6, rowHeight: 60, gap: 12 }
611
- xs: { cols: 4, rowHeight: 60, gap: 12 }
612
- xxs: { cols: 2, rowHeight: 60, gap: 12 }
613
- - name: Pillar
614
- type: bento
615
- scopeProfileSlug: pillar
616
- config:
617
- layout: bento
618
- blocks:
619
- - id: pillar-header
620
- kind: widget
621
- widgetType: section-header
622
- pos: { x: 0, y: 0, w: 12, h: 2 }
623
- config: { title: Content Pillars, icon: Layers, color: "#3B82F6", profileSlug: pillar }
624
- - id: pillar-count
625
- kind: widget
626
- widgetType: stat-card
627
- pos: { x: 0, y: 2, w: 3, h: 3 }
628
- config: { label: Pillars, aggregation: count, profileSlug: pillar, color: "#3B82F6", icon: Layers }
629
- - id: pillar-posts-count
630
- kind: widget
631
- widgetType: entity-count
632
- pos: { x: 3, y: 2, w: 3, h: 3 }
633
- config: { profileSlug: post, label: Posts in Pillar, icon: FileText, color: "#EC4899" }
634
- - id: pillar-campaigns
635
- kind: widget
636
- widgetType: entity-count
637
- pos: { x: 6, y: 2, w: 3, h: 3 }
638
- config: { profileSlug: campaign, label: Campaigns, icon: Target, color: "#8B5CF6" }
639
- - id: pillar-spotlight
640
- kind: widget
641
- widgetType: entity-spotlight
642
- pos: { x: 0, y: 5, w: 6, h: 4 }
643
- config: { profileSlug: pillar, seed: daily, layout: detail, color: "#3B82F6" }
644
- - id: pillar-post-list
645
- kind: widget
646
- widgetType: entity-list
647
- pos: { x: 6, y: 5, w: 6, h: 4 }
648
- config: { profileSlug: post, title: Posts in this Pillar, limit: 6, variant: compact }
649
- - id: pillar-table
650
- kind: view
651
- viewName: Pillars
652
- pos: { x: 0, y: 9, w: 12, h: 8 }
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
653
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
654
462
  entityLinks:
655
- - sourceProfileSlug: post
656
- targetProfileSlug: pillar
657
- type: under
658
463
  - sourceProfileSlug: post
659
464
  targetProfileSlug: campaign
660
- type: part_of
465
+ type: part_of_campaign
466
+ label: Campaign
661
467
  - sourceProfileSlug: post
662
- targetProfileSlug: sponsor
663
- type: sponsored_by
664
- - sourceProfileSlug: campaign
665
468
  targetProfileSlug: pillar
666
- type: under
667
- - sourceProfileSlug: newsletter
668
- targetProfileSlug: campaign
669
- type: part_of
670
- - sourceProfileSlug: newsletter
671
- targetProfileSlug: pillar
672
- type: under
673
- - sourceProfileSlug: newsletter
469
+ type: belongs_to_pillar
470
+ label: Pillar
471
+ - sourceProfileSlug: post
674
472
  targetProfileSlug: sponsor
675
473
  type: sponsored_by
676
- - sourceProfileSlug: platform
677
- targetProfileSlug: post
678
- type: publishes
679
-
680
- displayTemplates:
681
- - name: Post Card
682
- description: "Content unit with status, format, platform, and performance metrics"
683
- entityType: post
684
- targetType: entity
685
- isDefault: true
686
- config:
687
- layout:
688
- structure:
689
- header:
690
- enabled: true
691
- slots: [post-status, post-format, post-platform]
692
- layout: horizontal
693
- align: left
694
- sidebar:
695
- enabled: true
696
- slots: [post-pillar, post-campaign, post-sponsor, publish-date]
697
- position: right
698
- width: 260px
699
- layout: vertical
700
- content:
701
- enabled: true
702
- slots: [post-hook, post-cta, engagement-score, post-reach]
703
- layout: vertical
704
- fieldMapping:
705
- post-status:
706
- slot: post-status
707
- renderer: { type: badge, size: md }
708
- showLabel: false
709
- order: 1
710
- post-format:
711
- slot: post-format
712
- renderer: { type: badge, variant: muted, size: sm }
713
- showLabel: false
714
- order: 2
715
- post-platform:
716
- slot: post-platform
717
- renderer: { type: badge, variant: muted, size: sm }
718
- showLabel: false
719
- order: 3
720
- engagement-score:
721
- slot: engagement-score
722
- renderer: { type: number, size: md }
723
- label: Engagement
724
- showLabel: true
725
- order: 4
726
- post-reach:
727
- slot: post-reach
728
- renderer: { type: number, size: md }
729
- label: Reach
730
- showLabel: true
731
- order: 5
732
- publish-date:
733
- slot: publish-date
734
- renderer: { type: date, format: relative }
735
- order: 6
736
- colors: { accent: "#8B5CF6" }
737
- styling: { borderRadius: 10px }
738
- - name: Sponsor Deal
739
- description: "Brand deal with status, financials, and deliverable tracking"
740
- entityType: sponsor
741
- targetType: entity
742
- isDefault: true
743
- config:
744
- layout:
745
- structure:
746
- header:
747
- enabled: true
748
- slots: [deal-status, deal-value]
749
- layout: horizontal
750
- sidebar:
751
- enabled: true
752
- slots: [payment-status, deliverable-deadline, exclusivity-end, contact-name, contact-email]
753
- position: right
754
- width: 280px
755
- layout: vertical
756
- content:
757
- enabled: true
758
- slots: [deliverables, talking-points, performance-notes]
759
- layout: vertical
760
- fieldMapping:
761
- deal-status:
762
- slot: deal-status
763
- renderer: { type: badge, size: md }
764
- showLabel: false
765
- order: 1
766
- deal-value:
767
- slot: deal-value
768
- renderer: { type: number, format: currency, size: lg }
769
- label: Deal Value
770
- order: 2
771
- payment-status:
772
- slot: payment-status
773
- renderer: { type: badge, variant: muted, size: sm }
774
- showLabel: true
775
- order: 3
776
- deliverable-deadline:
777
- slot: deliverable-deadline
778
- renderer: { type: date, format: relative }
779
- label: Deadline
780
- showLabel: true
781
- order: 4
782
- colors: { accent: "#F59E0B" }
783
- styling: { borderRadius: 10px }
784
- - name: Campaign Brief
785
- description: "Campaign with timeline, KPI progress, and linked content"
786
- entityType: campaign
787
- targetType: entity
788
- isDefault: true
789
- config:
790
- layout:
791
- structure:
792
- header:
793
- enabled: true
794
- slots: [campaign-status, campaign-type]
795
- layout: horizontal
796
- sidebar:
797
- enabled: true
798
- slots: [campaign-pillar, campaign-start, campaign-end, kpi-target, kpi-result, post-count]
799
- position: right
800
- width: 260px
801
- layout: vertical
802
- content:
803
- enabled: true
804
- slots: [campaign-goal, campaign-kpi, campaign-budget]
805
- layout: vertical
806
- fieldMapping:
807
- campaign-status:
808
- slot: campaign-status
809
- renderer: { type: badge, size: md }
810
- showLabel: false
811
- order: 1
812
- campaign-type:
813
- slot: campaign-type
814
- renderer: { type: badge, variant: muted, size: sm }
815
- showLabel: false
816
- order: 2
817
- kpi-target:
818
- slot: kpi-target
819
- renderer: { type: number, size: md }
820
- label: KPI Target
821
- showLabel: true
822
- order: 3
823
- kpi-result:
824
- slot: kpi-result
825
- renderer: { type: number, size: md }
826
- label: KPI Result
827
- showLabel: true
828
- order: 4
829
- campaign-start:
830
- slot: campaign-start
831
- renderer: { type: date, format: relative }
832
- label: Start
833
- showLabel: true
834
- order: 5
835
- campaign-end:
836
- slot: campaign-end
837
- renderer: { type: date, format: relative }
838
- label: End
839
- showLabel: true
840
- order: 6
841
- colors: { accent: "#EC4899" }
842
- styling: { borderRadius: 10px }
843
- - name: Newsletter Issue
844
- description: "Newsletter issue with open/click rates and sponsor linkage"
845
- entityType: newsletter
846
- targetType: entity
847
- isDefault: true
848
- config:
849
- layout:
850
- structure:
851
- header:
852
- enabled: true
853
- slots: [newsletter-status, issue-number]
854
- layout: horizontal
855
- sidebar:
856
- enabled: true
857
- slots: [newsletter-sponsor, newsletter-campaign, newsletter-pillar, send-date, subscriber-count]
858
- position: right
859
- width: 260px
860
- layout: vertical
861
- content:
862
- enabled: true
863
- slots: [subject-line, preview-text, open-rate, click-rate]
864
- layout: vertical
865
- fieldMapping:
866
- newsletter-status:
867
- slot: newsletter-status
868
- renderer: { type: badge, size: md }
869
- showLabel: false
870
- order: 1
871
- open-rate:
872
- slot: open-rate
873
- renderer: { type: number, format: percent, size: md }
874
- label: Open Rate
875
- showLabel: true
876
- order: 2
877
- click-rate:
878
- slot: click-rate
879
- renderer: { type: number, format: percent, size: md }
880
- label: Click Rate
881
- showLabel: true
882
- order: 3
883
- send-date:
884
- slot: send-date
885
- renderer: { type: date, format: relative }
886
- label: Sent
887
- showLabel: true
888
- order: 4
889
- colors: { accent: "#14B8A6" }
890
- styling: { borderRadius: 10px }
891
-
892
- # Home dashboard bento (maps from source bentoLayout — all blocks are widgets)
893
- bento:
894
- blocks:
895
- - kind: widget
896
- widgetType: welcome-header
897
- pos: { x: 0, y: 0, w: 12, h: 2 }
898
- - kind: widget
899
- widgetType: entity-count
900
- pos: { x: 0, y: 2, w: 2, h: 2 }
901
- config: { profileSlug: post, label: Posts }
902
- - kind: widget
903
- widgetType: entity-count
904
- pos: { x: 2, y: 2, w: 2, h: 2 }
905
- config: { profileSlug: campaign, label: Campaigns }
906
- - kind: widget
907
- widgetType: entity-count
908
- pos: { x: 4, y: 2, w: 2, h: 2 }
909
- config: { profileSlug: pillar, label: Pillars }
910
- - kind: widget
911
- widgetType: entity-count
912
- pos: { x: 6, y: 2, w: 2, h: 2 }
913
- config: { profileSlug: newsletter, label: Newsletters }
914
- - kind: widget
915
- widgetType: entity-count
916
- pos: { x: 8, y: 2, w: 2, h: 2 }
917
- config: { profileSlug: platform, label: Platforms }
918
- - kind: widget
919
- widgetType: entity-count
920
- pos: { x: 10, y: 2, w: 2, h: 2 }
921
- config: { profileSlug: sponsor, label: Sponsors }
922
- - kind: widget
923
- widgetType: entity-list
924
- pos: { x: 0, y: 4, w: 5, h: 4 }
925
- config: { profileSlug: post, title: Post Pipeline, limit: 8, variant: compact }
926
- - kind: widget
927
- widgetType: entity-list
928
- pos: { x: 5, y: 4, w: 4, h: 4 }
929
- config: { profileSlug: campaign, title: Active Campaigns, limit: 5, variant: detailed }
930
- - kind: widget
931
- widgetType: entity-list
932
- pos: { x: 9, y: 4, w: 3, h: 4 }
933
- config: { profileSlug: sponsor, title: Sponsor Tracker, limit: 5, variant: compact }
934
- - kind: widget
935
- widgetType: calendar
936
- pos: { x: 0, y: 8, w: 5, h: 4 }
937
- - kind: widget
938
- widgetType: entity-list
939
- pos: { x: 5, y: 8, w: 4, h: 4 }
940
- config: { profileSlug: newsletter, title: Newsletter Issues, limit: 5, variant: compact }
941
- - kind: widget
942
- widgetType: feed
943
- pos: { x: 9, y: 8, w: 3, h: 4 }
944
-
945
- # Per-profile entity detail bento layouts
946
- profileEntityBentoTemplates:
947
- post:
948
- blocks:
949
- - id: post-eb-header
950
- kind: widget
951
- widgetType: entity-header
952
- pos: { x: 0, y: 0, w: 12, h: 2 }
953
- - id: post-eb-props
954
- kind: widget
955
- widgetType: entity-properties
956
- pos: { x: 0, y: 2, w: 4, h: 8 }
957
- - id: post-eb-content
958
- kind: widget
959
- widgetType: entity-content
960
- pos: { x: 4, y: 2, w: 8, h: 4 }
961
- - id: post-eb-similar
962
- kind: widget
963
- widgetType: entity-list
964
- pos: { x: 4, y: 6, w: 8, h: 4 }
965
- config: { profileSlug: post, title: Similar Posts, limit: 6, variant: compact }
966
- campaign:
967
- blocks:
968
- - id: camp-eb-header
969
- kind: widget
970
- widgetType: entity-header
971
- pos: { x: 0, y: 0, w: 12, h: 2 }
972
- - id: camp-eb-props
973
- kind: widget
974
- widgetType: entity-properties
975
- pos: { x: 0, y: 2, w: 4, h: 8 }
976
- - id: camp-eb-posts
977
- kind: widget
978
- widgetType: entity-list
979
- pos: { x: 4, y: 2, w: 8, h: 4 }
980
- config: { profileSlug: post, title: Campaign Posts, limit: 8, variant: compact }
981
- - id: camp-eb-content
982
- kind: widget
983
- widgetType: entity-content
984
- pos: { x: 4, y: 6, w: 8, h: 4 }
985
- pillar:
986
- blocks:
987
- - id: pillar-eb-header
988
- kind: widget
989
- widgetType: entity-header
990
- pos: { x: 0, y: 0, w: 12, h: 2 }
991
- - id: pillar-eb-props
992
- kind: widget
993
- widgetType: entity-properties
994
- pos: { x: 0, y: 2, w: 4, h: 8 }
995
- - id: pillar-eb-posts
996
- kind: widget
997
- widgetType: entity-list
998
- pos: { x: 4, y: 2, w: 8, h: 8 }
999
- config: { profileSlug: post, title: Posts in Pillar, limit: 10, variant: compact }
1000
-
1001
- # Capability templates to install on provisioning
1002
- integrations:
1003
- - templateKey: nango-google
1004
-
1005
- # Session-template playbooks (none in source)
1006
- playbooks: []
474
+ label: Sponsor
1007
475
 
1008
- # Per-workspace adaptive onboarding context — the "seasoned content strategist" framing
1009
476
  onboarding:
1010
- goal: "Capture the user's brand voice, audience, content pillars, and publishing rhythm across channels — so the workspace reflects how they actually create and distribute content, not a generic calendar."
1011
- framing: "Act as a seasoned content strategist. Tease out their voice and positioning, who they're talking to, the recurring themes they own, the channels they publish on, and how often. Translate that into pillars, platforms, and a seeded content calendar."
477
+ goal: >
478
+ Stand up the editorial engine — the pillars you're known for, the platforms
479
+ you publish on, the cadence you can actually sustain, and a first post placed
480
+ in the system — so the workspace mirrors how you turn thinking into published
481
+ content, not a blank calendar.
482
+ framing: >
483
+ THE EDITOR-IN-CHIEF: Act as a seasoned content strategist who has grown owned
484
+ audiences from zero. Anchor everything on PILLARS — the two-to-four recurring
485
+ themes an audience comes to you for — because pillars are what make a content
486
+ engine compound instead of drift. Pull voice and identity from Brand and the
487
+ target audience from Foundation so every piece lands on-strategy. Push for a
488
+ cadence they can hold for a year, not a heroic month. Be concrete: real
489
+ platforms, real formats, a real first post — a fuzzy "I'll post more" helps
490
+ no one.
1012
491
  expertise:
1013
492
  starters:
1014
493
  - >
1015
- Most creators over-index on one format and burn out. Propose 3–4 pillars
1016
- that mix TEACH (how it works), PROVE (customer/results), and PROVOKE
1017
- (point of view) — then adapt the mix to their niche. Don't ask for pillars
1018
- from a blank slate; offer a starting set and let them react.
494
+ Pillars before posts. A creator known for 2–4 clear themes compounds; one
495
+ posting about everything stays noise. Propose naming the pillars FIRST —
496
+ each with the audience it speaks to and the transformation it delivers —
497
+ then hang every post off one of them.
1019
498
  - >
1020
- Better to own ONE channel consistently than be thin on four. If they list
1021
- many platforms, propose a primary channel + a cadence they can actually
1022
- sustain, and treat the rest as repurposing targets, not separate efforts.
499
+ Cadence you can sustain beats cadence that impresses. A weekly post held
500
+ for a year outperforms daily-for-three-weeks-then-silence. Propose a
501
+ realistic per-platform frequency and let the calendar enforce it, rather
502
+ than an ambitious number that collapses.
1023
503
  - >
1024
- Every pillar should ladder to a business goal — awareness, trust, or
1025
- conversion. If a pillar is just "stuff we post", propose the job it should
1026
- do and tie it to a specific audience segment.
504
+ One idea, many formats — design for repurposing from the start. A single
505
+ strong thought becomes a thread, a short video, a newsletter section, and
506
+ a carousel. Propose the repurpose chain up front so one capture yields a
507
+ week of output, tracked via `repurposed-from`.
1027
508
  blindSpots:
1028
509
  - >
1029
- Distribution is the silent killer — great content nobody sees. If they
1030
- haven't said HOW each piece gets discovered (SEO, a platform's algorithm,
1031
- an email list, founder-led amplification), raise it before moving on.
510
+ No pillars = brand drift. If they list a dozen post ideas but can't name
511
+ the 2–4 themes underneath, that's the gap — surface it before filling the
512
+ calendar, because unpillared posting never builds an identity.
1032
513
  - >
1033
- No repurposing plan = 5× the work for 1× the reach. If they treat each
1034
- post as one-and-done, propose a chain: one deep piece → clips → posts →
1035
- newsletter.
514
+ The owned-audience channel is the one most creators under-invest in. If
515
+ they're all-in on algorithmic platforms with no newsletter or list, name
516
+ it: the newsletter is the audience they actually own when a platform
517
+ changes the rules.
1036
518
  - >
1037
- Pillars with no audience attached reach no one. Tie each pillar to a
1038
- specific segment — "content for everyone" is content for nobody.
519
+ Chasing every platform at once guarantees all of them stay shallow. If
520
+ they name five channels, push back: which ONE or two get real cadence and
521
+ attention now, and which are experiments?
1039
522
  bar: >
1040
- The pillars + primary platform + cadence read like a system a small team
1041
- could actually run for 3 months without burning out — where every pillar has
1042
- a clear audience, a clear job, and a way to get discovered.
523
+ The workspace reads like a running publication: a handful of named pillars
524
+ each tied to an audience, the platforms actually committed to with a
525
+ sustainable cadence, an owned newsletter channel, and a first post placed in
526
+ a pillar — so a strategist could see at a glance what the brand is about,
527
+ where it publishes, and what ships next.
1043
528
  collect:
1044
529
  - profileSlug: pillar
1045
- what: "Their recurring content themes / brand voice — the lenses their audience knows them for, and who each speaks to"
530
+ 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.
1046
531
  cardinality: few
1047
- keyFields: [title, pillar-audience, pillar-goal, posting-frequency]
532
+ keyFields: [pillar-status, pillar-audience, pillar-formats]
1048
533
  - profileSlug: platform
1049
- what: "The channels they publish on and the rhythm of each"
534
+ what: The channels you actually publish on — the social accounts, newsletter, or podcast — with their status and the cadence you can hold.
1050
535
  cardinality: several
1051
- keyFields: [title, platform-type, platform-posting-freq, follower-count]
536
+ keyFields: [platform-type, platform-status, platform-posting-freq]
1052
537
  - profileSlug: post
1053
- what: "A few real or upcoming pieces to seed the content calendar"
538
+ what: A first post or two already in motion, each placed on a pillar and a platform at its true status (Idea through Published).
1054
539
  cardinality: few
1055
- keyFields: [title, post-platform, post-pillar, publish-date]
1056
- - profileSlug: campaign
1057
- what: "Any active initiative or launch the content ladders up to"
1058
- cardinality: one
1059
- keyFields: [title, campaign-type, campaign-kpi]
540
+ keyFields: [post-status, post-format, post-platform]
1060
541
  openingQuestions:
1061
- - "How would you describe your voice, and who are you really creating for?"
1062
- - "What are the 3-4 themes you keep coming back to — the things your audience knows you for?"
1063
- - "Which channels do you publish on, and how often on each?"
1064
- doneWhen: "Their pillars and primary platforms exist with a realistic cadence, and a handful of posts sit on the content calendar."
542
+ - "What are the 2–4 themes your audience comes to you for — the pillars everything you make ties back to?"
543
+ - "Which platforms do you actually publish on, and what cadence can you genuinely sustain on each?"
544
+ - "Do you run (or want) a newsletter — the audience you own outright — and what's the first post you'd place in the system?"
545
+ doneWhen: at least one pillar + one platform with a cadence + a first post placed on a pillar.