@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/src/crm.yaml CHANGED
@@ -462,6 +462,52 @@ playbooks:
462
462
  - entity.create
463
463
  status: active
464
464
 
465
+ # ── CRM Hygiene — always-on maintenance agent ────────────────────────────
466
+ # A scheduled agent that keeps the pipeline healthy: (1) enriches stale
467
+ # contact/company records with missing or outdated fields, and (2) flags deals
468
+ # that have stalled (no recent activity / stuck in a stage past a healthy
469
+ # threshold). It runs UNATTENDED on a daily schedule, so EVERY change it makes
470
+ # is surfaced as a PROPOSAL for the user to review — never auto-applied. That is
471
+ # enforced by `metadata.governance.forceProposeWrites`, which `executePlaybookRun`
472
+ # stamps onto the run's focus session; the write gate then routes every agent
473
+ # write to a proposal (permission-check `deriveSessionForceProposeGovernance`).
474
+ # No subjectProfile → ONE reasoning session per run that sweeps the whole
475
+ # pipeline (not one session per entity), which keeps within the 10-proposals/
476
+ # user/day agent cap.
477
+ - name: CRM Hygiene
478
+ description: Always-on maintenance — enriches stale contacts/companies and flags stalled deals. Every change is proposed for your review, never auto-applied.
479
+ goalTemplate: >-
480
+ You are the CRM hygiene maintenance agent, running unattended on a daily
481
+ schedule. Keep the pipeline healthy in two passes, and remember: EVERY
482
+ change you make is a proposal for the user to review, so be precise and
483
+ never fabricate.
484
+
485
+ Pass 1 — Enrich stale records. Find `person` and `company` records that are
486
+ missing key fields (e.g. industry, companySize, location, jobTitle, email,
487
+ website) or whose `lastContactDate` is old. For each, fill in what you can
488
+ verify from existing pod data and public sources (use the enrichment tools
489
+ you are granted); leave a field blank rather than guess. Do not touch
490
+ records that are already complete and current.
491
+
492
+ Pass 2 — Flag stalled deals. Find `deal` records with no activity for a
493
+ while or stuck in the same `dealStage` past a healthy threshold (roughly:
494
+ contacted/qualified for 14+ days, proposal/negotiation for 30+ days).
495
+ For each stalled deal, propose a concise `nextStep` to unblock it and note
496
+ why it looks stalled. Do not advance a deal's stage on its behalf — surface
497
+ the risk and a recommended action for the human to decide.
498
+
499
+ Skip anything already healthy. If nothing needs attention, do nothing.
500
+ schedule:
501
+ cron: "0 8 * * *"
502
+ enabled: true
503
+ metadata:
504
+ governance:
505
+ forceProposeWrites: true
506
+ executor: is-agent
507
+ grants:
508
+ - apify_run_actor
509
+ status: active
510
+
465
511
  # Apify powers the enrichment playbook (scrape public sources for a lead's
466
512
  # firmographics). Composed pod-wide at provisioning via the existing
467
513
  # integrations door; the user supplies the Apify API key once on install — a
@@ -86,11 +86,22 @@ profiles:
86
86
  valueType: string
87
87
  inputType: textarea
88
88
 
89
+ # ── Audience ──────────────────────────────────────────────────────────────
90
+ # `scope: shared` + `entityScope: pod`: an ideal customer profile is not a
91
+ # foundation-local fact, it is the pod's answer to "who is this for". Content
92
+ # OS pillars, journey stages, and marketing segments all point AT these rows,
93
+ # so both axes have to open: `shared` makes the DEFINITION reachable across a
94
+ # `require` boundary (a consumer that re-declares the slug gets the grant),
95
+ # `entityScope: pod` makes the ROWS land with `workspace_id = NULL` so a
96
+ # sibling workspace's picker can actually see them. Neither alone is enough —
97
+ # they are independent axes (see TemplateProfile.entityScope).
89
98
  - slug: audience
90
99
  displayName: Audience
91
100
  icon: users
92
101
  color: blue
93
102
  description: "An ideal customer profile — who we are built for."
103
+ scope: shared
104
+ entityScope: pod
94
105
  properties:
95
106
  - slug: segment
96
107
  label: Segment
@@ -109,11 +120,15 @@ profiles:
109
120
  valueType: array
110
121
  inputType: text
111
122
 
123
+ # Shared + pod-scoped for the same reason as `audience`: a pain is the thing
124
+ # content is written to dissolve, so a Content OS pillar links to it directly.
112
125
  - slug: pain-point
113
126
  displayName: Pain Point
114
127
  icon: flame
115
128
  color: orange
116
129
  description: "A specific pain a target audience feels today."
130
+ scope: shared
131
+ entityScope: pod
117
132
  properties:
118
133
  - slug: description
119
134
  label: Description
@@ -135,11 +150,21 @@ profiles:
135
150
  valueType: string
136
151
  inputType: textarea
137
152
 
