@synap-core/workspace-templates 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: Repurposed From
220
+ label: Repurpose Notes
162
221
  valueType: string
163
222
  inputType: text
164
- placeholder: "Source post or content this was adapted from"
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 Segment
253
+ label: Audience Notes
186
254
  valueType: string
187
255
  inputType: text
188
- placeholder: "Who does this pillar speak to?"
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: view
566
- viewName: Content Calendar
567
- pos: { x: 0, y: 0, w: 12, h: 6 }
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: 0, y: 6, w: 6, h: 5 }
925
+ pos: { x: 6, y: 0, w: 6, h: 5 }
571
926
  - kind: view
572
- viewName: Newsletters
573
- pos: { x: 6, y: 6, w: 6, h: 5 }
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 platforms
615
- you publish on, the cadence you can actually sustain, and a first post placed
616
- in the system — so the workspace mirrors how you turn thinking into published
617
- content, not a blank calendar.
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. Pull voice and identity from Brand and the
623
- target audience from Foundation so every piece lands on-strategy. Push for a
624
- cadence they can hold for a year, not a heroic month. Be concrete: real
625
- platforms, real formats, a real first post — a fuzzy "I'll post more" helps
626
- no one.
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, tracked via `repurposed-from`.
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 of named pillars
660
- each tied to an audience, the platforms actually committed to with a
661
- sustainable cadence, an owned newsletter channel, and a first post placed in
662
- a pillar — so a strategist could see at a glance what the brand is about,
663
- where it publishes, and what ships next.
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 with the segment it speaks to and the goal it serves.
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: [pillar-status, pillar-audience, pillar-formats]
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: [post-status, post-format, post-platform]
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.