@synap-core/workspace-templates 0.7.2 → 0.7.4
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 +207 -8
- package/dist/index.js +2122 -1427
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-fleet.yaml +666 -108
- package/src/blockchain-ecosystem.yaml +1 -0
- package/src/brand-library.yaml +127 -12
- package/src/builder.yaml +116 -12
- package/src/business-developer.yaml +102 -18
- package/src/catalog.ts +13 -3
- package/src/communication.yaml +23 -453
- package/src/composition-graph.test.ts +26 -0
- package/src/content-os.yaml +103 -10
- package/src/content-studio.yaml +1 -0
- package/src/crm.yaml +12 -0
- package/src/define.ts +8 -8
- package/src/dev-dashboard.yaml +43 -9
- package/src/domains.test.ts +117 -0
- package/src/domains.ts +102 -0
- package/src/ecosystem.yaml +21 -4
- package/src/enterprise-os.yaml +5 -0
- package/src/finance.yaml +46 -5
- package/src/foundation.yaml +13 -1
- package/src/grants.yaml +1 -0
- package/src/hr.yaml +61 -8
- package/src/index.ts +29 -0
- package/src/internal-runbook.yaml +36 -5
- package/src/legal.yaml +97 -12
- package/src/life-os.yaml +188 -27
- package/src/marketing.yaml +15 -2
- package/src/meetings-knowledge.yaml +7 -1
- package/src/operations.yaml +351 -0
- package/src/outreach-comms.yaml +6 -1
- package/src/project-management.yaml +840 -111
- package/src/research-base.yaml +516 -0
- package/src/research-decision.yaml +158 -0
- package/src/shared-role-ssot.test.ts +268 -0
- package/src/social.yaml +105 -37
- package/src/templates.ts +4573 -4302
- package/src/the-arch.yaml +11 -0
- package/src/types.ts +9 -0
- package/src/validate.test.ts +81 -0
- package/src/validate.ts +561 -0
- package/src/personal.yaml +0 -177
package/src/operations.yaml
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
meta:
|
|
7
7
|
slug: operations
|
|
8
|
+
domain: company
|
|
8
9
|
name: Operations
|
|
9
10
|
description: "Deliver client work end to end — the engagements you're running, the deliverables due inside them, and the contracts that govern them. Won deals from CRM hand off into engagements here."
|
|
10
11
|
icon: briefcase
|
|
@@ -25,6 +26,19 @@ workspace:
|
|
|
25
26
|
crm: consumer
|
|
26
27
|
operations: provider
|
|
27
28
|
|
|
29
|
+
# The daily client-briefing automation below enumerates clients with
|
|
30
|
+
# `entity.query { profileSlug: "client" }`, and that verb FAILS CLOSED on an
|
|
31
|
+
# unknown slug (`assertKnownProfileSlug`) — an Operations workspace installed
|
|
32
|
+
# without the pod-wide `client` role would kill the run on its first node. The
|
|
33
|
+
# `client` role is foundation's SSOT (foundation.yaml, `scope: shared`,
|
|
34
|
+
# `profileKind: role`), so require it, exactly like crm.yaml does. `require`,
|
|
35
|
+
# not `compose`: foundation is a real workspace, not an overlay.
|
|
36
|
+
dependencies:
|
|
37
|
+
- slug: foundation
|
|
38
|
+
kind: workspace
|
|
39
|
+
relation: require
|
|
40
|
+
reason: "Provides the pod-wide `client` role the daily client-briefing automation queries."
|
|
41
|
+
|
|
28
42
|
profiles:
|
|
29
43
|
- slug: engagement
|
|
30
44
|
displayName: Engagement
|
|
@@ -140,6 +154,65 @@ profiles:
|
|
|
140
154
|
valueType: string
|
|
141
155
|
inputType: textarea
|
|
142
156
|
|
|
157
|
+
# The durable OUTPUT of the daily briefing automation. A recap that only ever
|
|
158
|
+
# exists as a chat message is not knowledge — it cannot be queried, ranked, or
|
|
159
|
+
# linked. This kind is what turns the digest into structure: one dated row per
|
|
160
|
+
# client per run, linked to the client entity and readable long after the
|
|
161
|
+
# message scrolled away. Written to the TEAM side only; never client-facing.
|
|
162
|
+
- slug: client-briefing
|
|
163
|
+
displayName: Client Briefing
|
|
164
|
+
icon: newspaper
|
|
165
|
+
color: "#F59E0B"
|
|
166
|
+
description: "A dated internal recap of one client's recent communications — what moved, what's at risk, what's next. For the team, never for the client."
|
|
167
|
+
properties:
|
|
168
|
+
- slug: briefing-client
|
|
169
|
+
label: Client
|
|
170
|
+
valueType: entity_id
|
|
171
|
+
targetProfileSlug: company
|
|
172
|
+
inputType: entity_select
|
|
173
|
+
- slug: briefing-date
|
|
174
|
+
label: Written At
|
|
175
|
+
valueType: date
|
|
176
|
+
inputType: date
|
|
177
|
+
# Idempotency key, NOT display. `output/entity_create`'s `dedupeBy` matches
|
|
178
|
+
# on this exact value, so a redelivered run reuses the row it already wrote
|
|
179
|
+
# instead of appending a second briefing for the same client and tick.
|
|
180
|
+
- slug: briefing-key
|
|
181
|
+
label: Run Key
|
|
182
|
+
valueType: string
|
|
183
|
+
inputType: text
|
|
184
|
+
- slug: briefing-headline
|
|
185
|
+
label: Headline
|
|
186
|
+
valueType: string
|
|
187
|
+
inputType: text
|
|
188
|
+
- slug: briefing-summary
|
|
189
|
+
label: Summary
|
|
190
|
+
valueType: string
|
|
191
|
+
inputType: textarea
|
|
192
|
+
- slug: briefing-risks
|
|
193
|
+
label: Risks
|
|
194
|
+
valueType: string
|
|
195
|
+
inputType: textarea
|
|
196
|
+
- slug: briefing-next-actions
|
|
197
|
+
label: Next Actions
|
|
198
|
+
valueType: string
|
|
199
|
+
inputType: textarea
|
|
200
|
+
- slug: briefing-message-count
|
|
201
|
+
label: Messages Read
|
|
202
|
+
valueType: number
|
|
203
|
+
inputType: number
|
|
204
|
+
|
|
205
|
+
# Schema relations. `briefing_about_client` MUST be declared here: an automation
|
|
206
|
+
# `relation_create` dispatches `graph.link` → `relations.create`, which rejects
|
|
207
|
+
# any type that is neither a system built-in nor a workspace relation def
|
|
208
|
+
# (routers/relations.ts — "Unknown relation type"). The applier find-or-creates
|
|
209
|
+
# the def from this block, so the automation's link resolves.
|
|
210
|
+
entityLinks:
|
|
211
|
+
- sourceProfileSlug: client-briefing
|
|
212
|
+
targetProfileSlug: company
|
|
213
|
+
type: briefing_about_client
|
|
214
|
+
label: Client
|
|
215
|
+
|
|
143
216
|
# CURATED views — the delivery motion, not one per profile
|
|
144
217
|
views:
|
|
145
218
|
- name: Engagements
|
|
@@ -159,6 +232,11 @@ views:
|
|
|
159
232
|
type: table
|
|
160
233
|
scopeProfileSlug: contract
|
|
161
234
|
description: "Contracts with their status, value, and term."
|
|
235
|
+
- name: Client Briefings
|
|
236
|
+
slug: client-briefings
|
|
237
|
+
type: list
|
|
238
|
+
scopeProfileSlug: client-briefing
|
|
239
|
+
description: "The daily internal recaps, newest first."
|
|
162
240
|
|
|
163
241
|
onboarding:
|
|
164
242
|
goal: >
|
|
@@ -220,3 +298,276 @@ bento:
|
|
|
220
298
|
- kind: view
|
|
221
299
|
viewName: Contracts
|
|
222
300
|
pos: { x: 6, y: 0, w: 6, h: 5 }
|
|
301
|
+
|
|
302
|
+
# ============================================================================
|
|
303
|
+
# Automations — the daily client-briefing digest.
|
|
304
|
+
#
|
|
305
|
+
# WHAT: once a day, for every `client`, read that client's inbound comms,
|
|
306
|
+
# distil the real signal with ONE batched AI call (never per-message), persist
|
|
307
|
+
# a durable `client-briefing` row, and post the recap to the client's TEAM-side
|
|
308
|
+
# channel. Firewall: the recap NEVER lands in the client-facing channel.
|
|
309
|
+
#
|
|
310
|
+
# GOVERNANCE DESIGN (why this run cannot die on a `proposed` verdict):
|
|
311
|
+
# • The three `capability` nodes call ONLY read-only builtins —
|
|
312
|
+
# `entity.query`, `ai.generate`, `channel.resolve` — all in
|
|
313
|
+
# READ_ONLY_BUILTIN_VERBS, so the capability gate AUTO-RUNS them (no grant,
|
|
314
|
+
# no proposal). A `capability` node THROWS on a `proposed` verdict
|
|
315
|
+
# (automation-executor.ts) — these three can never reach it.
|
|
316
|
+
# • `messages_query` + `condition` are pure read / pure eval — never governed.
|
|
317
|
+
# • The two governed WRITES are `output` nodes, NOT `capability` nodes:
|
|
318
|
+
# – `entity_create` returns `{status:"proposed"}` on a propose verdict
|
|
319
|
+
# (it never throws), and `entity.create` is in DEFAULT_AUTO_APPROVE, so
|
|
320
|
+
# a human-owned or agent-owned run auto-approves and writes directly.
|
|
321
|
+
# – `relation_create` dispatches `graph.link`; the `graph.link` builtin is
|
|
322
|
+
# seeded `approved:true` (rung 2.7 → auto) and `relation.create` is in
|
|
323
|
+
# DEFAULT_AUTO_APPROVE, so it auto-executes — never a propose verdict.
|
|
324
|
+
# – `channel_message` is a direct, firewall-checked insert with NO
|
|
325
|
+
# governance gate at all.
|
|
326
|
+
# • Every write also lives INSIDE the loop body, whose per-item try/catch
|
|
327
|
+
# aborts only THAT client's remaining steps on any error — the RUN itself
|
|
328
|
+
# survives regardless.
|
|
329
|
+
#
|
|
330
|
+
# `status: active` is load-bearing: the cron scheduler selects
|
|
331
|
+
# `status="active" AND nextRunAt <= now`. A `draft` never fires — that was the
|
|
332
|
+
# exact bug this automation replaces. `metadata.templateKey` makes re-apply
|
|
333
|
+
# idempotent (the post-workspace applier dedups by name / templateKey).
|
|
334
|
+
#
|
|
335
|
+
# ⚠️ KNOWN BLOCKER — RETURNS ZERO CLIENTS UNTIL A BACKEND FIX LANDS.
|
|
336
|
+
# `client`/`company` entities are POD-WIDE (`workspaceId: null`). This automation
|
|
337
|
+
# runs under a WORKSPACE LENS, and `workspaceLensWhere`
|
|
338
|
+
# (`packages/api/src/utils/user-visible-where.ts`) excludes pod-wide globals from
|
|
339
|
+
# a focused workspace by design (`includeGlobals` defaults falsy — product
|
|
340
|
+
# decision 2026-06-15). So the `entity.query` verb — which resolves
|
|
341
|
+
# `input.workspaceId ?? ctx.workspaceId` and CANNOT express a global lens (its
|
|
342
|
+
# param is `workspaceId: uuid().optional()`) — enumerates ZERO clients under the
|
|
343
|
+
# Operations lens. The `list-clients` node is deliberately left with NO
|
|
344
|
+
# workspace override so it starts working the moment the backend fix (let
|
|
345
|
+
# `entity.query` express a pod-wide/global lens → `workspaceLensWhere` with
|
|
346
|
+
# `null`/`includeGlobals:true`) lands. NO workaround is baked in here — a
|
|
347
|
+
# hardcoded client list or a workspace-scoped copy would defeat the point.
|
|
348
|
+
#
|
|
349
|
+
# LIMITATION (honest): `messages_query` resolves ONLY the EXTERNAL client-comms
|
|
350
|
+
# channel bound to the client entity — team-side chatter ABOUT a client is not
|
|
351
|
+
# in the digest input. The recap is intentionally still posted to the TEAM
|
|
352
|
+
# channel; it summarises the client conversation, not internal discussion.
|
|
353
|
+
# ============================================================================
|
|
354
|
+
automations:
|
|
355
|
+
- key: operations-daily-client-briefing
|
|
356
|
+
name: Daily client briefing
|
|
357
|
+
description: >-
|
|
358
|
+
Operations-native daily client briefing. Writes a durable, dated
|
|
359
|
+
client-briefing ENTITY per client (queryable history, linked to the
|
|
360
|
+
client) from their recent comms, and posts the recap to the client's
|
|
361
|
+
internal TEAM channel (never the client-facing channel). Knowledge-building
|
|
362
|
+
and entity-first — the delivery OS's structured memory of each client. For
|
|
363
|
+
a lighter, portable message-only digest installable on any pod, use the
|
|
364
|
+
client-comms-report capability.
|
|
365
|
+
triggerType: cron
|
|
366
|
+
triggerConfig:
|
|
367
|
+
expression: "0 8 * * *"
|
|
368
|
+
status: active
|
|
369
|
+
metadata:
|
|
370
|
+
templateKey: operations-daily-client-briefing
|
|
371
|
+
# The `list-clients` node uses `scope: pod` — the entity.query pod-wide
|
|
372
|
+
# opt-in. That opt-in must be DEPLOYED for the query to return pod-wide
|
|
373
|
+
# clients; without it a pod-wide client query yields 0 rows and this
|
|
374
|
+
# automation is a silent no-op (fires on cron, loops over nothing).
|
|
375
|
+
requiresPodWideEntityQueryLens: true
|
|
376
|
+
flow:
|
|
377
|
+
nodes:
|
|
378
|
+
- id: trigger
|
|
379
|
+
type: trigger
|
|
380
|
+
position: { x: 0, y: 0 }
|
|
381
|
+
data:
|
|
382
|
+
label: Daily at 08:00
|
|
383
|
+
triggerType: cron
|
|
384
|
+
config:
|
|
385
|
+
expression: "0 8 * * *"
|
|
386
|
+
|
|
387
|
+
# READ-ONLY builtin. Enumerates clients by the pod-wide `client` ROLE
|
|
388
|
+
# (foundation SSOT). `entity.query` fails CLOSED on an unknown slug via
|
|
389
|
+
# `assertKnownProfileSlug` — the `dependencies: require foundation` block
|
|
390
|
+
# above guarantees the `client` role exists, so the first node can't die.
|
|
391
|
+
#
|
|
392
|
+
# `scope: pod` = the EXPLICIT pod-wide opt-in. `client` entities are
|
|
393
|
+
# pod-wide (workspaceId:null); a run's workspace lens hides pod-wide
|
|
394
|
+
# globals by default (workspaceLensWhere, product decision 2026-06-15),
|
|
395
|
+
# so WITHOUT scope:pod this loops over zero clients. scope:pod pins the
|
|
396
|
+
# globals-only, owner-gated lens, returning exactly the caller's pod-wide
|
|
397
|
+
# clients — an explicit request, NOT globals silently bleeding into a
|
|
398
|
+
# focused workspace. Do NOT "fix" a zero result by hardcoding a client
|
|
399
|
+
# list or a workspace-scoped profile — scope:pod IS the fix.
|
|
400
|
+
- id: list-clients
|
|
401
|
+
type: capability
|
|
402
|
+
position: { x: 0, y: 120 }
|
|
403
|
+
data:
|
|
404
|
+
label: List clients (pod-wide `client` role)
|
|
405
|
+
verbId: entity.query
|
|
406
|
+
inputMapping:
|
|
407
|
+
profileSlug: client
|
|
408
|
+
scope: pod
|
|
409
|
+
limit: "100"
|
|
410
|
+
|
|
411
|
+
- id: clients-loop
|
|
412
|
+
type: loop
|
|
413
|
+
position: { x: 0, y: 240 }
|
|
414
|
+
data:
|
|
415
|
+
label: For each client
|
|
416
|
+
iteratorExpression: steps.list-clients.output.entities
|
|
417
|
+
itemVariable: client
|
|
418
|
+
|
|
419
|
+
# READ. Resolves the client's EXTERNAL client-comms channel bound to the
|
|
420
|
+
# client entity and returns its recent window. Team-side chatter is NOT
|
|
421
|
+
# included (see the LIMITATION note above).
|
|
422
|
+
- id: read-comms
|
|
423
|
+
type: messages_query
|
|
424
|
+
position: { x: 0, y: 360 }
|
|
425
|
+
data:
|
|
426
|
+
label: Read this client's recent comms
|
|
427
|
+
subjectEntityId: "{{loop.item.id}}"
|
|
428
|
+
limit: 40
|
|
429
|
+
|
|
430
|
+
# READ-ONLY builtin (pure compute — mutates nothing, auto-runs). ONE
|
|
431
|
+
# batched digest for the whole window; NO per-message AI. Returns STRICT
|
|
432
|
+
# JSON whose fields map onto the client-briefing properties below.
|
|
433
|
+
- id: digest
|
|
434
|
+
type: capability
|
|
435
|
+
position: { x: 0, y: 480 }
|
|
436
|
+
data:
|
|
437
|
+
label: Distil the real signal → JSON
|
|
438
|
+
verbId: ai.generate
|
|
439
|
+
inputMapping:
|
|
440
|
+
json: "true"
|
|
441
|
+
maxTokens: "1200"
|
|
442
|
+
system: >-
|
|
443
|
+
You advise a client-delivery team. You read one client's recent
|
|
444
|
+
inbound messages and surface only what genuinely matters. You
|
|
445
|
+
are firewall-aware: your output is for the INTERNAL team, never
|
|
446
|
+
the client.
|
|
447
|
+
prompt: >-
|
|
448
|
+
Digest the recent communications for the client
|
|
449
|
+
"{{loop.item.title}}" into a concise internal briefing for OUR
|
|
450
|
+
delivery team.
|
|
451
|
+
|
|
452
|
+
PEOPLE GUARD (do not break): distinguish the CLIENT's people
|
|
453
|
+
from OUR team members who also appear in the thread. Never treat
|
|
454
|
+
a teammate as a client contact, lead, or new person. Only real,
|
|
455
|
+
client-side signal counts.
|
|
456
|
+
|
|
457
|
+
Surface ONLY real signal: status changes, decisions,
|
|
458
|
+
deals/opportunities, competitor mentions, new client-side
|
|
459
|
+
leads/contacts, risks/blockers, and deadlines/commitments. Skip
|
|
460
|
+
small talk and noise. Lead with what matters; keep it tight.
|
|
461
|
+
|
|
462
|
+
Respond with STRICT JSON and NOTHING else:
|
|
463
|
+
{
|
|
464
|
+
"hasSignal": "true" or "false" (a STRING),
|
|
465
|
+
"headline": "<one-line summary; empty string when hasSignal is 'false'>",
|
|
466
|
+
"summary": "<a short markdown digest for the team; empty string when 'false'>",
|
|
467
|
+
"risks": "<risks/blockers as short markdown, or empty string>",
|
|
468
|
+
"nextActions": "<recommended next actions as short markdown, or empty string>"
|
|
469
|
+
}
|
|
470
|
+
Set hasSignal to "false" when there is no notable activity (no
|
|
471
|
+
messages, or only chatter).
|
|
472
|
+
|
|
473
|
+
Recent conversation (oldest first, JSON):
|
|
474
|
+
{{steps.read-comms.output.messages}}
|
|
475
|
+
|
|
476
|
+
# PER-ITEM FILTER (continue-semantics). When the digest found no signal,
|
|
477
|
+
# skip the rest of THIS client's body — no briefing row, no post. A
|
|
478
|
+
# `condition` node is a loop-body type; a `switch` is NOT (it would break
|
|
479
|
+
# the loop's contiguous-body chain), so the gate MUST be a `condition`.
|
|
480
|
+
- id: signal-gate
|
|
481
|
+
type: condition
|
|
482
|
+
position: { x: 0, y: 600 }
|
|
483
|
+
data:
|
|
484
|
+
label: Only continue when there's real signal
|
|
485
|
+
expression: "steps.digest.output.hasSignal === true"
|
|
486
|
+
|
|
487
|
+
# GOVERNED WRITE via an `output` node (returns `proposed`, never throws;
|
|
488
|
+
# `entity.create` is auto-approved). Durable, dated log row.
|
|
489
|
+
# `dedupeBy: briefing-key` makes a redelivered run reuse the same row
|
|
490
|
+
# instead of appending a duplicate for the same client + tick.
|
|
491
|
+
- id: write-briefing
|
|
492
|
+
type: output
|
|
493
|
+
position: { x: 0, y: 720 }
|
|
494
|
+
data:
|
|
495
|
+
label: Save the client briefing
|
|
496
|
+
outputType: entity_create
|
|
497
|
+
config:
|
|
498
|
+
profileSlug: client-briefing
|
|
499
|
+
title: "Briefing — {{loop.item.title}}"
|
|
500
|
+
dedupeBy: briefing-key
|
|
501
|
+
properties:
|
|
502
|
+
briefing-key: "{{loop.item.id}}::{{trigger.payload.scheduledAt}}"
|
|
503
|
+
briefing-client: "{{loop.item.id}}"
|
|
504
|
+
briefing-date: "{{trigger.payload.scheduledAt}}"
|
|
505
|
+
briefing-headline: "{{steps.digest.output.headline}}"
|
|
506
|
+
briefing-summary: "{{steps.digest.output.summary}}"
|
|
507
|
+
briefing-risks: "{{steps.digest.output.risks}}"
|
|
508
|
+
briefing-next-actions: "{{steps.digest.output.nextActions}}"
|
|
509
|
+
briefing-message-count: "{{steps.read-comms.output.count}}"
|
|
510
|
+
|
|
511
|
+
# GOVERNED WRITE via `output` node → `graph.link` (auto-executes:
|
|
512
|
+
# builtin approved + relation.create in DEFAULT_AUTO_APPROVE). Links the
|
|
513
|
+
# briefing to its client. The relation TYPE is declared in `entityLinks`
|
|
514
|
+
# above, so `relations.create` accepts it instead of "Unknown relation
|
|
515
|
+
# type". `dedupe` (default) skips a duplicate edge on re-run.
|
|
516
|
+
- id: link-briefing
|
|
517
|
+
type: output
|
|
518
|
+
position: { x: 0, y: 840 }
|
|
519
|
+
data:
|
|
520
|
+
label: Link briefing → client
|
|
521
|
+
outputType: relation_create
|
|
522
|
+
config:
|
|
523
|
+
fromEntityId: "{{steps.write-briefing.output.entityId}}"
|
|
524
|
+
toEntityId: "{{loop.item.id}}"
|
|
525
|
+
relationType: briefing_about_client
|
|
526
|
+
|
|
527
|
+
# READ-ONLY builtin. Resolves the client's TEAM-side channel by
|
|
528
|
+
# branchPurpose (the firewall ROLE), NOT the client-facing channel.
|
|
529
|
+
- id: resolve-team
|
|
530
|
+
type: capability
|
|
531
|
+
position: { x: 0, y: 960 }
|
|
532
|
+
data:
|
|
533
|
+
label: Resolve the client's TEAM channel (FIREWALL)
|
|
534
|
+
verbId: channel.resolve
|
|
535
|
+
inputMapping:
|
|
536
|
+
contextObjectType: entity
|
|
537
|
+
contextObjectId: "{{loop.item.id}}"
|
|
538
|
+
branchPurpose: team
|
|
539
|
+
|
|
540
|
+
# UNGOVERNED direct insert (firewall-checked mirror). Posts to the
|
|
541
|
+
# EXPLICIT resolved team channelId — NOT channelType:'subjectEntity',
|
|
542
|
+
# which needs a run subjectEntityId that CRON runs never carry.
|
|
543
|
+
- id: post-recap
|
|
544
|
+
type: output
|
|
545
|
+
position: { x: 0, y: 1080 }
|
|
546
|
+
data:
|
|
547
|
+
label: Post the recap to the TEAM channel (FIREWALL)
|
|
548
|
+
outputType: channel_message
|
|
549
|
+
config:
|
|
550
|
+
channelId: "{{steps.resolve-team.output.channelId}}"
|
|
551
|
+
content: >-
|
|
552
|
+
**{{loop.item.title}} — daily briefing**
|
|
553
|
+
|
|
554
|
+
{{steps.digest.output.headline}}
|
|
555
|
+
|
|
556
|
+
{{steps.digest.output.summary}}
|
|
557
|
+
|
|
558
|
+
**Risks:** {{steps.digest.output.risks}}
|
|
559
|
+
|
|
560
|
+
**Next:** {{steps.digest.output.nextActions}}
|
|
561
|
+
metadata:
|
|
562
|
+
automationMessage: true
|
|
563
|
+
kind: client-briefing
|
|
564
|
+
edges:
|
|
565
|
+
- { id: e-trigger-list, source: trigger, target: list-clients }
|
|
566
|
+
- { id: e-list-loop, source: list-clients, target: clients-loop }
|
|
567
|
+
- { id: e-loop-read, source: clients-loop, target: read-comms }
|
|
568
|
+
- { id: e-read-digest, source: read-comms, target: digest }
|
|
569
|
+
- { id: e-digest-gate, source: digest, target: signal-gate }
|
|
570
|
+
- { id: e-gate-write, source: signal-gate, target: write-briefing }
|
|
571
|
+
- { id: e-write-link, source: write-briefing, target: link-briefing }
|
|
572
|
+
- { id: e-link-resolve, source: link-briefing, target: resolve-team }
|
|
573
|
+
- { id: e-resolve-post, source: resolve-team, target: post-recap }
|
package/src/outreach-comms.yaml
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
meta:
|
|
10
10
|
slug: outreach-comms
|
|
11
|
+
domain: revenue
|
|
11
12
|
name: Outreach & Comms
|
|
12
13
|
description: Personalized LinkedIn and email outreach — sequenced touches against real people and companies, drafted and sent by an agent.
|
|
13
14
|
icon: send
|
|
@@ -189,7 +190,11 @@ playbooks:
|
|
|
189
190
|
- { name: target, type: entity, label: Target contact, required: true }
|
|
190
191
|
- { name: campaign, type: text, label: Campaign name, required: true }
|
|
191
192
|
- { name: channel, type: choice, label: Channel, defaultValue: linkedin }
|
|
192
|
-
- {
|
|
193
|
+
- {
|
|
194
|
+
name: campaignContext,
|
|
195
|
+
type: text,
|
|
196
|
+
label: Value prop + call to action,
|
|
197
|
+
}
|
|
193
198
|
executor: is-agent
|
|
194
199
|
grants:
|
|
195
200
|
- linkedin_send_invite
|