153
+ # ── Messaging kinds (positioning / narrative / differentiator) ────────────
154
+ # `scope: shared` because Communication RE-DECLARES all three: they are the
155
+ # on-message layer every downstream domain reads, and without `shared` the
156
+ # re-declaration mints a SECOND row and the two drift silently (that fork is
157
+ # what the comment at communication.yaml:42 used to hide). `positioning` also
158
+ # takes `entityScope: pod`, because offers and journey stages point at the
159
+ # positioning ROW from other workspaces; narrative and differentiator stay
160
+ # workspace-placed — nothing references their rows across a boundary yet.
138
161
  - slug: positioning
139
162
  displayName: Positioning
140
163
  icon: crosshair
141
164
  color: cyan
142
165
  description: "Market category, who we stand against, and why we win."
166
+ scope: shared
167
+ entityScope: pod
143
168
  properties:
144
169
  - slug: category
145
170
  label: Category
@@ -159,6 +184,7 @@ profiles:
159
184
  icon: book-open
160
185
  color: amber
161
186
  description: "The origin -> insight -> mission story arc."
187
+ scope: shared
162
188
  properties:
163
189
  - slug: arc
164
190
  label: Story arc
@@ -176,6 +202,7 @@ profiles:
176
202
  icon: sparkles
177
203
  color: green
178
204
  description: "One thing only we do, believe, or refuse."
205
+ scope: shared
179
206
  properties:
180
207
  - slug: statement
181
208
  label: Statement
@@ -201,6 +228,72 @@ profiles:
201
228
  valueType: string
202
229
  inputType: textarea
203
230
 
231
+ # ═══════════════════════════════════════════════════════════════════════════
232
+ # OFFER — the thing being sold, pod-wide (SSOT)
233
+ #
234
+ # Nothing in the corpus represented a SKU before this. It belongs in
235
+ # foundation because an offer is IDENTITY, not content: it is the commercial
236
+ # expression of the positioning, aimed at an audience, and it is read by every
237
+ # commercial domain (a CRM deal is a deal ON an offer; a marketing campaign
238
+ # promotes one; a Content OS post teaches toward one). Same mechanics as the
239
+ # ecosystem roles below — `scope: shared` so the DEFINITION crosses a `require`
240
+ # boundary once a consumer re-declares the slug, `entityScope: pod` so the ROWS
241
+ # are visible from every workspace. It is a KIND, not a role: an offer has its
242
+ # own price, lifecycle, and positioning — it is not a hat some other entity
243
+ # wears.
244
+ #
245
+ # OPTIONAL BY CONSTRUCTION. A user who sells nothing never creates one, and
246
+ # every consumer must treat absence as "no commercial axis" rather than an
247
+ # empty required field. Deliberately thin: what it is, whether it is live, how
248
+ # it is priced, and the two identity rows it hangs off. Money AMOUNTS live on
249
+ # the deal/contract that references the offer, not here — an offer's price is a
250
+ # model, not a number.
251
+ # ═══════════════════════════════════════════════════════════════════════════
252
+ - slug: offer
253
+ displayName: Offer
254
+ icon: package
255
+ color: fuchsia
256
+ description: "Something the company sells — a product, service, or program. Optional: a pod that sells nothing simply has none."
257
+ scope: shared
258
+ entityScope: pod
259
+ properties:
260
+ - slug: offer-status
261
+ label: Status
262
+ valueType: string
263
+ inputType: select
264
+ enumValues:
265
+ - draft
266
+ - active
267
+ - paused
268
+ - retired
269
+ constraints: { defaultValue: draft }
270
+ - slug: offer-summary
271
+ label: Summary
272
+ valueType: string
273
+ inputType: textarea
274
+ placeholder: "What it is and what changes for the buyer"
275
+ - slug: offer-price-model
276
+ label: Price Model
277
+ valueType: string
278
+ inputType: select
279
+ enumValues:
280
+ - one-off
281
+ - subscription
282
+ - retainer
283
+ - usage-based
284
+ - free
285
+ - other
286
+ - slug: offer-audience
287
+ label: Audience
288
+ valueType: entity_id
289
+ targetProfileSlug: audience
290
+ inputType: entity_select
291
+ - slug: offer-positioning
292
+ label: Positioning
293
+ valueType: entity_id
294
+ targetProfileSlug: positioning
295
+ inputType: entity_select
296
+
204
297
  # ═══════════════════════════════════════════════════════════════════════════
205
298
  # ECOSYSTEM ROLES — pod-wide, cross-workspace role vocabulary (SSOT)
206
299
  #
@@ -345,7 +345,7 @@ views:
345
345
  scopeProfileSlug: ritual
346
346
  description: Recurring meetings and cadences on a calendar.
347
347
  config:
348
- dateField: ritual-date
348
+ dateField: properties.ritual-date
349
349
  colorField: cadence
350
350
 
351
351
  # Schema-level links — give the relation picker and graph domain-specific
@@ -493,7 +493,7 @@ views:
493
493
  scopeProfileSlugs: [task, sprint, project]
494
494
  description: "Unified calendar of task due dates, sprint cycles, and project deadlines"
495
495
  config:
496
- dateField: task-due-date
496
+ dateField: properties.task-due-date
497
497
 
498
498
  - name: OKRs by Quarter
499
499
  slug: okr-by-quarter