@synap-core/workspace-templates 0.7.3 → 0.8.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.
- package/dist/index.js +881 -148
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/crm.yaml +16 -0
- package/src/enterprise-os.yaml +4 -0
- package/src/networking.yaml +597 -0
- package/src/operations.yaml +7 -3
- package/src/templates.ts +873 -148
- package/src/the-arch.yaml +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synap-core/workspace-templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@9.15.4",
|
|
6
6
|
"description": "Canonical workspace definitions — YAML source + typed exports for browser, backend, CLI, and marketplace",
|
package/src/crm.yaml
CHANGED
|
@@ -108,6 +108,11 @@ profiles:
|
|
|
108
108
|
description: An organisation being qualified or sold to.
|
|
109
109
|
scope: WORKSPACE
|
|
110
110
|
properties:
|
|
111
|
+
- slug: contextSummary
|
|
112
|
+
label: Context
|
|
113
|
+
valueType: string
|
|
114
|
+
inputType: textarea
|
|
115
|
+
placeholder: The AI-maintained one-paragraph summary of who this client is and where they stand.
|
|
111
116
|
- slug: website
|
|
112
117
|
label: Website
|
|
113
118
|
valueType: string
|
|
@@ -178,6 +183,17 @@ profiles:
|
|
|
178
183
|
- won
|
|
179
184
|
- lost
|
|
180
185
|
- inactive
|
|
186
|
+
- slug: commercialStage
|
|
187
|
+
label: Commercial stage
|
|
188
|
+
valueType: string
|
|
189
|
+
inputType: select
|
|
190
|
+
enumValues:
|
|
191
|
+
- draft
|
|
192
|
+
- proposed
|
|
193
|
+
- negotiating
|
|
194
|
+
- won
|
|
195
|
+
- lost
|
|
196
|
+
constraints: { defaultValue: draft }
|
|
181
197
|
- slug: valueType
|
|
182
198
|
label: Value type
|
|
183
199
|
valueType: string
|
package/src/enterprise-os.yaml
CHANGED
|
@@ -48,6 +48,10 @@ dependencies:
|
|
|
48
48
|
kind: workspace
|
|
49
49
|
relation: require
|
|
50
50
|
reason: "The pipeline — contacts, leads, clients, and deals."
|
|
51
|
+
- slug: business-developer
|
|
52
|
+
kind: workspace
|
|
53
|
+
relation: require
|
|
54
|
+
reason: "The lead-first sales motion (crm-lead/crm-client roles, commercial proposals, commission handoff) that the CRM app runs on. Composes onto the CRM workspace above — without it the CRM lens is provisioned with pod-wide lead/client only and the app's pipeline is empty."
|
|
51
55
|
- slug: content-os
|
|
52
56
|
kind: workspace
|
|
53
57
|
relation: require
|
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# Networking — the BD-middleman overlay on the CRM workspace.
|
|
2
|
+
# =========================================================================
|
|
3
|
+
# The Arch sits between clients: client A has a NEED, client B has a
|
|
4
|
+
# CAPABILITY (a `solution-provider` role), and The Arch brokers the INTRO and
|
|
5
|
+
# bills the match as a deal. This is an ADDITIVE overlay on the CRM workspace —
|
|
6
|
+
# it reuses CRM's `company`/`deal`/`client` vocabulary and layers three things:
|
|
7
|
+
#
|
|
8
|
+
# • need — KIND: the client's stated ask (the demand side).
|
|
9
|
+
# • intro — KIND: the brokerage artifact (the match + its fee).
|
|
10
|
+
# • solution-provider — FACET (role on company/person): the supply side.
|
|
11
|
+
#
|
|
12
|
+
# KIND vs FACET rationale (applied rigorously):
|
|
13
|
+
# - `need` is a KIND: it is a thing with its own independent life — a status
|
|
14
|
+
# that moves (open → matching → intro-made → satisfied), a requester, an
|
|
15
|
+
# evidence trail. It is NOT a hat a company wears; it is authored, tracked,
|
|
16
|
+
# and retired on its own timeline. So it is a primary kind, never a facet.
|
|
17
|
+
# - `intro` is a KIND for the same reason `deal` is: it is a relationship-thing
|
|
18
|
+
# (requester × provider × need × stage × fee) with its own life and its own
|
|
19
|
+
# board. It cannot be a facet of any single entity — it spans two companies.
|
|
20
|
+
# - `solution-provider` is a FACET (profileKind: role): "being a provider of
|
|
21
|
+
# X" is a HAT an existing company or person wears. The Arch must NOT spin up
|
|
22
|
+
# a second entity for a firm it already knows as a company/client — it
|
|
23
|
+
# resolves identity first, then attaches this role. It carries no independent
|
|
24
|
+
# identity, it cascades with the parent company, and graph edges link the
|
|
25
|
+
# PARENT company (introduced_provider), not the facet. Mirrors the web3 role
|
|
26
|
+
# facets in blockchain-ecosystem.yaml (market-maker / security-auditor / …).
|
|
27
|
+
#
|
|
28
|
+
# SHARED-ROLE SSOT: `solution-provider` is a networking-specific role with a
|
|
29
|
+
# single consumer (this template). Following the blockchain-ecosystem precedent
|
|
30
|
+
# — where market-maker / security-auditor / dev-shop are `scope: shared` roles
|
|
31
|
+
# OWNED by their domain template, not foundation — it is declared ONCE here as
|
|
32
|
+
# `scope: shared`, `profileKind: role`. foundation.yaml is the SSOT only for the
|
|
33
|
+
# CROSS-CUTTING roles that many templates reuse (client / partner / lead /
|
|
34
|
+
# churned / sponsor); a single-consumer domain role belongs with its domain, and
|
|
35
|
+
# a single declaration cannot drift, so it is NOT added to foundation and NOT
|
|
36
|
+
# re-declared anywhere else. (The shared-role-ssot tripwire polices rows (b)/(c)
|
|
37
|
+
# per profile slug and rule (d) applicableKinds — a lone consistent declaration
|
|
38
|
+
# passes all of them.)
|
|
39
|
+
|
|
40
|
+
meta:
|
|
41
|
+
slug: networking
|
|
42
|
+
domain: revenue
|
|
43
|
+
name: Networking
|
|
44
|
+
description: "The BD-middleman layer over your CRM — capture client needs, keep a roster of solution providers, and broker the intros that connect demand to supply."
|
|
45
|
+
icon: waypoints
|
|
46
|
+
color: "#6366F1"
|
|
47
|
+
tags: [networking, business-development, matchmaking, intros, middleman]
|
|
48
|
+
isPublic: true
|
|
49
|
+
|
|
50
|
+
# OVERLAY on CRM — composes additively onto the CRM workspace (no separate
|
|
51
|
+
# workspace is created); subtype MATCHES the compose base (`crm`, a no-op on the
|
|
52
|
+
# additive reconcile) and visibility is OMITTED so the base CRM keeps its own
|
|
53
|
+
# identity. `require foundation` (deps-first) guarantees the pod-wide role
|
|
54
|
+
# vocabulary CRM reuses is seeded before this overlay lands — mirroring
|
|
55
|
+
# grants.yaml's compose-base + require-foundation dependency shape.
|
|
56
|
+
dependencies:
|
|
57
|
+
- slug: crm
|
|
58
|
+
kind: workspace
|
|
59
|
+
relation: compose
|
|
60
|
+
reason: "Adds a needs → intros brokerage workflow onto your CRM workspace."
|
|
61
|
+
- slug: foundation
|
|
62
|
+
kind: workspace
|
|
63
|
+
relation: require
|
|
64
|
+
reason: "Provides the pod-wide client/partner role vocabulary the CRM base reuses."
|
|
65
|
+
|
|
66
|
+
workspace:
|
|
67
|
+
name: Networking
|
|
68
|
+
description: "A brokerage workspace layered on your CRM — client needs, a provider roster, and the intros that connect them."
|
|
69
|
+
proposalId: networking-v1
|
|
70
|
+
subtype: crm
|
|
71
|
+
|
|
72
|
+
profiles:
|
|
73
|
+
# ── Need (KIND) — the demand side: a client's stated ask ───────────────────
|
|
74
|
+
- slug: need
|
|
75
|
+
displayName: Need
|
|
76
|
+
icon: target
|
|
77
|
+
color: amber
|
|
78
|
+
description: A client's stated ask — the demand The Arch brokers a provider against.
|
|
79
|
+
scope: WORKSPACE
|
|
80
|
+
properties:
|
|
81
|
+
- slug: needStatus
|
|
82
|
+
label: Status
|
|
83
|
+
valueType: string
|
|
84
|
+
inputType: select
|
|
85
|
+
enumValues: [open, matching, intro-made, satisfied, dropped]
|
|
86
|
+
constraints: { defaultValue: open }
|
|
87
|
+
- slug: domain
|
|
88
|
+
label: Domain
|
|
89
|
+
valueType: string
|
|
90
|
+
inputType: text
|
|
91
|
+
placeholder: The area of the ask — e.g. market-making, audit, design.
|
|
92
|
+
- slug: urgency
|
|
93
|
+
label: Urgency
|
|
94
|
+
valueType: string
|
|
95
|
+
inputType: text
|
|
96
|
+
- slug: budgetRange
|
|
97
|
+
label: Budget Range
|
|
98
|
+
valueType: string
|
|
99
|
+
inputType: text
|
|
100
|
+
- slug: requesterCompanyId
|
|
101
|
+
label: Requester
|
|
102
|
+
valueType: entity_id
|
|
103
|
+
inputType: entity_select
|
|
104
|
+
targetProfileSlug: company
|
|
105
|
+
# The A↔B FIREWALL control. `confidential` (the default) keeps a need OUT
|
|
106
|
+
# of the match roster entirely — only `shareable` needs may be surfaced to
|
|
107
|
+
# a provider match. Defaulting to confidential means a need is private
|
|
108
|
+
# until a human explicitly opens it up.
|
|
109
|
+
- slug: confidentiality
|
|
110
|
+
label: Confidentiality
|
|
111
|
+
valueType: string
|
|
112
|
+
inputType: select
|
|
113
|
+
enumValues: [shareable, confidential]
|
|
114
|
+
constraints: { defaultValue: confidential }
|
|
115
|
+
- slug: evidenceQuote
|
|
116
|
+
label: Evidence Quote
|
|
117
|
+
valueType: string
|
|
118
|
+
inputType: textarea
|
|
119
|
+
placeholder: The client's own words that captured this need.
|
|
120
|
+
- slug: sourceMessageId
|
|
121
|
+
label: Source Message ID
|
|
122
|
+
valueType: string
|
|
123
|
+
inputType: text
|
|
124
|
+
- slug: notes
|
|
125
|
+
label: Notes
|
|
126
|
+
valueType: string
|
|
127
|
+
inputType: textarea
|
|
128
|
+
|
|
129
|
+
# ── Intro (KIND) — the brokerage artifact: the match + how it bills ────────
|
|
130
|
+
- slug: intro
|
|
131
|
+
displayName: Intro
|
|
132
|
+
icon: waypoints
|
|
133
|
+
color: violet
|
|
134
|
+
description: The middleman brokerage artifact — connects a requester to a provider for a need, and bills the match as a deal.
|
|
135
|
+
scope: WORKSPACE
|
|
136
|
+
properties:
|
|
137
|
+
- slug: introStage
|
|
138
|
+
label: Stage
|
|
139
|
+
valueType: string
|
|
140
|
+
inputType: select
|
|
141
|
+
enumValues: [identified, proposed, approved, introduced, in-discussion, signed, dead]
|
|
142
|
+
constraints: { defaultValue: identified }
|
|
143
|
+
- slug: requesterCompanyId
|
|
144
|
+
label: Requester
|
|
145
|
+
valueType: entity_id
|
|
146
|
+
inputType: entity_select
|
|
147
|
+
targetProfileSlug: company
|
|
148
|
+
- slug: providerCompanyId
|
|
149
|
+
label: Provider
|
|
150
|
+
valueType: entity_id
|
|
151
|
+
inputType: entity_select
|
|
152
|
+
targetProfileSlug: company
|
|
153
|
+
- slug: needId
|
|
154
|
+
label: Need
|
|
155
|
+
valueType: entity_id
|
|
156
|
+
inputType: entity_select
|
|
157
|
+
targetProfileSlug: need
|
|
158
|
+
# ── Fee vocabulary — REUSED VERBATIM from business-developer.yaml's `deal`
|
|
159
|
+
# commercial contract, so an intro bills exactly like a CRM deal does. ──
|
|
160
|
+
- slug: synapFeeModel
|
|
161
|
+
label: Synap fee model
|
|
162
|
+
valueType: string
|
|
163
|
+
inputType: select
|
|
164
|
+
enumValues: [fixed, success_fee]
|
|
165
|
+
- slug: synapSuccessFeeRate
|
|
166
|
+
label: Synap success fee rate
|
|
167
|
+
valueType: number
|
|
168
|
+
inputType: number
|
|
169
|
+
- slug: synapFixedFee
|
|
170
|
+
label: Synap fixed fee
|
|
171
|
+
valueType: number
|
|
172
|
+
inputType: number
|
|
173
|
+
- slug: expectedClientGain
|
|
174
|
+
label: Expected client gain
|
|
175
|
+
valueType: number
|
|
176
|
+
inputType: number
|
|
177
|
+
- slug: synapFeeAmount
|
|
178
|
+
label: Synap fee amount
|
|
179
|
+
valueType: number
|
|
180
|
+
inputType: number
|
|
181
|
+
- slug: sourcedByUserId
|
|
182
|
+
label: Sourced by user ID
|
|
183
|
+
valueType: string
|
|
184
|
+
inputType: text
|
|
185
|
+
- slug: commissionStatus
|
|
186
|
+
label: Commission status
|
|
187
|
+
valueType: string
|
|
188
|
+
inputType: select
|
|
189
|
+
enumValues: [estimated, earned, paid, requires_review, void]
|
|
190
|
+
- slug: introducedAt
|
|
191
|
+
label: Introduced at
|
|
192
|
+
valueType: date
|
|
193
|
+
inputType: date
|
|
194
|
+
- slug: outcome
|
|
195
|
+
label: Outcome
|
|
196
|
+
valueType: string
|
|
197
|
+
inputType: textarea
|
|
198
|
+
- slug: deadReason
|
|
199
|
+
label: Dead reason
|
|
200
|
+
valueType: string
|
|
201
|
+
inputType: textarea
|
|
202
|
+
|
|
203
|
+
# ── Solution Provider (FACET) — the supply side: a role on company/person ──
|
|
204
|
+
# `scope: shared`, `profileKind: role` — the ONE pod-wide row for "is a
|
|
205
|
+
# provider of some capability". Attached to the EXISTING company/person via
|
|
206
|
+
# FacetRepository; never a second entity. Mirrors blockchain-ecosystem's role
|
|
207
|
+
# facets. applicableKinds is required (rule (d) of the shared-role tripwire).
|
|
208
|
+
- slug: solution-provider
|
|
209
|
+
displayName: Solution Provider
|
|
210
|
+
icon: handshake
|
|
211
|
+
color: teal
|
|
212
|
+
description: A company or person that provides a capability The Arch can broker a client need against.
|
|
213
|
+
scope: shared
|
|
214
|
+
profileKind: role
|
|
215
|
+
applicableKinds:
|
|
216
|
+
- company
|
|
217
|
+
- person
|
|
218
|
+
properties:
|
|
219
|
+
- slug: status
|
|
220
|
+
label: Status
|
|
221
|
+
valueType: string
|
|
222
|
+
inputType: select
|
|
223
|
+
enumValues: [prospect, active, paused, churned]
|
|
224
|
+
- slug: domains
|
|
225
|
+
label: Domains
|
|
226
|
+
valueType: array
|
|
227
|
+
inputType: text
|
|
228
|
+
placeholder: The capability areas this provider covers.
|
|
229
|
+
- slug: capacity
|
|
230
|
+
label: Capacity
|
|
231
|
+
valueType: string
|
|
232
|
+
inputType: text
|
|
233
|
+
- slug: pricingNotes
|
|
234
|
+
label: Pricing Notes
|
|
235
|
+
valueType: string
|
|
236
|
+
inputType: textarea
|
|
237
|
+
- slug: geography
|
|
238
|
+
label: Geography
|
|
239
|
+
valueType: string
|
|
240
|
+
inputType: text
|
|
241
|
+
- slug: notes
|
|
242
|
+
label: Notes
|
|
243
|
+
valueType: string
|
|
244
|
+
inputType: textarea
|
|
245
|
+
|
|
246
|
+
# Relations. `company`/`deal` resolve from the CRM compose base (not redeclared);
|
|
247
|
+
# `need`/`intro` are this overlay's own kinds. The graph links the PARENT company
|
|
248
|
+
# on both sides of an intro — a facet is never a link endpoint.
|
|
249
|
+
entityLinks:
|
|
250
|
+
- sourceProfileSlug: need
|
|
251
|
+
targetProfileSlug: company
|
|
252
|
+
type: needed_by
|
|
253
|
+
label: Needed by
|
|
254
|
+
- sourceProfileSlug: intro
|
|
255
|
+
targetProfileSlug: need
|
|
256
|
+
type: resolves
|
|
257
|
+
label: Resolves
|
|
258
|
+
- sourceProfileSlug: intro
|
|
259
|
+
targetProfileSlug: company
|
|
260
|
+
type: introduced_requester
|
|
261
|
+
label: Requester
|
|
262
|
+
- sourceProfileSlug: intro
|
|
263
|
+
targetProfileSlug: company
|
|
264
|
+
type: introduced_provider
|
|
265
|
+
label: Provider
|
|
266
|
+
- sourceProfileSlug: intro
|
|
267
|
+
targetProfileSlug: deal
|
|
268
|
+
type: billed_as
|
|
269
|
+
label: Billed as
|
|
270
|
+
|
|
271
|
+
# Curated views — the two boards of the brokerage motion plus the provider roster.
|
|
272
|
+
views:
|
|
273
|
+
- name: Needs
|
|
274
|
+
slug: needs
|
|
275
|
+
type: kanban
|
|
276
|
+
scopeProfileSlug: need
|
|
277
|
+
description: Client needs on a board by status — open demand waiting for a match.
|
|
278
|
+
config:
|
|
279
|
+
groupByField: needStatus
|
|
280
|
+
- name: Intros
|
|
281
|
+
slug: intros
|
|
282
|
+
type: kanban
|
|
283
|
+
scopeProfileSlug: intro
|
|
284
|
+
description: Brokered intros on a board by stage — from identified match to signed.
|
|
285
|
+
config:
|
|
286
|
+
groupByField: introStage
|
|
287
|
+
- name: Providers
|
|
288
|
+
slug: providers
|
|
289
|
+
type: table
|
|
290
|
+
scopeProfileSlug: solution-provider
|
|
291
|
+
description: The solution-provider roster — companies wearing the provider role, by status.
|
|
292
|
+
config:
|
|
293
|
+
columns: [title, status, domains, geography, capacity]
|
|
294
|
+
|
|
295
|
+
# ============================================================================
|
|
296
|
+
# Intro Matcher automation — the daily demand↔supply match.
|
|
297
|
+
#
|
|
298
|
+
# WHAT: once a day, enumerate the SHAREABLE open needs, and for each one match
|
|
299
|
+
# it against the solution-provider roster with ONE batched AI call, propose a
|
|
300
|
+
# governed `intro`, and post a match brief to The Arch's INTERNAL team channel.
|
|
301
|
+
#
|
|
302
|
+
# A↔B FIREWALL (never bend):
|
|
303
|
+
# • Only `confidentiality: shareable` needs enter the roster — the query
|
|
304
|
+
# filters on it, so a confidential need is never even read into a match.
|
|
305
|
+
# • The brief posts to a `team`-purpose channel (branchPurpose: team) — The
|
|
306
|
+
# Arch's INTERNAL artifact channel — resolved from the REQUESTER COMPANY
|
|
307
|
+
# (client A, the need's owner), which owns a team channel bound at onboarding.
|
|
308
|
+
# That is The Arch's internal thread for client A, NOT a client-comms channel
|
|
309
|
+
# and NEVER client B's (the provider's) channel. It does not land in any
|
|
310
|
+
# client-facing channel. (It cannot resolve from the just-proposed intro
|
|
311
|
+
# entity: channel.resolve is a read-only FIND and the intro has no channel
|
|
312
|
+
# bound to it yet, so that returned null and the brief never posted.)
|
|
313
|
+
# • The matcher prompt forbids drafting anything for a client's own channel
|
|
314
|
+
# and forbids revealing one client's need to another client.
|
|
315
|
+
#
|
|
316
|
+
# GOVERNANCE: the two read builtins (`entity.query`, `channel.resolve`) and the
|
|
317
|
+
# `ai.generate` digest are READ-ONLY and auto-run; the single WRITE is an
|
|
318
|
+
# `output/entity_create` (returns `proposed`, never throws; `entity.create` is
|
|
319
|
+
# auto-approved for an owner/agent run) — mirrors operations.yaml's daily
|
|
320
|
+
# briefing exactly.
|
|
321
|
+
#
|
|
322
|
+
# NOTE on the query lens: `entity.query`'s DEFAULT lens already reads pod-wide ∪
|
|
323
|
+
# workspace, so needs (workspace-scoped) and providers (pod-wide shared role)
|
|
324
|
+
# both resolve WITHOUT a `scope: pod` opt-in. `need` is this template's own
|
|
325
|
+
# profile, so `assertKnownProfileSlug` cannot fail the first node.
|
|
326
|
+
# ============================================================================
|
|
327
|
+
automations:
|
|
328
|
+
- key: networking-intro-matcher
|
|
329
|
+
name: Intro Matcher
|
|
330
|
+
description: >-
|
|
331
|
+
Once a day, match each shareable open client need against the
|
|
332
|
+
solution-provider roster, propose an intro for the best fit, and post a
|
|
333
|
+
match brief to The Arch's internal team channel — never to a client's own
|
|
334
|
+
channel.
|
|
335
|
+
triggerType: cron
|
|
336
|
+
triggerConfig:
|
|
337
|
+
expression: "0 9 * * *"
|
|
338
|
+
status: active
|
|
339
|
+
metadata:
|
|
340
|
+
templateKey: networking-intro-matcher
|
|
341
|
+
flow:
|
|
342
|
+
nodes:
|
|
343
|
+
- id: trigger
|
|
344
|
+
type: trigger
|
|
345
|
+
position: { x: 0, y: 0 }
|
|
346
|
+
data:
|
|
347
|
+
label: Daily at 09:00
|
|
348
|
+
triggerType: cron
|
|
349
|
+
config:
|
|
350
|
+
expression: "0 9 * * *"
|
|
351
|
+
|
|
352
|
+
# READ-ONLY builtin. Enumerates ONLY shareable open needs — the firewall
|
|
353
|
+
# filter lives here, so a confidential need is never read into a match.
|
|
354
|
+
- id: list-needs
|
|
355
|
+
type: capability
|
|
356
|
+
position: { x: 0, y: 120 }
|
|
357
|
+
data:
|
|
358
|
+
label: List shareable open needs (FIREWALL filter)
|
|
359
|
+
verbId: entity.query
|
|
360
|
+
inputMapping:
|
|
361
|
+
profileSlug: need
|
|
362
|
+
filter:
|
|
363
|
+
needStatus: open
|
|
364
|
+
confidentiality: shareable
|
|
365
|
+
limit: "100"
|
|
366
|
+
|
|
367
|
+
# READ-ONLY builtin. The provider roster to match against — companies /
|
|
368
|
+
# people wearing the pod-wide `solution-provider` role.
|
|
369
|
+
- id: list-providers
|
|
370
|
+
type: capability
|
|
371
|
+
position: { x: 0, y: 240 }
|
|
372
|
+
data:
|
|
373
|
+
label: List the solution-provider roster
|
|
374
|
+
verbId: entity.query
|
|
375
|
+
inputMapping:
|
|
376
|
+
profileSlug: solution-provider
|
|
377
|
+
limit: "100"
|
|
378
|
+
|
|
379
|
+
- id: needs-loop
|
|
380
|
+
type: loop
|
|
381
|
+
position: { x: 0, y: 360 }
|
|
382
|
+
data:
|
|
383
|
+
label: For each shareable open need
|
|
384
|
+
iteratorExpression: steps.list-needs.output.entities
|
|
385
|
+
itemVariable: need
|
|
386
|
+
|
|
387
|
+
# READ-ONLY builtin (pure compute — auto-runs). ONE batched match call
|
|
388
|
+
# per need against the whole roster. Returns STRICT JSON naming the best
|
|
389
|
+
# provider (by entity id) or none.
|
|
390
|
+
- id: match
|
|
391
|
+
type: capability
|
|
392
|
+
position: { x: 0, y: 480 }
|
|
393
|
+
data:
|
|
394
|
+
label: Match this need to the roster → JSON
|
|
395
|
+
verbId: ai.generate
|
|
396
|
+
inputMapping:
|
|
397
|
+
json: "true"
|
|
398
|
+
maxTokens: "800"
|
|
399
|
+
system: >-
|
|
400
|
+
You are The Arch's business-development matchmaker. You connect a
|
|
401
|
+
CLIENT'S NEED to the single best SOLUTION PROVIDER from a roster.
|
|
402
|
+
|
|
403
|
+
FIREWALL (never break): your output is a brief for The Arch's
|
|
404
|
+
INTERNAL team channel ONLY. NEVER draft anything to be sent into a
|
|
405
|
+
client's own channel, and NEVER reveal one client's need to
|
|
406
|
+
another client. Do not name the requesting client in any text
|
|
407
|
+
intended for the provider.
|
|
408
|
+
prompt: >-
|
|
409
|
+
The client need:
|
|
410
|
+
title: {{loop.item.title}}
|
|
411
|
+
domain: {{loop.item.properties.domain}}
|
|
412
|
+
urgency: {{loop.item.properties.urgency}}
|
|
413
|
+
budget: {{loop.item.properties.budgetRange}}
|
|
414
|
+
detail: {{loop.item.properties.notes}}
|
|
415
|
+
|
|
416
|
+
The solution-provider roster (JSON — each has an id, title, and
|
|
417
|
+
properties incl. domains/geography/capacity):
|
|
418
|
+
{{steps.list-providers.output.entities}}
|
|
419
|
+
|
|
420
|
+
Pick the SINGLE best provider for this need, or none if nothing
|
|
421
|
+
fits well. Respond with STRICT JSON and NOTHING else:
|
|
422
|
+
{
|
|
423
|
+
"hasMatch": "true" or "false" (a STRING),
|
|
424
|
+
"providerEntityId": "<the matched provider's entity id, or empty string>",
|
|
425
|
+
"providerName": "<the matched provider's name, or empty string>",
|
|
426
|
+
"rationale": "<one or two sentences on why this provider fits, for the internal team>",
|
|
427
|
+
"brief": "<a short internal match brief for the team, markdown>"
|
|
428
|
+
}
|
|
429
|
+
Set hasMatch to "false" when no provider is a genuine fit.
|
|
430
|
+
|
|
431
|
+
# PER-ITEM gate (continue-semantics). Skip the rest of THIS need's body
|
|
432
|
+
# when no provider fit — no intro, no post. A `condition` (not `switch`)
|
|
433
|
+
# keeps the loop body a contiguous chain.
|
|
434
|
+
- id: match-gate
|
|
435
|
+
type: condition
|
|
436
|
+
position: { x: 0, y: 600 }
|
|
437
|
+
data:
|
|
438
|
+
label: Only continue when there is a real match
|
|
439
|
+
expression: "steps.match.output.hasMatch === true"
|
|
440
|
+
|
|
441
|
+
# GOVERNED WRITE via `output/entity_create` (returns `proposed`, never
|
|
442
|
+
# throws; `entity.create` is auto-approved for an owner/agent run).
|
|
443
|
+
# Proposes the intro linking this need's requester to the matched provider.
|
|
444
|
+
- id: create-intro
|
|
445
|
+
type: output
|
|
446
|
+
position: { x: 0, y: 720 }
|
|
447
|
+
data:
|
|
448
|
+
label: Propose the intro
|
|
449
|
+
outputType: entity_create
|
|
450
|
+
config:
|
|
451
|
+
profileSlug: intro
|
|
452
|
+
title: "Intro — {{loop.item.title}} → {{steps.match.output.providerName}}"
|
|
453
|
+
properties:
|
|
454
|
+
introStage: identified
|
|
455
|
+
needId: "{{loop.item.id}}"
|
|
456
|
+
requesterCompanyId: "{{loop.item.properties.requesterCompanyId}}"
|
|
457
|
+
providerCompanyId: "{{steps.match.output.providerEntityId}}"
|
|
458
|
+
|
|
459
|
+
# READ-ONLY builtin. Resolves the REQUESTER COMPANY's team-side channel
|
|
460
|
+
# (The Arch's internal artifact channel for that client) — NOT a company
|
|
461
|
+
# client-comms channel, NEVER the provider's. This is the firewall
|
|
462
|
+
# boundary: the brief posts internally, never client-facing.
|
|
463
|
+
- id: resolve-team
|
|
464
|
+
type: capability
|
|
465
|
+
position: { x: 0, y: 840 }
|
|
466
|
+
data:
|
|
467
|
+
label: Resolve The Arch's internal team channel for the requester (FIREWALL)
|
|
468
|
+
_note: >-
|
|
469
|
+
Resolves from the REQUESTER COMPANY, not the intro entity.
|
|
470
|
+
channel.resolve is a read-only FIND — it never creates. A
|
|
471
|
+
just-proposed `intro` has no channel bound to it, so resolving from
|
|
472
|
+
{{steps.create-intro.output.entityId}} returned channelId: null and
|
|
473
|
+
the brief silently never posted. The requester company is a stable
|
|
474
|
+
entity that already owns a `team`-purpose channel (bound at
|
|
475
|
+
onboarding), so we resolve from it instead. Firewall-safe: this is
|
|
476
|
+
The Arch's INTERNAL team thread for client A (the need's owner), NOT
|
|
477
|
+
client B's (the provider's) channel and NOT any client-comms channel.
|
|
478
|
+
verbId: channel.resolve
|
|
479
|
+
inputMapping:
|
|
480
|
+
contextObjectType: entity
|
|
481
|
+
contextObjectId: "{{loop.item.properties.requesterCompanyId}}"
|
|
482
|
+
branchPurpose: team
|
|
483
|
+
|
|
484
|
+
# UNGOVERNED direct insert to the EXPLICIT resolved internal channelId.
|
|
485
|
+
# Posts the match brief to The Arch's team channel ONLY.
|
|
486
|
+
- id: post-brief
|
|
487
|
+
type: output
|
|
488
|
+
position: { x: 0, y: 960 }
|
|
489
|
+
data:
|
|
490
|
+
label: Post the match brief to the INTERNAL team channel (FIREWALL)
|
|
491
|
+
outputType: channel_message
|
|
492
|
+
config:
|
|
493
|
+
channelId: "{{steps.resolve-team.output.channelId}}"
|
|
494
|
+
content: >-
|
|
495
|
+
**Match found — {{loop.item.title}} → {{steps.match.output.providerName}}**
|
|
496
|
+
|
|
497
|
+
{{steps.match.output.rationale}}
|
|
498
|
+
|
|
499
|
+
{{steps.match.output.brief}}
|
|
500
|
+
metadata:
|
|
501
|
+
automationMessage: true
|
|
502
|
+
kind: intro-match
|
|
503
|
+
edges:
|
|
504
|
+
- { id: e-trigger-needs, source: trigger, target: list-needs }
|
|
505
|
+
- { id: e-needs-providers, source: list-needs, target: list-providers }
|
|
506
|
+
- { id: e-providers-loop, source: list-providers, target: needs-loop }
|
|
507
|
+
- { id: e-loop-match, source: needs-loop, target: match }
|
|
508
|
+
- { id: e-match-gate, source: match, target: match-gate }
|
|
509
|
+
- { id: e-gate-intro, source: match-gate, target: create-intro }
|
|
510
|
+
- { id: e-intro-resolve, source: create-intro, target: resolve-team }
|
|
511
|
+
- { id: e-resolve-post, source: resolve-team, target: post-brief }
|
|
512
|
+
|
|
513
|
+
app:
|
|
514
|
+
kind: stored
|
|
515
|
+
cellKey: entity-detail
|
|
516
|
+
|
|
517
|
+
layout:
|
|
518
|
+
defaultView: Needs
|
|
519
|
+
defaultApp: null
|
|
520
|
+
pinnedApps: [dashboard, data]
|
|
521
|
+
sidebarItems:
|
|
522
|
+
- kind: view
|
|
523
|
+
viewName: Needs
|
|
524
|
+
label: Needs
|
|
525
|
+
icon: target
|
|
526
|
+
- kind: view
|
|
527
|
+
viewName: Intros
|
|
528
|
+
label: Intros
|
|
529
|
+
icon: waypoints
|
|
530
|
+
- kind: view
|
|
531
|
+
viewName: Providers
|
|
532
|
+
label: Providers
|
|
533
|
+
icon: handshake
|
|
534
|
+
|
|
535
|
+
bento:
|
|
536
|
+
viewName: Needs
|
|
537
|
+
blocks:
|
|
538
|
+
- kind: view
|
|
539
|
+
viewName: Needs
|
|
540
|
+
pos: { x: 0, y: 0, w: 6, h: 5 }
|
|
541
|
+
- kind: view
|
|
542
|
+
viewName: Intros
|
|
543
|
+
pos: { x: 6, y: 0, w: 6, h: 5 }
|
|
544
|
+
|
|
545
|
+
onboarding:
|
|
546
|
+
goal: >
|
|
547
|
+
Capture the two sides of the brokerage — the live client NEEDS and the
|
|
548
|
+
SOLUTION-PROVIDER roster — so the matcher has real demand and real supply to
|
|
549
|
+
connect, and every intro traces back to a stated need and a named provider.
|
|
550
|
+
framing: |
|
|
551
|
+
THE BD MIDDLEMAN: Act as a sharp business-development broker who lives
|
|
552
|
+
between clients. One client has a need; another has the capability to meet
|
|
553
|
+
it; your value is the introduction and the fee on the match. Push for the
|
|
554
|
+
concrete: whose need is this, how confidential is it, and which real provider
|
|
555
|
+
can serve it. Tag the provider role onto the REAL company you already know —
|
|
556
|
+
never a second entity for the same firm.
|
|
557
|
+
expertise:
|
|
558
|
+
starters:
|
|
559
|
+
- >
|
|
560
|
+
A need has an owner and a confidentiality. Default every need to
|
|
561
|
+
confidential — it only enters the match roster once a human marks it
|
|
562
|
+
shareable. Capture whose need it is (the requester company) and, where
|
|
563
|
+
you can, the client's own words as the evidence quote.
|
|
564
|
+
- >
|
|
565
|
+
The provider is a HAT, not a new entity. Attach the solution-provider
|
|
566
|
+
role to the real company/person you already track (match on domain /
|
|
567
|
+
name first) — a market of providers is a graph of roles on known
|
|
568
|
+
companies, not a parallel directory.
|
|
569
|
+
blindSpots:
|
|
570
|
+
- >
|
|
571
|
+
A need with no provider and no status is a wish, not a pipeline. Force
|
|
572
|
+
the status (open / matching / intro-made) and, once a match exists,
|
|
573
|
+
broker the intro rather than letting the need sit open.
|
|
574
|
+
- >
|
|
575
|
+
The firewall is the product. Never let one client's confidential need
|
|
576
|
+
leak to another client. If a need is sensitive, keep it confidential —
|
|
577
|
+
an intro is proposed to the internal team first, never straight to a
|
|
578
|
+
client's channel.
|
|
579
|
+
bar: >
|
|
580
|
+
A workspace with real client needs (each with a requester and a
|
|
581
|
+
confidentiality), a roster of solution providers tagged onto real
|
|
582
|
+
companies, and the first intros brokered between them — so a glance shows
|
|
583
|
+
what demand is open and which providers can serve it.
|
|
584
|
+
collect:
|
|
585
|
+
- profileSlug: need
|
|
586
|
+
what: The live client needs you could broker a provider against.
|
|
587
|
+
cardinality: several
|
|
588
|
+
keyFields: [needStatus, domain, requesterCompanyId, confidentiality]
|
|
589
|
+
- profileSlug: solution-provider
|
|
590
|
+
what: The solution providers you can connect clients to, tagged onto the real company.
|
|
591
|
+
cardinality: several
|
|
592
|
+
keyFields: [status, domains, geography]
|
|
593
|
+
openingQuestions:
|
|
594
|
+
- "Which client needs are open right now, and whose needs are they?"
|
|
595
|
+
- "Which of those needs are shareable, and which must stay confidential?"
|
|
596
|
+
- "Which solution providers can you connect clients to, and what do they cover?"
|
|
597
|
+
doneWhen: A few client needs captured with a requester and a confidentiality, a handful of solution providers tagged onto real companies, and at least one intro brokered between them.
|
package/src/operations.yaml
CHANGED
|
@@ -355,9 +355,13 @@ automations:
|
|
|
355
355
|
- key: operations-daily-client-briefing
|
|
356
356
|
name: Daily client briefing
|
|
357
357
|
description: >-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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.
|
|
361
365
|
triggerType: cron
|
|
362
366
|
triggerConfig:
|
|
363
367
|
expression: "0 8 * * *"
|