@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/dist/index.d.ts +38 -1
- package/dist/index.js +635 -27
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/builder.yaml +1 -1
- package/src/business-developer.template.test.ts +2 -0
- package/src/business-developer.yaml +84 -0
- package/src/communication.yaml +28 -1
- package/src/content-os.yaml +487 -31
- package/src/crm.yaml +46 -0
- package/src/foundation.yaml +93 -0
- package/src/internal-runbook.yaml +1 -1
- package/src/project-management.yaml +1 -1
- package/src/templates.ts +635 -27
- package/src/types.ts +38 -2
package/src/content-os.yaml
CHANGED
|
@@ -27,6 +27,20 @@ meta:
|
|
|
27
27
|
# guarantees its SSOT body is the base; this template re-declares `sponsor`
|
|
28
28
|
# below with `scope: shared` (identical to grants.yaml's shared-role reuse) so
|
|
29
29
|
# provisioning dedups to the ONE pod-wide row and grants THIS workspace access.
|
|
30
|
+
# • `offer` — foundation's pod-wide `scope: shared` KIND (the thing being sold).
|
|
31
|
+
# Same mechanics as `sponsor`, and the re-declaration is equally load-bearing:
|
|
32
|
+
# both provisioning doors iterate ONLY `definition.profiles`, and that
|
|
33
|
+
# iteration is what writes the `profile_workspace_access` grant. Omit it and
|
|
34
|
+
# `offer` is invisible here — every `post → offer` link lands in
|
|
35
|
+
# `report.entityLinks.unresolved` instead of resolving.
|
|
36
|
+
# • `audience` / `pain-point` / `positioning` — foundation's pod-wide strategy
|
|
37
|
+
# kinds, targeted by six properties here. They ARE re-declared below, for the
|
|
38
|
+
# same grant reason as `offer`: a `targetProfileSlug` reference earns nothing.
|
|
39
|
+
# `resolve-property-target-profiles.ts:117` resolves the target through
|
|
40
|
+
# `getBySlugForWorkspace`, which for a SHARED profile requires a
|
|
41
|
+
# `profile_workspace_access` row — written only by declaring the slug. Without
|
|
42
|
+
# the declaration all six resolve to a NULL `target_profile_id`, i.e. an
|
|
43
|
+
# unconstrained picker.
|
|
30
44
|
# Both are `require` (not `compose`): each dependency is a real standalone
|
|
31
45
|
# workspace, and this template creates its OWN workspace — it does not overlay onto
|
|
32
46
|
# theirs.
|
|
@@ -132,6 +146,44 @@ profiles:
|
|
|
132
146
|
valueType: entity_id
|
|
133
147
|
targetProfileSlug: sponsor
|
|
134
148
|
inputType: entity_select
|
|
149
|
+
# ── The journey layer — BOTH axes optional, and independent ────────────
|
|
150
|
+
# `post-stage` is the AUDIENCE axis (unordered: where is the reader), and
|
|
151
|
+
# `post-series` + `post-position` are the CURRICULUM axis (ordered:
|
|
152
|
+
# chapter by chapter). A post carries at most one of each; with neither,
|
|
153
|
+
# this workspace behaves exactly as it did before the journey existed.
|
|
154
|
+
# Position lives on the POST, not on the series, so reordering a
|
|
155
|
+
# curriculum is one write per moved piece instead of rewriting a list.
|
|
156
|
+
- slug: post-stage
|
|
157
|
+
label: Journey Stage
|
|
158
|
+
valueType: entity_id
|
|
159
|
+
targetProfileSlug: journey-stage
|
|
160
|
+
inputType: entity_select
|
|
161
|
+
- slug: post-series
|
|
162
|
+
label: Series
|
|
163
|
+
valueType: entity_id
|
|
164
|
+
targetProfileSlug: series
|
|
165
|
+
inputType: entity_select
|
|
166
|
+
# ⚠️ Position is AGENT-READ, not yet user-rendered in sequence. `Plan Next
|
|
167
|
+
# Content` reasons over it (finds "a series that stops mid-chapter"), so it
|
|
168
|
+
# is real data, not dead — but NO view surfaces a single series' posts in
|
|
169
|
+
# order today (the series-detail relations slot is an unordered ObjectGraph,
|
|
170
|
+
# and corpus-wide views sort on ONE key so a flat position-sort interleaves
|
|
171
|
+
# every series). FAST-FOLLOW: an ordered "Posts in this series" section on
|
|
172
|
+
# the series detail page. Until then the order lives in the number, read by
|
|
173
|
+
# the agent, not shown back to the user as a sequence.
|
|
174
|
+
- slug: post-position
|
|
175
|
+
label: Position in Series
|
|
176
|
+
valueType: number
|
|
177
|
+
inputType: number
|
|
178
|
+
placeholder: "1 = first chapter"
|
|
179
|
+
# The commercial axis. Present ONLY when the pod actually sells something
|
|
180
|
+
# — `offer` is optional by construction (foundation.yaml), so an empty
|
|
181
|
+
# value here means "no commerce", never "unfilled".
|
|
182
|
+
- slug: post-offer
|
|
183
|
+
label: Offer
|
|
184
|
+
valueType: entity_id
|
|
185
|
+
targetProfileSlug: offer
|
|
186
|
+
inputType: entity_select
|
|
135
187
|
- slug: publish-date
|
|
136
188
|
label: Publish Date
|
|
137
189
|
valueType: date
|
|
@@ -157,11 +209,22 @@ profiles:
|
|
|
157
209
|
label: "Reach / Views"
|
|
158
210
|
valueType: number
|
|
159
211
|
inputType: number
|
|
212
|
+
# `repurposed-from` is free TEXT and stays that way. A `valueType` change
|
|
213
|
+
# is reported as a conflict and NEVER applied to a live workspace
|
|
214
|
+
# (reconcile-workspace-from-definition.ts), so retyping it would leave YAML
|
|
215
|
+
# and every provisioned pod permanently divergent. The real edge is added
|
|
216
|
+
# ALONGSIDE as `derived-from`; the text field is relabelled to say so and
|
|
217
|
+
# dropped from the views. A `remapPropertyValues` conversion can backfill
|
|
218
|
+
# the old values later — that is an operator-run script, not automatic.
|
|
160
219
|
- slug: repurposed-from
|
|
161
|
-
label:
|
|
220
|
+
label: Repurpose Notes
|
|
162
221
|
valueType: string
|
|
163
222
|
inputType: text
|
|
164
|
-
|
|
223
|
+
- slug: derived-from
|
|
224
|
+
label: Repurposed From
|
|
225
|
+
valueType: entity_id
|
|
226
|
+
targetProfileSlug: post
|
|
227
|
+
inputType: entity_select
|
|
165
228
|
- slug: post-url
|
|
166
229
|
label: Published URL
|
|
167
230
|
valueType: string
|
|
@@ -181,11 +244,25 @@ profiles:
|
|
|
181
244
|
valueType: string
|
|
182
245
|
inputType: select
|
|
183
246
|
enumValues: ["Active", "Developing", "Paused", "Retired"]
|
|
247
|
+
# `pillar-audience` was free text and MUST stay free text — see the note on
|
|
248
|
+
# `repurposed-from` above: a retype is a conflict that never reaches a live
|
|
249
|
+
# workspace. The real graph edges are the two `-ref` slugs added below,
|
|
250
|
+
# which point at foundation's pod-wide `audience` / `pain-point` rows. Both
|
|
251
|
+
# legacy fields keep their data and are dropped from the view columns.
|
|
184
252
|
- slug: pillar-audience
|
|
185
|
-
label: Audience
|
|
253
|
+
label: Audience Notes
|
|
186
254
|
valueType: string
|
|
187
255
|
inputType: text
|
|
188
|
-
|
|
256
|
+
- slug: pillar-audience-ref
|
|
257
|
+
label: Audience
|
|
258
|
+
valueType: entity_id
|
|
259
|
+
targetProfileSlug: audience
|
|
260
|
+
inputType: entity_select
|
|
261
|
+
- slug: pillar-pain-ref
|
|
262
|
+
label: Pain Addressed
|
|
263
|
+
valueType: entity_id
|
|
264
|
+
targetProfileSlug: pain-point
|
|
265
|
+
inputType: entity_select
|
|
189
266
|
- slug: pillar-goal
|
|
190
267
|
label: Audience Goal
|
|
191
268
|
valueType: string
|
|
@@ -236,6 +313,99 @@ profiles:
|
|
|
236
313
|
label: Tags
|
|
237
314
|
valueType: array
|
|
238
315
|
|
|
316
|
+
# ── Journey Stage — the AUDIENCE axis ─────────────────────────────────────
|
|
317
|
+
# A stage is an ENTITY, not an enum, because each one carries reasoning the
|
|
318
|
+
# agent needs to write toward it: the belief to install, the objection to
|
|
319
|
+
# dissolve, the next step to earn. An enum would carry a label and nothing
|
|
320
|
+
# else, and the whole point of the journey is that it explains itself.
|
|
321
|
+
#
|
|
322
|
+
# `scope: WORKSPACE` — content-os OWNS this vocabulary and no other template
|
|
323
|
+
# re-declares it. Making it `shared` would bind the ENTIRE corpus to this body
|
|
324
|
+
# forever via the shared-kind SSOT rule; that is a one-way door and this
|
|
325
|
+
# concept is young. `entityScope: pod` is the axis that actually matters here:
|
|
326
|
+
# the stage ROWS go pod-wide so marketing and CRM can point at "which stage
|
|
327
|
+
# was this reader in" without content-os having to hand out its vocabulary.
|
|
328
|
+
#
|
|
329
|
+
# Seeded by the onboarding interview as a small ladder the operator renames —
|
|
330
|
+
# 3 stages held honestly beats 7 invented ones.
|
|
331
|
+
- slug: journey-stage
|
|
332
|
+
displayName: Journey Stage
|
|
333
|
+
icon: footprints
|
|
334
|
+
color: "#F59E0B"
|
|
335
|
+
description: "A step on the reader's journey — where they are, what they now believe, and what moves them forward."
|
|
336
|
+
scope: WORKSPACE
|
|
337
|
+
entityScope: pod
|
|
338
|
+
properties:
|
|
339
|
+
- slug: stage-order
|
|
340
|
+
label: Order
|
|
341
|
+
valueType: number
|
|
342
|
+
inputType: number
|
|
343
|
+
placeholder: "1 = first contact"
|
|
344
|
+
- slug: stage-audience
|
|
345
|
+
label: Audience
|
|
346
|
+
valueType: entity_id
|
|
347
|
+
targetProfileSlug: audience
|
|
348
|
+
inputType: entity_select
|
|
349
|
+
- slug: belief-installed
|
|
350
|
+
label: Now Believes
|
|
351
|
+
valueType: string
|
|
352
|
+
inputType: textarea
|
|
353
|
+
placeholder: "What should the reader now believe that they didn't before?"
|
|
354
|
+
- slug: objection-removed
|
|
355
|
+
label: Doubt Answered
|
|
356
|
+
valueType: string
|
|
357
|
+
inputType: textarea
|
|
358
|
+
placeholder: "Which doubt does this stage dissolve?"
|
|
359
|
+
- slug: next-step
|
|
360
|
+
label: Next Step
|
|
361
|
+
valueType: string
|
|
362
|
+
inputType: text
|
|
363
|
+
placeholder: "The one action that moves them to the next stage"
|
|
364
|
+
# No `stage-offer` here — deliberate. The post already carries `post-offer`,
|
|
365
|
+
# and a stage-level offer would be a SECOND path to the same mapping with no
|
|
366
|
+
# rule for which wins when a stage says offer A and its posts say offer B.
|
|
367
|
+
# Which offer a stage serves is derivable through the posts that sit in it.
|
|
368
|
+
|
|
369
|
+
# ── Series — the CURRICULUM axis ──────────────────────────────────────────
|
|
370
|
+
# Ordered and prerequisite-bearing: "chapter by chapter" rather than "where is
|
|
371
|
+
# the reader". Workspace-scoped AND workspace-placed — a series is an editorial
|
|
372
|
+
# artifact with no life outside the publication, so unlike `journey-stage` its
|
|
373
|
+
# rows have no reason to go pod-wide. It holds no member list and no length:
|
|
374
|
+
# the members ARE whatever posts carry this series in `post-series`, ordered
|
|
375
|
+
# by `post-position`, so there is nothing here to keep in sync.
|
|
376
|
+
- slug: series
|
|
377
|
+
displayName: Series
|
|
378
|
+
icon: layers
|
|
379
|
+
color: "#0EA5E9"
|
|
380
|
+
description: "An ordered run of posts that teaches one thing end to end — the curriculum axis of the content engine."
|
|
381
|
+
scope: WORKSPACE
|
|
382
|
+
properties:
|
|
383
|
+
- slug: series-status
|
|
384
|
+
label: Status
|
|
385
|
+
valueType: string
|
|
386
|
+
inputType: select
|
|
387
|
+
enumValues: ["Planned", "Running", "Complete", "Abandoned"]
|
|
388
|
+
- slug: series-goal
|
|
389
|
+
label: Goal
|
|
390
|
+
valueType: string
|
|
391
|
+
inputType: textarea
|
|
392
|
+
placeholder: "What can the reader do at the end that they couldn't at the start?"
|
|
393
|
+
# A series belongs to ONE pillar — the relationship the onboarding copy,
|
|
394
|
+
# the collect entry and the skill all already assert. It also settles the
|
|
395
|
+
# only real overlap in the model: `pillar-goal` ("what should the audience
|
|
396
|
+
# think/feel/do") and `series-goal` ("what can they DO at the end") ask
|
|
397
|
+
# nearly the same question, and a series that points at its pillar reads
|
|
398
|
+
# as the pillar's curriculum rather than competing with it.
|
|
399
|
+
#
|
|
400
|
+
# No `series-audience`: the pillar already carries one. Three pointers to
|
|
401
|
+
# the same answer (`pillar-audience-ref`, `series-audience`,
|
|
402
|
+
# `stage-audience`) is three places to keep in sync and two to get wrong.
|
|
403
|
+
- slug: series-pillar
|
|
404
|
+
label: Pillar
|
|
405
|
+
valueType: entity_id
|
|
406
|
+
targetProfileSlug: pillar
|
|
407
|
+
inputType: entity_select
|
|
408
|
+
|
|
239
409
|
# `platform` is the ONE pod-wide publishing-account *vocabulary*. `scope: shared`
|
|
240
410
|
# makes it a single pod-wide profile row that a `require`-consumer (e.g.
|
|
241
411
|
# social.yaml) can adopt by RE-DECLARING this slug — the resolver writes the
|
|
@@ -340,7 +510,7 @@ profiles:
|
|
|
340
510
|
enumValues:
|
|
341
511
|
["Idea", "Drafting", "Editing", "Scheduled", "Sent", "Archived"]
|
|
342
512
|
- slug: newsletter-series
|
|
343
|
-
label: Series
|
|
513
|
+
label: Newsletter Series
|
|
344
514
|
valueType: string
|
|
345
515
|
inputType: text
|
|
346
516
|
placeholder: "Which newsletter series is this issue part of?"
|
|
@@ -487,6 +657,134 @@ profiles:
|
|
|
487
657
|
valueType: string
|
|
488
658
|
inputType: textarea
|
|
489
659
|
|
|
660
|
+
# ── Strategy kinds (shared pod-wide — SSOT is foundation.yaml) ────────────
|
|
661
|
+
# Re-declared for the same grant-gating rule as `sponsor`/`offer` (see the meta
|
|
662
|
+
# header): six `targetProfileSlug` properties here point at these three kinds,
|
|
663
|
+
# and a reference earns no grant — omit the declaration and all six resolve to
|
|
664
|
+
# a NULL `target_profile_id`. Bodies verbatim from foundation; SSOT stays there.
|
|
665
|
+
- slug: audience
|
|
666
|
+
displayName: Audience
|
|
667
|
+
icon: users
|
|
668
|
+
color: blue
|
|
669
|
+
description: "An ideal customer profile — who we are built for."
|
|
670
|
+
scope: shared
|
|
671
|
+
entityScope: pod
|
|
672
|
+
properties:
|
|
673
|
+
- slug: segment
|
|
674
|
+
label: Segment
|
|
675
|
+
valueType: string
|
|
676
|
+
inputType: text
|
|
677
|
+
- slug: persona
|
|
678
|
+
label: Persona
|
|
679
|
+
valueType: string
|
|
680
|
+
inputType: textarea
|
|
681
|
+
- slug: jobs-to-be-done
|
|
682
|
+
label: Jobs to be done
|
|
683
|
+
valueType: string
|
|
684
|
+
inputType: textarea
|
|
685
|
+
- slug: traits
|
|
686
|
+
label: Traits
|
|
687
|
+
valueType: array
|
|
688
|
+
inputType: text
|
|
689
|
+
|
|
690
|
+
- slug: pain-point
|
|
691
|
+
displayName: Pain Point
|
|
692
|
+
icon: flame
|
|
693
|
+
color: orange
|
|
694
|
+
description: "A specific pain a target audience feels today."
|
|
695
|
+
scope: shared
|
|
696
|
+
entityScope: pod
|
|
697
|
+
properties:
|
|
698
|
+
- slug: description
|
|
699
|
+
label: Description
|
|
700
|
+
valueType: string
|
|
701
|
+
inputType: textarea
|
|
702
|
+
- slug: severity
|
|
703
|
+
label: Severity
|
|
704
|
+
valueType: string
|
|
705
|
+
inputType: select
|
|
706
|
+
enumValues: [low, medium, high, critical]
|
|
707
|
+
constraints: { defaultValue: medium }
|
|
708
|
+
- slug: target-audience
|
|
709
|
+
label: Audience
|
|
710
|
+
valueType: entity_id
|
|
711
|
+
targetProfileSlug: audience
|
|
712
|
+
inputType: entity_select
|
|
713
|
+
- slug: evidence
|
|
714
|
+
label: Evidence
|
|
715
|
+
valueType: string
|
|
716
|
+
inputType: textarea
|
|
717
|
+
|
|
718
|
+
- slug: positioning
|
|
719
|
+
displayName: Positioning
|
|
720
|
+
icon: crosshair
|
|
721
|
+
color: cyan
|
|
722
|
+
description: "Market category, who we stand against, and why we win."
|
|
723
|
+
scope: shared
|
|
724
|
+
entityScope: pod
|
|
725
|
+
properties:
|
|
726
|
+
- slug: category
|
|
727
|
+
label: Category
|
|
728
|
+
valueType: string
|
|
729
|
+
inputType: text
|
|
730
|
+
- slug: against
|
|
731
|
+
label: Against
|
|
732
|
+
valueType: string
|
|
733
|
+
inputType: textarea
|
|
734
|
+
- slug: because
|
|
735
|
+
label: Because
|
|
736
|
+
valueType: string
|
|
737
|
+
inputType: textarea
|
|
738
|
+
|
|
739
|
+
# ── Offer (shared pod-wide kind — SSOT is foundation.yaml) ────────────────
|
|
740
|
+
# Grant-gated exactly like the three above; body verbatim from foundation,
|
|
741
|
+
# `entityScope: pod` restated so rows land pod-wide even in the
|
|
742
|
+
# (ordering-degenerate) case where this workspace creates the profile first.
|
|
743
|
+
- slug: offer
|
|
744
|
+
displayName: Offer
|
|
745
|
+
icon: package
|
|
746
|
+
color: fuchsia
|
|
747
|
+
description: "Something the company sells — a product, service, or program. Optional: a pod that sells nothing simply has none."
|
|
748
|
+
scope: shared
|
|
749
|
+
entityScope: pod
|
|
750
|
+
properties:
|
|
751
|
+
- slug: offer-status
|
|
752
|
+
label: Status
|
|
753
|
+
valueType: string
|
|
754
|
+
inputType: select
|
|
755
|
+
enumValues:
|
|
756
|
+
- draft
|
|
757
|
+
- active
|
|
758
|
+
- paused
|
|
759
|
+
- retired
|
|
760
|
+
constraints: { defaultValue: draft }
|
|
761
|
+
- slug: offer-summary
|
|
762
|
+
label: Summary
|
|
763
|
+
valueType: string
|
|
764
|
+
inputType: textarea
|
|
765
|
+
placeholder: "What it is and what changes for the buyer"
|
|
766
|
+
- slug: offer-price-model
|
|
767
|
+
label: Price Model
|
|
768
|
+
valueType: string
|
|
769
|
+
inputType: select
|
|
770
|
+
enumValues:
|
|
771
|
+
- one-off
|
|
772
|
+
- subscription
|
|
773
|
+
- retainer
|
|
774
|
+
- usage-based
|
|
775
|
+
- free
|
|
776
|
+
- other
|
|
777
|
+
- slug: offer-audience
|
|
778
|
+
label: Audience
|
|
779
|
+
valueType: entity_id
|
|
780
|
+
targetProfileSlug: audience
|
|
781
|
+
inputType: entity_select
|
|
782
|
+
- slug: offer-positioning
|
|
783
|
+
label: Positioning
|
|
784
|
+
valueType: entity_id
|
|
785
|
+
targetProfileSlug: positioning
|
|
786
|
+
inputType: entity_select
|
|
787
|
+
|
|
490
788
|
# CURATED views — the editorial spine
|
|
491
789
|
views:
|
|
492
790
|
- name: Posts
|
|
@@ -514,6 +812,21 @@ views:
|
|
|
514
812
|
type: list
|
|
515
813
|
scopeProfileSlug: pillar
|
|
516
814
|
description: Content pillars — the recurring themes.
|
|
815
|
+
# The reader's journey, in order. Onboarding seeds a 3-stage ladder and invests
|
|
816
|
+
# a whole opening question in the belief each stage installs — this is where
|
|
817
|
+
# that is seen and edited (without it the stages are write-once, reachable only
|
|
818
|
+
# as a post's picker option). `type: list` reads `sortColumn`/`sortDirection`
|
|
819
|
+
# off `config` (ListAdapter.tsx:107-108, numeric-aware), so the ladder renders
|
|
820
|
+
# in `stage-order` sequence — NOT `sortBy`/`sortOrder`, which is the TABLE
|
|
821
|
+
# adapter's key and would be silently ignored here.
|
|
822
|
+
- name: Journey
|
|
823
|
+
slug: content-journey
|
|
824
|
+
type: list
|
|
825
|
+
scopeProfileSlug: journey-stage
|
|
826
|
+
description: The reader's journey, in order — each stage's belief and the doubt it answers.
|
|
827
|
+
config:
|
|
828
|
+
sortColumn: stage-order
|
|
829
|
+
sortDirection: asc
|
|
517
830
|
- name: Platforms
|
|
518
831
|
slug: content-platforms
|
|
519
832
|
type: table
|
|
@@ -525,6 +838,31 @@ views:
|
|
|
525
838
|
scopeProfileSlug: newsletter
|
|
526
839
|
groupBy: newsletter-status
|
|
527
840
|
description: Newsletter issues by status.
|
|
841
|
+
# ⚠️ NO `Journey Board` (matrix) view here — DELIBERATE, do not "restore" it.
|
|
842
|
+
# A coverage grid is the right idea and this is the wrong substrate today:
|
|
843
|
+
# `MatrixAdapter` derives each axis from the property's enum constraint, else
|
|
844
|
+
# from the values PRESENT on the loaded entities (MatrixAdapter.tsx:83-137).
|
|
845
|
+
# Both axes would be `entity_id`, which has no enum — so a stage no post
|
|
846
|
+
# occupies gets no column and a pillar with nothing written gets no row. The
|
|
847
|
+
# one thing a coverage grid exists to show — the EMPTY cell — is precisely
|
|
848
|
+
# what it cannot render. Axis labels would also be raw entity ids (nothing in
|
|
849
|
+
# view-renderer resolves an `entity_id` to a title; TableAdapter.tsx:53 says
|
|
850
|
+
# the same). Prerequisite: teach `deriveAxisValues` to seed axes from the
|
|
851
|
+
# TARGET profile's entities for `entity_id` fields, and resolve labels. Until
|
|
852
|
+
# then the coverage read is the `Plan Next Content` playbook's job, which does
|
|
853
|
+
# it properly by reading the corpus.
|
|
854
|
+
#
|
|
855
|
+
# The curriculum. Scoped to `series`, NOT a timeline over posts: a timeline
|
|
856
|
+
# keyed on `publish-date` silently DROPS every undated entity
|
|
857
|
+
# (TimelineAdapter.tsx:128) — which is every post onboarding creates, all of
|
|
858
|
+
# them status `Idea` — and sorts newest-first (`:144,147`), so a curriculum
|
|
859
|
+
# would read chapter 5 above chapter 1. Per-series ordering (`post-position`)
|
|
860
|
+
# belongs on the series detail page, not on a corpus-wide time axis.
|
|
861
|
+
- name: Series
|
|
862
|
+
slug: content-series
|
|
863
|
+
type: list
|
|
864
|
+
scopeProfileSlug: series
|
|
865
|
+
description: The curriculums you're running — each an ordered arc through one pillar.
|
|
528
866
|
|
|
529
867
|
# App reference — view-driven, no custom app (mirrors marketing.yaml)
|
|
530
868
|
app:
|
|
@@ -549,28 +887,48 @@ layout:
|
|
|
549
887
|
viewName: Pillars
|
|
550
888
|
label: Pillars
|
|
551
889
|
icon: columns
|
|
890
|
+
- kind: view
|
|
891
|
+
viewName: Journey
|
|
892
|
+
label: Journey
|
|
893
|
+
icon: footprints
|
|
894
|
+
- kind: view
|
|
895
|
+
viewName: Series
|
|
896
|
+
label: Series
|
|
897
|
+
icon: layers
|
|
552
898
|
- kind: view
|
|
553
899
|
viewName: Platforms
|
|
554
900
|
label: Platforms
|
|
555
901
|
icon: share-2
|
|
902
|
+
# Last: a creator with no newsletter never fills this, and an empty kanban
|
|
903
|
+
# high in the rail reads as a workspace that isn't working.
|
|
556
904
|
- kind: view
|
|
557
905
|
viewName: Newsletters
|
|
558
906
|
label: Newsletters
|
|
559
907
|
icon: mail
|
|
560
908
|
|
|
561
|
-
# Bento dashboard
|
|
909
|
+
# Bento dashboard = the STUDIO HOME (the browser landing surface — `defaultView`
|
|
910
|
+
# is inert in browser/, so the workspace opens on this bento, not on a view).
|
|
911
|
+
# Ordered so a FRESH workspace still says something: the Coverage grid leads
|
|
912
|
+
# (the "where am I thin" read — a custom cell, `content-coverage-map`, since a
|
|
913
|
+
# generic matrix can't render an entity_id×entity_id grid's empty cells), then
|
|
914
|
+
# Pillars and Posts which onboarding fills, then the calendar below (empty until
|
|
915
|
+
# something carries a publish date). Newsletters deliberately absent — it stays
|
|
916
|
+
# empty forever for anyone who doesn't run one.
|
|
562
917
|
bento:
|
|
563
918
|
viewName: Posts
|
|
564
919
|
blocks:
|
|
565
|
-
- kind:
|
|
566
|
-
|
|
567
|
-
pos: { x: 0, y: 0, w:
|
|
920
|
+
- kind: widget
|
|
921
|
+
widgetType: content-coverage-map
|
|
922
|
+
pos: { x: 0, y: 0, w: 6, h: 5 }
|
|
568
923
|
- kind: view
|
|
569
924
|
viewName: Pillars
|
|
570
|
-
pos: { x:
|
|
925
|
+
pos: { x: 6, y: 0, w: 6, h: 5 }
|
|
571
926
|
- kind: view
|
|
572
|
-
viewName:
|
|
573
|
-
pos: { x:
|
|
927
|
+
viewName: Posts
|
|
928
|
+
pos: { x: 0, y: 5, w: 6, h: 5 }
|
|
929
|
+
- kind: view
|
|
930
|
+
viewName: Content Calendar
|
|
931
|
+
pos: { x: 6, y: 5, w: 6, h: 5 }
|
|
574
932
|
|
|
575
933
|
# Editorial graph: connect posts to campaign / pillar / sponsor
|
|
576
934
|
entityLinks:
|
|
@@ -586,6 +944,25 @@ entityLinks:
|
|
|
586
944
|
targetProfileSlug: sponsor
|
|
587
945
|
type: sponsored_by
|
|
588
946
|
label: Sponsor
|
|
947
|
+
# The journey edges. `derived_from` is a self-link on `post` — the repurpose
|
|
948
|
+
# chain (one idea → thread → video → newsletter section) finally as a real
|
|
949
|
+
# graph edge instead of the free text it lived in.
|
|
950
|
+
- sourceProfileSlug: post
|
|
951
|
+
targetProfileSlug: journey-stage
|
|
952
|
+
type: at_journey_stage
|
|
953
|
+
label: Journey Stage
|
|
954
|
+
- sourceProfileSlug: post
|
|
955
|
+
targetProfileSlug: series
|
|
956
|
+
type: part_of_series
|
|
957
|
+
label: Series
|
|
958
|
+
- sourceProfileSlug: post
|
|
959
|
+
targetProfileSlug: offer
|
|
960
|
+
type: teaches_toward
|
|
961
|
+
label: Offer
|
|
962
|
+
- sourceProfileSlug: post
|
|
963
|
+
targetProfileSlug: post
|
|
964
|
+
type: derived_from
|
|
965
|
+
label: Repurposed From
|
|
589
966
|
|
|
590
967
|
# Playbooks — session templates seeded with the workspace.
|
|
591
968
|
# `subjectProfile` binds this playbook to the `post` kind so the Capture → Session
|
|
@@ -609,21 +986,58 @@ playbooks:
|
|
|
609
986
|
executor: is-agent
|
|
610
987
|
status: active
|
|
611
988
|
|
|
989
|
+
# The proactive half. Deliberately has NO `subjectProfile`: a subject profile
|
|
990
|
+
# binds a playbook to ONE entity, so pairing it with a cron fans out into one
|
|
991
|
+
# run PER EXISTING POST (capped at 100) — exactly the wrong shape for a
|
|
992
|
+
# question about the corpus as a whole. This one reasons over everything at
|
|
993
|
+
# once and proposes the NEXT piece, which is why it also takes no params.
|
|
994
|
+
# Weekly, because a content plan that is re-litigated daily is not a plan.
|
|
995
|
+
- name: Plan Next Content
|
|
996
|
+
description: Audit the whole corpus against the journey, find the gap, and propose what to make next.
|
|
997
|
+
goalTemplate: >-
|
|
998
|
+
Audit the content corpus. Map every post onto its pillar and its journey
|
|
999
|
+
stage, and list which pillar × stage cells are empty or thin. Check the
|
|
1000
|
+
running series for the chapter that is missing. If the pod has offers,
|
|
1001
|
+
note which ones no content teaches toward; if it has none, plan purely for
|
|
1002
|
+
audience-building and do not invent commerce. Then propose the next one or
|
|
1003
|
+
two pieces — each as a post idea placed on a pillar, a stage, and a
|
|
1004
|
+
platform, with the hook and the reason it is next.
|
|
1005
|
+
executor: is-agent
|
|
1006
|
+
# Monday 09:00, shipped OFF — the operator opts in. `enabled` is read
|
|
1007
|
+
# STRICTLY (`cron-automation.ts:43` is `s.enabled === true`, and `:194`
|
|
1008
|
+
# skips a falsy one), so OMITTING the key does not mean "on by default":
|
|
1009
|
+
# it means the backing cron automation is never created and this playbook
|
|
1010
|
+
# silently never fires. Declaring it explicitly is the difference between
|
|
1011
|
+
# an opt-in cadence and a dead one. Off is the right default for a
|
|
1012
|
+
# `isPublic: true` template — every installer would otherwise be enrolled
|
|
1013
|
+
# in a recurring agent run (real token cost, a queue of proposals) they
|
|
1014
|
+
# never asked for. Matches `ecosystem.yaml:413,436`, same cadence.
|
|
1015
|
+
schedule: { cron: "0 9 * * 1", enabled: false }
|
|
1016
|
+
status: active
|
|
1017
|
+
|
|
612
1018
|
onboarding:
|
|
613
1019
|
goal: >
|
|
614
|
-
Stand up the editorial engine — the pillars you're known for, the
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1020
|
+
Stand up the editorial engine — the pillars you're known for, the journey a
|
|
1021
|
+
reader travels through them, the platforms you publish on, the cadence you
|
|
1022
|
+
can actually sustain, and a first post placed in the system — so the
|
|
1023
|
+
workspace mirrors how you turn thinking into published content that
|
|
1024
|
+
compounds, not a blank calendar.
|
|
618
1025
|
framing: >
|
|
619
1026
|
THE EDITOR-IN-CHIEF: Act as a seasoned content strategist who has grown owned
|
|
620
1027
|
audiences from zero. Anchor everything on PILLARS — the two-to-four recurring
|
|
621
1028
|
themes an audience comes to you for — because pillars are what make a content
|
|
622
|
-
engine compound instead of drift.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1029
|
+
engine compound instead of drift. Then ask the second question most creators
|
|
1030
|
+
never ask: where is the reader when they arrive, and where are you taking
|
|
1031
|
+
them? That is the JOURNEY — the ladder of belief a reader climbs, which turns a feed into a path.
|
|
1032
|
+
Pull voice and identity from Brand and the target audience from Foundation so
|
|
1033
|
+
every piece lands on-strategy. Push for a cadence they can hold for a year,
|
|
1034
|
+
not a heroic month. Be concrete: real platforms, real formats, a real first
|
|
1035
|
+
post — a fuzzy "I'll post more" helps no one.
|
|
1036
|
+
COMMERCE IS OPTIONAL AND YOU MUST TREAT IT THAT WAY. Ask ONCE whether they
|
|
1037
|
+
sell anything. If the answer is no, or vague, SKIP offers entirely, never
|
|
1038
|
+
mention them again, and build the journey as pure audience-building — a
|
|
1039
|
+
journey with no product at the end is a complete, correct answer, not a
|
|
1040
|
+
half-finished one. Only if they name something real do you capture it.
|
|
627
1041
|
expertise:
|
|
628
1042
|
starters:
|
|
629
1043
|
- >
|
|
@@ -640,7 +1054,19 @@ onboarding:
|
|
|
640
1054
|
One idea, many formats — design for repurposing from the start. A single
|
|
641
1055
|
strong thought becomes a thread, a short video, a newsletter section, and
|
|
642
1056
|
a carousel. Propose the repurpose chain up front so one capture yields a
|
|
643
|
-
week of output,
|
|
1057
|
+
week of output, linked via `derived-from`.
|
|
1058
|
+
- >
|
|
1059
|
+
Most creators have never named the journey, so don't ask them to invent
|
|
1060
|
+
one — PROPOSE a three-stage ladder and let them rename it. The usual
|
|
1061
|
+
shape is: doesn't know the problem is theirs → believes it's solvable →
|
|
1062
|
+
ready to act. For each stage, push for the BELIEF it installs and the
|
|
1063
|
+
OBJECTION it removes; a stage with only a name teaches the engine
|
|
1064
|
+
nothing.
|
|
1065
|
+
- >
|
|
1066
|
+
A series is how a pillar becomes a curriculum. Once the pillars are
|
|
1067
|
+
named, propose ONE series inside the strongest of them — an ordered run
|
|
1068
|
+
that takes a reader from not-knowing to able-to-do — and place the first
|
|
1069
|
+
post as chapter one. One real series beats five planned ones.
|
|
644
1070
|
blindSpots:
|
|
645
1071
|
- >
|
|
646
1072
|
No pillars = brand drift. If they list a dozen post ideas but can't name
|
|
@@ -655,27 +1081,57 @@ onboarding:
|
|
|
655
1081
|
Chasing every platform at once guarantees all of them stay shallow. If
|
|
656
1082
|
they name five channels, push back: which ONE or two get real cadence and
|
|
657
1083
|
attention now, and which are experiments?
|
|
1084
|
+
- >
|
|
1085
|
+
Almost every corpus is stacked at one end of the journey — usually all
|
|
1086
|
+
top-of-funnel awareness, or all deep expert material for people who
|
|
1087
|
+
already believe. Once the stages exist, say which end is empty. That gap
|
|
1088
|
+
is the single most useful thing this workspace can tell them.
|
|
658
1089
|
bar: >
|
|
659
|
-
The workspace reads like a running publication: a handful
|
|
660
|
-
each tied to an audience,
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
1090
|
+
The workspace reads like a running publication with a direction: a handful
|
|
1091
|
+
of named pillars each tied to an audience, a journey the reader visibly
|
|
1092
|
+
travels, the platforms actually committed to with a sustainable cadence, an
|
|
1093
|
+
owned newsletter channel, and a first post placed in a pillar — so a
|
|
1094
|
+
strategist could see at a glance what the brand is about, who it is taking
|
|
1095
|
+
where, where it publishes, and what ships next. Close by telling them they
|
|
1096
|
+
can ask "what should I write next?" anytime to see where the journey is
|
|
1097
|
+
thin — so the coverage read is a habit, not a feature they never find.
|
|
664
1098
|
collect:
|
|
665
1099
|
- profileSlug: pillar
|
|
666
|
-
what: The recurring themes you're known for — the 2–4 lenses your audience comes to you for, each
|
|
1100
|
+
what: The recurring themes you're known for — the 2–4 lenses your audience comes to you for, each pointed at the audience it speaks to and the goal it serves.
|
|
1101
|
+
cardinality: few
|
|
1102
|
+
keyFields: [pillar-status, pillar-audience-ref, pillar-goal]
|
|
1103
|
+
- profileSlug: journey-stage
|
|
1104
|
+
what: The steps a reader travels, in order — propose a three-stage ladder and let them rename it. Each stage needs the belief it installs and the objection it removes, not just a label.
|
|
667
1105
|
cardinality: few
|
|
668
|
-
keyFields: [
|
|
1106
|
+
keyFields: [stage-order, belief-installed, objection-removed]
|
|
1107
|
+
- profileSlug: series
|
|
1108
|
+
what: One ordered run of posts inside the strongest pillar that takes a reader from not-knowing to able-to-do. One real series, not a backlog of planned ones.
|
|
1109
|
+
cardinality: one
|
|
1110
|
+
keyFields: [series-status, series-goal, series-pillar]
|
|
669
1111
|
- profileSlug: platform
|
|
670
1112
|
what: The channels you actually publish on — the social accounts, newsletter, or podcast — with their status and the cadence you can hold.
|
|
671
1113
|
cardinality: several
|
|
672
1114
|
keyFields: [platform-type, platform-status, platform-posting-freq]
|
|
673
1115
|
- profileSlug: post
|
|
674
|
-
what: A first post or two already in motion, each placed on a pillar and a platform at its true status (Idea through Published).
|
|
1116
|
+
what: A first post or two already in motion, each placed on a pillar and a platform at its true status (Idea through Published), and on a stage if the journey exists.
|
|
1117
|
+
cardinality: few
|
|
1118
|
+
keyFields: [post-status, post-pillar, post-platform]
|
|
1119
|
+
- profileSlug: offer
|
|
1120
|
+
what: >-
|
|
1121
|
+
ONLY IF THEY SELL SOMETHING. Ask once; if they don't sell, or hedge,
|
|
1122
|
+
create NOTHING here and drop the subject — a content engine with no
|
|
1123
|
+
commercial axis is a complete result. If they do, capture what it is,
|
|
1124
|
+
how it's priced, and who it's for.
|
|
675
1125
|
cardinality: few
|
|
676
|
-
keyFields: [
|
|
1126
|
+
keyFields: [offer-status, offer-summary, offer-price-model]
|
|
677
1127
|
openingQuestions:
|
|
678
1128
|
- "What are the 2–4 themes your audience comes to you for — the pillars everything you make ties back to?"
|
|
1129
|
+
- "Most people have never named the journey, so I'll start you with three stages — doesn't know it's a problem, believes it's solvable, ready to act. Rename them to your world, or leave them and we'll come back to it."
|
|
679
1130
|
- "Which platforms do you actually publish on, and what cadence can you genuinely sustain on each?"
|
|
680
1131
|
- "Do you run (or want) a newsletter — the audience you own outright — and what's the first post you'd place in the system?"
|
|
1132
|
+
# The BAR is deliberately unchanged: pillars, a platform with a cadence, and a
|
|
1133
|
+
# first post. The journey is what makes the engine compound, but requiring it
|
|
1134
|
+
# would make a perfectly good editorial workspace fail its own standard on day
|
|
1135
|
+
# one. Mirrored verbatim by standards/content-os.standard.yaml — the journey
|
|
1136
|
+
# layer lives in its `recommends`, not its `requires`.
|
|
681
1137
|
doneWhen: at least one pillar + one platform with a cadence + a first post placed on a pillar.
|