@synap-core/workspace-templates 0.9.4 → 0.10.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.d.ts +95 -11
- package/dist/index.js +2018 -467
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__fixtures__/default-relation-defs.json +208 -0
- package/src/__fixtures__/report-automation-flow.json +393 -0
- package/src/agent-fleet.yaml +6 -1
- package/src/base.template.test.ts +160 -0
- package/src/base.yaml +839 -0
- package/src/business-developer.template.test.ts +79 -10
- package/src/business-developer.yaml +61 -39
- package/src/concept-guard.test.ts +47 -31
- package/src/concept-guard.ts +23 -19
- package/src/content-os.yaml +22 -0
- package/src/corpus-flow-validity.test.ts +130 -0
- package/src/crm.yaml +59 -8
- package/src/define.primary-surface.test.ts +2 -4
- package/src/define.ts +28 -0
- package/src/ecosystem.yaml +14 -0
- package/src/enterprise-os.yaml +1 -1
- package/src/foundation.yaml +8 -0
- package/src/grants.yaml +8 -0
- package/src/life-os.yaml +6 -1
- package/src/operations.yaml +270 -0
- package/src/outreach-comms.yaml +196 -0
- package/src/project-management.yaml +6 -1
- package/src/templates.ts +1659 -373
- package/src/types.ts +66 -0
- package/src/validate-flow.ts +186 -7
- package/src/validate.ts +26 -2
package/src/base.yaml
ADDED
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
2
|
+
# base — universal operational defaults (slug: base, domain: core)
|
|
3
|
+
#
|
|
4
|
+
# GENERATED by scripts/gen-base (throwaway) from three backend seeders:
|
|
5
|
+
# - automations[0].flow ← REPORT_AUTOMATION_FLOW (ensure-report-automation.ts)
|
|
6
|
+
# - relationDefs[] ← DEFAULT_RELATION_DEFS (default-relation-defs.ts)
|
|
7
|
+
# - commands[] ← DEFAULT_COMMANDS (ensure-default-commands.ts)
|
|
8
|
+
#
|
|
9
|
+
# The flow is transcribed BYTE-FAITHFULLY — the hardened report prompts are the
|
|
10
|
+
# product. Its fidelity is pinned by base.report-automation.test.ts against
|
|
11
|
+
# __fixtures__/report-automation-flow.json. Do NOT hand-edit the flow here; edit
|
|
12
|
+
# the source of truth and regenerate, OR (post-Wave-4, once the backend const is
|
|
13
|
+
# deleted) edit here and update the fixture in the same commit.
|
|
14
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
15
|
+
meta:
|
|
16
|
+
slug: base
|
|
17
|
+
domain: core
|
|
18
|
+
name: Base
|
|
19
|
+
description: Universal operational defaults every workspace inherits — the report automation, the default relation vocabulary, and the default command pack.
|
|
20
|
+
icon: layers
|
|
21
|
+
color: "#6B7280"
|
|
22
|
+
tags:
|
|
23
|
+
- core
|
|
24
|
+
- defaults
|
|
25
|
+
- operational
|
|
26
|
+
isPublic: true
|
|
27
|
+
workspace:
|
|
28
|
+
name: Base
|
|
29
|
+
description: The pod-wide operational defaults — carried as config so one reconcile door seeds them into every workspace instead of three hardcoded seeders.
|
|
30
|
+
proposalId: base-v1
|
|
31
|
+
visibility: pod_visible
|
|
32
|
+
profiles: []
|
|
33
|
+
relationDefs:
|
|
34
|
+
- slug: assigned_to
|
|
35
|
+
displayName: Assigned To
|
|
36
|
+
description: Person assigned to task/project
|
|
37
|
+
isDirectional: true
|
|
38
|
+
uiHints:
|
|
39
|
+
category: workflow
|
|
40
|
+
- slug: blocks
|
|
41
|
+
displayName: Blocks
|
|
42
|
+
description: Prevents progress on another task
|
|
43
|
+
isDirectional: true
|
|
44
|
+
uiHints:
|
|
45
|
+
category: workflow
|
|
46
|
+
- slug: depends_on
|
|
47
|
+
displayName: Depends On
|
|
48
|
+
description: Requires completion of another task
|
|
49
|
+
isDirectional: true
|
|
50
|
+
uiHints:
|
|
51
|
+
category: workflow
|
|
52
|
+
- slug: relates_to
|
|
53
|
+
displayName: Relates To
|
|
54
|
+
description: General relationship between entities
|
|
55
|
+
isDirectional: false
|
|
56
|
+
uiHints:
|
|
57
|
+
category: reference
|
|
58
|
+
- slug: mentions
|
|
59
|
+
displayName: Mentions
|
|
60
|
+
description: Referenced in content
|
|
61
|
+
isDirectional: true
|
|
62
|
+
uiHints:
|
|
63
|
+
category: reference
|
|
64
|
+
- slug: links_to
|
|
65
|
+
displayName: Links To
|
|
66
|
+
description: Hyperlink or reference
|
|
67
|
+
isDirectional: true
|
|
68
|
+
uiHints:
|
|
69
|
+
category: reference
|
|
70
|
+
- slug: parent_of
|
|
71
|
+
displayName: Parent Of
|
|
72
|
+
description: Hierarchical parent relationship
|
|
73
|
+
isDirectional: true
|
|
74
|
+
uiHints:
|
|
75
|
+
category: hierarchy
|
|
76
|
+
- slug: tagged_with
|
|
77
|
+
displayName: Tagged With
|
|
78
|
+
description: Categorization tag
|
|
79
|
+
isDirectional: true
|
|
80
|
+
uiHints:
|
|
81
|
+
category: reference
|
|
82
|
+
- slug: created_by
|
|
83
|
+
displayName: Created By
|
|
84
|
+
description: Author or creator
|
|
85
|
+
isDirectional: true
|
|
86
|
+
uiHints:
|
|
87
|
+
category: social
|
|
88
|
+
- slug: attended_by
|
|
89
|
+
displayName: Attended By
|
|
90
|
+
description: Participant in event
|
|
91
|
+
isDirectional: true
|
|
92
|
+
uiHints:
|
|
93
|
+
category: social
|
|
94
|
+
- slug: belongs_to_project
|
|
95
|
+
displayName: Belongs To Project
|
|
96
|
+
description: Project membership
|
|
97
|
+
isDirectional: true
|
|
98
|
+
uiHints:
|
|
99
|
+
category: hierarchy
|
|
100
|
+
- slug: founder_brand_of
|
|
101
|
+
displayName: Founder Brand Of
|
|
102
|
+
description: A personal-brand project is the founder brand of a company project — links a person's brand to the company it expresses
|
|
103
|
+
isDirectional: true
|
|
104
|
+
uiHints:
|
|
105
|
+
category: hierarchy
|
|
106
|
+
inverseLabel: Expression of
|
|
107
|
+
- slug: references
|
|
108
|
+
displayName: References
|
|
109
|
+
description: Cites or refers to
|
|
110
|
+
isDirectional: true
|
|
111
|
+
uiHints:
|
|
112
|
+
category: reference
|
|
113
|
+
- slug: works_at
|
|
114
|
+
displayName: Works At
|
|
115
|
+
description: Person works at an organization
|
|
116
|
+
isDirectional: true
|
|
117
|
+
uiHints:
|
|
118
|
+
category: social
|
|
119
|
+
inverseLabel: Employs
|
|
120
|
+
- slug: deal_for
|
|
121
|
+
displayName: Deal For
|
|
122
|
+
description: Sales deal associated with a contact
|
|
123
|
+
isDirectional: true
|
|
124
|
+
uiHints:
|
|
125
|
+
category: workflow
|
|
126
|
+
inverseLabel: Has Deal
|
|
127
|
+
- slug: advances
|
|
128
|
+
displayName: Advances
|
|
129
|
+
description: advances the company's Foundation mission/vision goal
|
|
130
|
+
isDirectional: true
|
|
131
|
+
uiHints:
|
|
132
|
+
category: workflow
|
|
133
|
+
inverseLabel: Advanced by
|
|
134
|
+
- slug: met_at
|
|
135
|
+
displayName: Met at
|
|
136
|
+
description: Person met at an event or gathering
|
|
137
|
+
isDirectional: true
|
|
138
|
+
uiHints:
|
|
139
|
+
category: social
|
|
140
|
+
inverseLabel: Attendees
|
|
141
|
+
- slug: works_on
|
|
142
|
+
displayName: Works on
|
|
143
|
+
description: Person contributes to a project
|
|
144
|
+
isDirectional: true
|
|
145
|
+
uiHints:
|
|
146
|
+
category: workflow
|
|
147
|
+
inverseLabel: Contributors
|
|
148
|
+
- slug: has_skill
|
|
149
|
+
displayName: Has skill
|
|
150
|
+
description: Person possesses a skill or expertise
|
|
151
|
+
isDirectional: true
|
|
152
|
+
uiHints:
|
|
153
|
+
category: reference
|
|
154
|
+
inverseLabel: Skilled people
|
|
155
|
+
- slug: affiliated_with
|
|
156
|
+
displayName: Affiliated with
|
|
157
|
+
description: Person affiliated with an organization
|
|
158
|
+
isDirectional: true
|
|
159
|
+
uiHints:
|
|
160
|
+
category: social
|
|
161
|
+
inverseLabel: Members
|
|
162
|
+
- slug: knows
|
|
163
|
+
displayName: Knows
|
|
164
|
+
description: Two people know each other
|
|
165
|
+
isDirectional: false
|
|
166
|
+
uiHints:
|
|
167
|
+
category: social
|
|
168
|
+
- slug: discussed_with
|
|
169
|
+
displayName: Discussed with
|
|
170
|
+
description: Had a discussion or conversation with
|
|
171
|
+
isDirectional: false
|
|
172
|
+
uiHints:
|
|
173
|
+
category: social
|
|
174
|
+
commands:
|
|
175
|
+
- title: Create View from Description
|
|
176
|
+
promptTemplate: |-
|
|
177
|
+
You are helping the user create a new view from a natural language description.
|
|
178
|
+
|
|
179
|
+
**User's goal:** @{arg:goal}
|
|
180
|
+
|
|
181
|
+
Create a proposal for a new view that matches this description. Include:
|
|
182
|
+
- View type (table, kanban, list, etc.)
|
|
183
|
+
- Filters, sorts, and scope
|
|
184
|
+
- Layout configuration
|
|
185
|
+
|
|
186
|
+
Output your proposal clearly so it can be turned into a Synap view.
|
|
187
|
+
derivedInputs:
|
|
188
|
+
- name: goal
|
|
189
|
+
label: Goal
|
|
190
|
+
type: text
|
|
191
|
+
outputMode: view
|
|
192
|
+
canCreateViews: true
|
|
193
|
+
permissionsProfile: propose_writes
|
|
194
|
+
- title: Extract Entities from Text
|
|
195
|
+
promptTemplate: |-
|
|
196
|
+
You are helping the user extract entities and relationships from text.
|
|
197
|
+
|
|
198
|
+
**User's text or selection:**
|
|
199
|
+
|
|
200
|
+
@{context:text}
|
|
201
|
+
|
|
202
|
+
Analyze this content and propose:
|
|
203
|
+
1. New entities to create (Notes, Tasks, People, Companies, etc.) with their properties
|
|
204
|
+
2. Relationships between entities (blocks, related_to, parent_of, etc.)
|
|
205
|
+
|
|
206
|
+
Output proposals that the user can review and approve. Do not create anything directly—always propose.
|
|
207
|
+
derivedInputs: []
|
|
208
|
+
outputMode: proposal
|
|
209
|
+
canCreateViews: false
|
|
210
|
+
permissionsProfile: propose_writes
|
|
211
|
+
- title: Summarize Selection
|
|
212
|
+
promptTemplate: |-
|
|
213
|
+
You are helping the user get a concise summary of their selection.
|
|
214
|
+
|
|
215
|
+
**User's text or selection:**
|
|
216
|
+
|
|
217
|
+
@{context:text}
|
|
218
|
+
|
|
219
|
+
Provide a clear, structured summary. Highlight key points, dates, and actionable items if relevant. Keep it concise but comprehensive.
|
|
220
|
+
derivedInputs: []
|
|
221
|
+
outputMode: text
|
|
222
|
+
canCreateViews: false
|
|
223
|
+
permissionsProfile: read_only
|
|
224
|
+
- title: Analyze Entities
|
|
225
|
+
promptTemplate: |-
|
|
226
|
+
You are helping the user analyze selected entities.
|
|
227
|
+
|
|
228
|
+
**Current entity context:**
|
|
229
|
+
|
|
230
|
+
@{context:entity}
|
|
231
|
+
|
|
232
|
+
Analyze this entity and provide:
|
|
233
|
+
- Overview and key properties
|
|
234
|
+
- Insights, gaps, or opportunities
|
|
235
|
+
- Suggestions for relationships or next steps
|
|
236
|
+
|
|
237
|
+
Be concise and actionable.
|
|
238
|
+
derivedInputs: []
|
|
239
|
+
outputMode: text
|
|
240
|
+
canCreateViews: false
|
|
241
|
+
permissionsProfile: read_only
|
|
242
|
+
- title: Create Task from Text
|
|
243
|
+
promptTemplate: |-
|
|
244
|
+
You are helping the user turn text into tasks.
|
|
245
|
+
|
|
246
|
+
**User's text (meeting notes, pasted content, or selection):**
|
|
247
|
+
|
|
248
|
+
@{context:text}
|
|
249
|
+
|
|
250
|
+
Extract actionable tasks. For each task propose:
|
|
251
|
+
- Title
|
|
252
|
+
- Optional: due date, priority, assignee, status
|
|
253
|
+
- Link to source if applicable
|
|
254
|
+
|
|
255
|
+
Output proposals only—do not create entities directly. The user will review and approve.
|
|
256
|
+
derivedInputs: []
|
|
257
|
+
outputMode: proposal
|
|
258
|
+
canCreateViews: false
|
|
259
|
+
permissionsProfile: propose_writes
|
|
260
|
+
- title: Create Event from Text
|
|
261
|
+
promptTemplate: |-
|
|
262
|
+
You are helping the user turn text into events.
|
|
263
|
+
|
|
264
|
+
**User's text (meeting notes, calendar copy, or selection):**
|
|
265
|
+
|
|
266
|
+
@{context:text}
|
|
267
|
+
|
|
268
|
+
Extract events with dates/times. For each event propose:
|
|
269
|
+
- Title
|
|
270
|
+
- Start time and end time
|
|
271
|
+
- Optional: description, tags
|
|
272
|
+
|
|
273
|
+
Output proposals only—do not create entities directly. The user will review and approve.
|
|
274
|
+
derivedInputs: []
|
|
275
|
+
outputMode: proposal
|
|
276
|
+
canCreateViews: false
|
|
277
|
+
permissionsProfile: propose_writes
|
|
278
|
+
automations:
|
|
279
|
+
- name: Generate report
|
|
280
|
+
description: Gather this workspace's state, interpret it over three AI rounds, and write a report entity. Run it as-is for a general report, or pass a prompt (e.g. "focus on our clients") to steer what it is about.
|
|
281
|
+
triggerType: manual
|
|
282
|
+
triggerConfig: {}
|
|
283
|
+
status: active
|
|
284
|
+
flow:
|
|
285
|
+
nodes:
|
|
286
|
+
- id: trigger
|
|
287
|
+
type: trigger
|
|
288
|
+
position:
|
|
289
|
+
x: 0
|
|
290
|
+
"y": 200
|
|
291
|
+
data:
|
|
292
|
+
triggerType: manual
|
|
293
|
+
label: Run report
|
|
294
|
+
config: {}
|
|
295
|
+
- id: now
|
|
296
|
+
type: compute
|
|
297
|
+
position:
|
|
298
|
+
x: 200
|
|
299
|
+
"y": 200
|
|
300
|
+
data:
|
|
301
|
+
label: Timestamp
|
|
302
|
+
operation: now
|
|
303
|
+
- id: gather-tasks
|
|
304
|
+
type: query
|
|
305
|
+
position:
|
|
306
|
+
x: 420
|
|
307
|
+
"y": 20
|
|
308
|
+
data:
|
|
309
|
+
label: Tasks
|
|
310
|
+
profileSlug: task
|
|
311
|
+
filter: "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}"
|
|
312
|
+
orderBy: updatedAt
|
|
313
|
+
orderDir: desc
|
|
314
|
+
limit: 25
|
|
315
|
+
errorHandling:
|
|
316
|
+
continueOnError: true
|
|
317
|
+
- id: gather-notes
|
|
318
|
+
type: query
|
|
319
|
+
position:
|
|
320
|
+
x: 420
|
|
321
|
+
"y": 140
|
|
322
|
+
data:
|
|
323
|
+
label: Notes
|
|
324
|
+
profileSlug: note
|
|
325
|
+
filter: "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}"
|
|
326
|
+
orderBy: updatedAt
|
|
327
|
+
orderDir: desc
|
|
328
|
+
limit: 15
|
|
329
|
+
errorHandling:
|
|
330
|
+
continueOnError: true
|
|
331
|
+
- id: gather-people
|
|
332
|
+
type: query
|
|
333
|
+
position:
|
|
334
|
+
x: 420
|
|
335
|
+
"y": 260
|
|
336
|
+
data:
|
|
337
|
+
label: People
|
|
338
|
+
profileSlug: person
|
|
339
|
+
filter: "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}"
|
|
340
|
+
orderBy: updatedAt
|
|
341
|
+
orderDir: desc
|
|
342
|
+
limit: 15
|
|
343
|
+
errorHandling:
|
|
344
|
+
continueOnError: true
|
|
345
|
+
- id: gather-companies
|
|
346
|
+
type: query
|
|
347
|
+
position:
|
|
348
|
+
x: 420
|
|
349
|
+
"y": 380
|
|
350
|
+
data:
|
|
351
|
+
label: Companies
|
|
352
|
+
profileSlug: company
|
|
353
|
+
filter: "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}"
|
|
354
|
+
orderBy: updatedAt
|
|
355
|
+
orderDir: desc
|
|
356
|
+
limit: 15
|
|
357
|
+
errorHandling:
|
|
358
|
+
continueOnError: true
|
|
359
|
+
- id: project-tasks
|
|
360
|
+
type: transform
|
|
361
|
+
position:
|
|
362
|
+
x: 540
|
|
363
|
+
"y": 20
|
|
364
|
+
data:
|
|
365
|
+
label: Project tasks
|
|
366
|
+
expression: "{{steps.gather-tasks.output.entities}} | map: {{item.id}} · {{item.title}} · status={{item.properties.status}} · priority={{item.properties.priority}} · due={{item.properties.dueDate}} · updated={{item.updatedAt}}"
|
|
367
|
+
errorHandling:
|
|
368
|
+
continueOnError: true
|
|
369
|
+
- id: project-notes
|
|
370
|
+
type: transform
|
|
371
|
+
position:
|
|
372
|
+
x: 540
|
|
373
|
+
"y": 140
|
|
374
|
+
data:
|
|
375
|
+
label: Project notes
|
|
376
|
+
expression: "{{steps.gather-notes.output.entities}} | map: {{item.id}} · {{item.title}} · updated={{item.updatedAt}}"
|
|
377
|
+
errorHandling:
|
|
378
|
+
continueOnError: true
|
|
379
|
+
- id: project-people
|
|
380
|
+
type: transform
|
|
381
|
+
position:
|
|
382
|
+
x: 540
|
|
383
|
+
"y": 260
|
|
384
|
+
data:
|
|
385
|
+
label: Project people
|
|
386
|
+
expression: "{{steps.gather-people.output.entities}} | map: {{item.id}} · {{item.title}} · email={{item.properties.email}} · lastInteraction={{item.properties.lastInteractionAt}} · updated={{item.updatedAt}}"
|
|
387
|
+
errorHandling:
|
|
388
|
+
continueOnError: true
|
|
389
|
+
- id: project-companies
|
|
390
|
+
type: transform
|
|
391
|
+
position:
|
|
392
|
+
x: 540
|
|
393
|
+
"y": 380
|
|
394
|
+
data:
|
|
395
|
+
label: Project companies
|
|
396
|
+
expression: "{{steps.gather-companies.output.entities}} | map: {{item.id}} · {{item.title}} · industry={{item.properties.industry}} · location={{item.properties.location}} · updated={{item.updatedAt}}"
|
|
397
|
+
errorHandling:
|
|
398
|
+
continueOnError: true
|
|
399
|
+
- id: analyze
|
|
400
|
+
type: capability
|
|
401
|
+
position:
|
|
402
|
+
x: 660
|
|
403
|
+
"y": 200
|
|
404
|
+
data:
|
|
405
|
+
label: Analyze
|
|
406
|
+
verbId: ai.generate
|
|
407
|
+
verbKind: read
|
|
408
|
+
inputMapping:
|
|
409
|
+
system: |-
|
|
410
|
+
You are the ANALYST round of a Synap workspace report.
|
|
411
|
+
Read the gathered workspace data and say what it MEANS: volume and shape of work,
|
|
412
|
+
what is in flight versus stalled, what is well-covered versus thin.
|
|
413
|
+
Work from the FIELDS on each record, not just its name: status, priority and
|
|
414
|
+
due dates are what distinguish moving work from stalled work, and an unset
|
|
415
|
+
field is itself worth reporting. Listing or counting what exists is NOT a
|
|
416
|
+
finding — say what the state of it means.
|
|
417
|
+
DATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.
|
|
418
|
+
You are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {"error": "..."} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.
|
|
419
|
+
EVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write "25" — never "23", never "about two dozen", never "Ten". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.
|
|
420
|
+
NEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like "the steer lines are empty, so…" or "no focus was provided" or "based on the gathered data". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.
|
|
421
|
+
STEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.
|
|
422
|
+
LINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.
|
|
423
|
+
Output PLAIN PROSE — short paragraphs and bullets, with [[entity:…]] chips
|
|
424
|
+
inline wherever you name a record. No markdown headings, no ::: directive
|
|
425
|
+
blocks, no code fences. Under 300 words. Another round will format this.
|
|
426
|
+
prompt: |-
|
|
427
|
+
STEER:
|
|
428
|
+
- Prompt: {{trigger.payload.prompt}}
|
|
429
|
+
- Focus: {{trigger.payload.focus}}
|
|
430
|
+
- Project lens id: {{trigger.payload.projectId}}
|
|
431
|
+
|
|
432
|
+
COUNTS — the exact number of records gathered for each kind. These figures
|
|
433
|
+
are measured, not estimated, and they are the ONLY quantities available to
|
|
434
|
+
you. Each line also gives the cap the gather was limited to.
|
|
435
|
+
- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)
|
|
436
|
+
- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)
|
|
437
|
+
- People: {{steps.gather-people.output.count}} gathered (cap 15)
|
|
438
|
+
- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)
|
|
439
|
+
READING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of
|
|
440
|
+
that kind is listed below — the set is COMPLETE and you may describe it as
|
|
441
|
+
such. A count EQUAL to the cap means the gather stopped at the cap and more
|
|
442
|
+
records exist that you cannot see — describe that kind as "at least N" and
|
|
443
|
+
never as complete, and never claim something does not exist merely because
|
|
444
|
+
it is absent from the list.
|
|
445
|
+
|
|
446
|
+
Time window: only records updated on or after {{trigger.payload.since}}
|
|
447
|
+
|
|
448
|
+
GATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line
|
|
449
|
+
is `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that
|
|
450
|
+
record's real state. An EMPTY value means the field is not set on that
|
|
451
|
+
record — that is a fact you may report (an unset status or a missing due
|
|
452
|
+
date is a finding), and never a value to guess at. Recent does not mean
|
|
453
|
+
important.
|
|
454
|
+
Tasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}
|
|
455
|
+
Notes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}
|
|
456
|
+
People — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}
|
|
457
|
+
Companies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}
|
|
458
|
+
maxTokens: "700"
|
|
459
|
+
errorHandling:
|
|
460
|
+
continueOnError: true
|
|
461
|
+
- id: relate
|
|
462
|
+
type: capability
|
|
463
|
+
position:
|
|
464
|
+
x: 900
|
|
465
|
+
"y": 200
|
|
466
|
+
data:
|
|
467
|
+
label: Patterns
|
|
468
|
+
verbId: ai.generate
|
|
469
|
+
verbKind: read
|
|
470
|
+
inputMapping:
|
|
471
|
+
system: |-
|
|
472
|
+
You are the PATTERNS round of a Synap workspace report.
|
|
473
|
+
You receive the raw gathered data AND the analyst's read of it. Your job is what
|
|
474
|
+
the analyst did NOT do: surface RELATIONSHIPS and things worth attention —
|
|
475
|
+
clusters, repeated themes, people or companies that recur across kinds, gaps
|
|
476
|
+
where a kind is conspicuously empty, and anything that looks like it needs a
|
|
477
|
+
decision. Prefer three sharp observations over ten shallow ones.
|
|
478
|
+
YOUR SCOPE IS WHAT CONNECTS RECORDS, not what they individually are. Every
|
|
479
|
+
observation you write must involve TWO OR MORE records, or TWO OR MORE kinds:
|
|
480
|
+
a name that appears in both a task and a company, work clustered on one
|
|
481
|
+
person, a due date that lines up with an event, a kind that is empty while a
|
|
482
|
+
related kind is busy. Do NOT re-describe volume, status distribution or how
|
|
483
|
+
busy the workspace is — that is the analyst's section and repeating it makes
|
|
484
|
+
yours redundant. Do not summarise or restate the analyst's read.
|
|
485
|
+
NEVER RETURN AN EMPTY ANSWER. If you genuinely find no cross-kind pattern,
|
|
486
|
+
write one or two sentences naming what you looked for and did not find (for
|
|
487
|
+
example: no person recurs across the tasks and companies gathered). That is a
|
|
488
|
+
real finding and the reader needs it. Silence is not an available outcome.
|
|
489
|
+
DATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.
|
|
490
|
+
You are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {"error": "..."} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.
|
|
491
|
+
EVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write "25" — never "23", never "about two dozen", never "Ten". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.
|
|
492
|
+
NEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like "the steer lines are empty, so…" or "no focus was provided" or "based on the gathered data". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.
|
|
493
|
+
STEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.
|
|
494
|
+
LINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.
|
|
495
|
+
Output PLAIN PROSE, under 250 words, with [[entity:…]] chips inline. No
|
|
496
|
+
headings, no ::: directive blocks, no code fences.
|
|
497
|
+
prompt: |-
|
|
498
|
+
STEER:
|
|
499
|
+
- Prompt: {{trigger.payload.prompt}}
|
|
500
|
+
- Focus: {{trigger.payload.focus}}
|
|
501
|
+
- Project lens id: {{trigger.payload.projectId}}
|
|
502
|
+
|
|
503
|
+
COUNTS — the exact number of records gathered for each kind. These figures
|
|
504
|
+
are measured, not estimated, and they are the ONLY quantities available to
|
|
505
|
+
you. Each line also gives the cap the gather was limited to.
|
|
506
|
+
- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)
|
|
507
|
+
- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)
|
|
508
|
+
- People: {{steps.gather-people.output.count}} gathered (cap 15)
|
|
509
|
+
- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)
|
|
510
|
+
READING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of
|
|
511
|
+
that kind is listed below — the set is COMPLETE and you may describe it as
|
|
512
|
+
such. A count EQUAL to the cap means the gather stopped at the cap and more
|
|
513
|
+
records exist that you cannot see — describe that kind as "at least N" and
|
|
514
|
+
never as complete, and never claim something does not exist merely because
|
|
515
|
+
it is absent from the list.
|
|
516
|
+
|
|
517
|
+
Time window: only records updated on or after {{trigger.payload.since}}
|
|
518
|
+
|
|
519
|
+
GATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line
|
|
520
|
+
is `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that
|
|
521
|
+
record's real state. An EMPTY value means the field is not set on that
|
|
522
|
+
record — that is a fact you may report (an unset status or a missing due
|
|
523
|
+
date is a finding), and never a value to guess at. Recent does not mean
|
|
524
|
+
important.
|
|
525
|
+
Tasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}
|
|
526
|
+
Notes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}
|
|
527
|
+
People — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}
|
|
528
|
+
Companies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}
|
|
529
|
+
|
|
530
|
+
ANALYST'S READ — already written and already in the report. Do NOT
|
|
531
|
+
restate, summarise or re-analyse it. It is here so you do not repeat it:
|
|
532
|
+
{{steps.analyze.output}}
|
|
533
|
+
|
|
534
|
+
YOUR TASK — cross-kind patterns ONLY. Look for what connects the lists
|
|
535
|
+
above: a person who recurs in tasks and companies, work clustered on one
|
|
536
|
+
name, due dates that line up, a kind that is empty while a related kind
|
|
537
|
+
is busy, a company with people but no tasks. Write only observations
|
|
538
|
+
that span TWO OR MORE records or kinds. If there are none, say what you
|
|
539
|
+
checked and did not find — never answer with nothing.
|
|
540
|
+
maxTokens: "700"
|
|
541
|
+
errorHandling:
|
|
542
|
+
continueOnError: true
|
|
543
|
+
- id: assemble
|
|
544
|
+
type: capability
|
|
545
|
+
position:
|
|
546
|
+
x: 1140
|
|
547
|
+
"y": 200
|
|
548
|
+
data:
|
|
549
|
+
label: Assemble
|
|
550
|
+
verbId: ai.generate
|
|
551
|
+
verbKind: read
|
|
552
|
+
inputMapping:
|
|
553
|
+
system: |-
|
|
554
|
+
You are the ASSEMBLER round of a Synap workspace report. You compose the FINAL
|
|
555
|
+
document body in Synap-flavoured markdown. You add no new facts — you arrange,
|
|
556
|
+
title, and format what the earlier rounds produced.
|
|
557
|
+
|
|
558
|
+
NEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like "the steer lines are empty, so…" or "no focus was provided" or "based on the gathered data". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.
|
|
559
|
+
|
|
560
|
+
NUMBERS: you were given no COUNTS block, so you may not originate a quantity
|
|
561
|
+
at all. Reproduce a figure ONLY by copying one that already appears in the
|
|
562
|
+
round material, digit for digit. Never total, re-count, convert to words, or
|
|
563
|
+
put a number in the title or in a ## heading that is not already in the
|
|
564
|
+
material beneath it.
|
|
565
|
+
|
|
566
|
+
SYNAP MARKDOWN SYNTAX — these rules are absolute:
|
|
567
|
+
1. A section is a FOUR-COLON container that must be opened and closed:
|
|
568
|
+
::::synap-section{agent="analyst" round="analyze" confidence="0.8"}
|
|
569
|
+
...content...
|
|
570
|
+
::::
|
|
571
|
+
2. Leaf embeds use THREE colons and MUST BE CLOSED with their own ::: line.
|
|
572
|
+
There is NO self-closing form. An unclosed embed swallows every line that
|
|
573
|
+
follows it until the section ends, and that content is LOST. Always write
|
|
574
|
+
the closing ::: immediately after the opening line:
|
|
575
|
+
:::synap-cell{instanceId="..."}
|
|
576
|
+
:::
|
|
577
|
+
:::synap-view{viewId="..."}
|
|
578
|
+
:::
|
|
579
|
+
:::synap-entity{id="..."}
|
|
580
|
+
:::
|
|
581
|
+
3. The outer container must always have STRICTLY MORE colons than anything nested
|
|
582
|
+
inside it. Four outside, three inside. If you ever nest a four-colon block in a
|
|
583
|
+
four-colon block the section closes early and the rest of the report leaks out.
|
|
584
|
+
4. ATTRIBUTE VALUES MUST BE QUOTED. An UNQUOTED value ends at the first } it
|
|
585
|
+
contains, which silently truncates the attribute — so always quote. Use
|
|
586
|
+
DOUBLE quotes for a plain value, and SINGLE quotes when the value is itself
|
|
587
|
+
a JSON object, so the JSON's own double quotes survive:
|
|
588
|
+
:::synap-cell{cellKey="chart-pie" cellProps='{"profileSlug":"task","groupBy":"status"}'}
|
|
589
|
+
:::
|
|
590
|
+
A single-quoted value may contain both " and } — those are only special to
|
|
591
|
+
an unquoted value. Write the JSON compact, on one line, with no trailing
|
|
592
|
+
comma; malformed JSON renders the cell with empty config.
|
|
593
|
+
5. The ONLY section attributes that survive are: id, agent, round, skills,
|
|
594
|
+
confidence, stepRunId, nodeId, status. Anything else is silently dropped.
|
|
595
|
+
6. confidence is a 0-1 ratio written as a decimal string, e.g. confidence="0.8".
|
|
596
|
+
Never a percentage.
|
|
597
|
+
7. CHIPS ARE CONTENT — COPY THEM THROUGH VERBATIM. The round material you were
|
|
598
|
+
given already contains [[entity:<id>|<label>]] markers. Reproduce every one
|
|
599
|
+
EXACTLY as written: same brackets, same id, same label. Do NOT tidy them into
|
|
600
|
+
plain text, do NOT rewrite or re-case an id, do NOT convert them to markdown
|
|
601
|
+
links. You are told you add no new FACTS; a chip is not a fact, it is the
|
|
602
|
+
earlier round's finding made navigable, and stripping it removes the only
|
|
603
|
+
clickable thing in the report while leaving the prose looking fine.
|
|
604
|
+
You may add a chip ONLY by copying a marker that already appears in the
|
|
605
|
+
material. Never invent an id, and never use a kind word other than `entity` —
|
|
606
|
+
this report is READ-ONLY prose, and either mistake produces a dead chip.
|
|
607
|
+
8. NEVER EMIT :::synap-view, AND NEVER EMIT :::synap-cell WITH AN instanceId.
|
|
608
|
+
Both need an id you have not been given and cannot obtain, and a made-up
|
|
609
|
+
reference produces a permanent broken block in a document a human reads.
|
|
610
|
+
9. YOU MAY EMIT A CHART :::synap-cell — from this allowlist ONLY, configured
|
|
611
|
+
through cellProps. These cells fetch their own data at render time, so they
|
|
612
|
+
need no id, and they make the report a live surface instead of a text file:
|
|
613
|
+
- :::synap-cell{cellKey="stat-card" cellProps='{"profileSlug":"<kind>","aggregation":"count","timePeriod":"week","label":"<Kind> created this week"}'}
|
|
614
|
+
- :::synap-cell{cellKey="chart-pie" cellProps='{"profileSlug":"<kind>","groupBy":"<property>","label":"<the finding this shows>"}'}
|
|
615
|
+
- :::synap-cell{cellKey="chart-bar" cellProps='{"profileSlug":"<kind>","mode":"category","groupBy":"<property>","label":"<the finding this shows>"}'}
|
|
616
|
+
Rules, all mandatory:
|
|
617
|
+
(a) profileSlug must be one of the kinds gathered for THIS report: task,
|
|
618
|
+
note, person, company. A chart over any other kind is a claim this
|
|
619
|
+
report cannot back — the data was never read.
|
|
620
|
+
(b) groupBy / field must name a property that plausibly exists on that
|
|
621
|
+
kind. If you are not confident one does, use the stat-card line above
|
|
622
|
+
EXACTLY as written — profileSlug, aggregation, timePeriod AND a label
|
|
623
|
+
naming the window. Never drop timePeriod and never label a windowed
|
|
624
|
+
count with a bare kind name: the card counts records CREATED in that
|
|
625
|
+
window, so "Tasks" over a weekly count is a false statement about
|
|
626
|
+
the workspace.
|
|
627
|
+
(c) Close EVERY embed with its own ::: line, per rule 2. An unclosed embed
|
|
628
|
+
swallows the rest of the section.
|
|
629
|
+
(d) EDITORIAL RULE — embed to SHOW what a sentence cannot: a distribution,
|
|
630
|
+
a ratio, a count. Never to decorate. At most 2-3 in the whole report,
|
|
631
|
+
placed inside the section whose prose they support. A chart that merely
|
|
632
|
+
restates the sentence beside it is noise, and noise is worse than
|
|
633
|
+
nothing here. If no gathered kind has a distribution worth showing,
|
|
634
|
+
emit no cells at all — that is a correct report, not a lesser one.
|
|
635
|
+
(e) AT MOST ONE cell per section, and NEVER at the top of a section.
|
|
636
|
+
Place it immediately AFTER the sentence making the claim it evidences,
|
|
637
|
+
so the reader has the claim before the picture. A chart that opens a
|
|
638
|
+
section makes the reader decode a graphic to find out what the section
|
|
639
|
+
is about, which is the delay the ## heading and the claim sentence
|
|
640
|
+
exist to remove.
|
|
641
|
+
(f) The label must state the FINDING, not the dimension. Write
|
|
642
|
+
label:"Half the open tasks are blocked", never label:"Distribution",
|
|
643
|
+
"Status" or "Tasks by status". Always set label explicitly: with none,
|
|
644
|
+
a pie chart titles itself "Distribution" and a bar chart "Breakdown" —
|
|
645
|
+
words true of every such chart ever drawn, which tell the reader
|
|
646
|
+
nothing they cannot already see in the shapes.
|
|
647
|
+
(g) A CHART IS A LIVE VIEW, THE PROSE IS A SNAPSHOT. These cells run
|
|
648
|
+
their own query when the report is OPENED, which may be long after
|
|
649
|
+
this text was written — so a sentence quoting a count and a chart
|
|
650
|
+
measuring the same thing WILL eventually disagree, and the reader
|
|
651
|
+
cannot tell which is stale. Never write a specific number in a
|
|
652
|
+
sentence that sits beside a chart of the same data: let the chart
|
|
653
|
+
carry the number and let the sentence carry the claim
|
|
654
|
+
("most open tasks are stalled at review", not "12 of 19 are stalled").
|
|
655
|
+
And if the sentence already gives the number the chart would show,
|
|
656
|
+
DROP THE CHART — one of them is redundant, and the sentence is the one
|
|
657
|
+
that cannot go stale against itself.
|
|
658
|
+
Everything else this report needs is ::::synap-section containers plus
|
|
659
|
+
[[entity:…]] chips inline.
|
|
660
|
+
|
|
661
|
+
10. EVERY ::::synap-section MUST OPEN WITH A ## CLAIM HEADING — its first
|
|
662
|
+
line, before any prose. The heading states WHAT THE SECTION FOUND, not
|
|
663
|
+
what produced it: "## Half the open tasks are blocked", never
|
|
664
|
+
"## Analysis", "## Analyze round" or "## Findings". Under 10 words, no
|
|
665
|
+
trailing period, no markdown or [[entity:…]] chip inside the heading
|
|
666
|
+
itself — put the chip in the prose beneath it. Write exactly one ## per
|
|
667
|
+
section. This heading is the section's title everywhere it is read: the
|
|
668
|
+
slide deck sets it as the slide headline (with no heading it falls back
|
|
669
|
+
to the round and agent names, which the slide already shows underneath),
|
|
670
|
+
and the scrolling reader has no section titles at all without it. A
|
|
671
|
+
section whose heading merely names the round tells the reader nothing
|
|
672
|
+
they cannot already see.
|
|
673
|
+
|
|
674
|
+
STRUCTURE: one ::::synap-section per round, in order — analyze, then relate.
|
|
675
|
+
Open with a single "# " title line ABOVE the first section, and open each
|
|
676
|
+
section with its own ## claim heading per rule 10.
|
|
677
|
+
|
|
678
|
+
STEER HANDLING: if the STEER lines are empty, title the report generically
|
|
679
|
+
("Workspace report — <period>") and keep the rounds in their given order. If any
|
|
680
|
+
STEER line has text, name that subject in the title and lead each section with
|
|
681
|
+
the material that answers it. Never add facts the rounds did not give you.
|
|
682
|
+
|
|
683
|
+
MISSING ROUNDS: if a round's material looks like {"error": "..."} or is empty,
|
|
684
|
+
you MUST still emit its section, with status="failed", containing one visible
|
|
685
|
+
sentence naming what is missing. NEVER silently produce a shorter report — a
|
|
686
|
+
reader must be able to see that a round did not run. A failed section is NOT
|
|
687
|
+
exempt from rule 10: give it a ## heading that states what is missing
|
|
688
|
+
("## The patterns round produced nothing"), because it still occupies a slide
|
|
689
|
+
and still needs a headline.
|
|
690
|
+
|
|
691
|
+
WORKED EXAMPLE of correct output:
|
|
692
|
+
# Workspace report — July 2026
|
|
693
|
+
|
|
694
|
+
::::synap-section{agent="analyst" round="analyze" confidence="0.8"}
|
|
695
|
+
## Most open tasks are stalled at review
|
|
696
|
+
|
|
697
|
+
Open tasks are piling up untouched since the sprint opened, and the great
|
|
698
|
+
majority are sitting in one status — the oldest is
|
|
699
|
+
[[entity:3f2a91c4-7b10-4e55-9c02-8ad1f6e4b2d7|Migrate the billing worker]].
|
|
700
|
+
Notes are thin.
|
|
701
|
+
:::synap-cell{cellKey="chart-pie" cellProps='{"profileSlug":"task","groupBy":"status","label":"Review is where open tasks accumulate"}'}
|
|
702
|
+
:::
|
|
703
|
+
::::
|
|
704
|
+
|
|
705
|
+
::::synap-section{agent="analyst" round="relate" status="failed" confidence="0.2"}
|
|
706
|
+
## The patterns round produced nothing
|
|
707
|
+
|
|
708
|
+
This section is missing: the patterns round failed and produced no material.
|
|
709
|
+
::::
|
|
710
|
+
|
|
711
|
+
Be CONCISE — your entire output must fit in 2000 tokens. Emit ONLY the markdown
|
|
712
|
+
document. No preamble, no explanation, no code fences around the whole thing.
|
|
713
|
+
prompt: |-
|
|
714
|
+
STEER:
|
|
715
|
+
- Prompt: {{trigger.payload.prompt}}
|
|
716
|
+
- Focus: {{trigger.payload.focus}}
|
|
717
|
+
- Project lens id: {{trigger.payload.projectId}}
|
|
718
|
+
|
|
719
|
+
Report generated at: {{steps.now.output.result}}
|
|
720
|
+
|
|
721
|
+
ANALYZE ROUND MATERIAL:
|
|
722
|
+
{{steps.analyze.output}}
|
|
723
|
+
|
|
724
|
+
RELATE ROUND MATERIAL:
|
|
725
|
+
{{steps.relate.output}}
|
|
726
|
+
maxTokens: "2000"
|
|
727
|
+
- id: summarize
|
|
728
|
+
type: capability
|
|
729
|
+
position:
|
|
730
|
+
x: 1380
|
|
731
|
+
"y": 200
|
|
732
|
+
data:
|
|
733
|
+
label: One-line summary
|
|
734
|
+
verbId: ai.generate
|
|
735
|
+
verbKind: read
|
|
736
|
+
inputMapping:
|
|
737
|
+
system: |-
|
|
738
|
+
Write ONE plain sentence, under 200 characters, stating what this report found.
|
|
739
|
+
No markdown, no directives, no quotes, no leading label. Just the sentence.
|
|
740
|
+
If the material is an error or empty, write exactly: "Report generated with incomplete data."
|
|
741
|
+
prompt: |-
|
|
742
|
+
Material to summarize (may be an error object if the round failed):
|
|
743
|
+
{{steps.analyze.output}}
|
|
744
|
+
maxTokens: "500"
|
|
745
|
+
- id: guard-assembled
|
|
746
|
+
type: guard
|
|
747
|
+
position:
|
|
748
|
+
x: 1620
|
|
749
|
+
"y": 200
|
|
750
|
+
data:
|
|
751
|
+
label: Body assembled
|
|
752
|
+
checks:
|
|
753
|
+
- path: steps.assemble.output
|
|
754
|
+
minLength: 200
|
|
755
|
+
message: The assembler produced no usable body (under 200 characters) — refusing to write an empty report.
|
|
756
|
+
- path: steps.assemble.output.error
|
|
757
|
+
exists: false
|
|
758
|
+
message: The assembler round failed — refusing to write a report without a body.
|
|
759
|
+
- id: create-report
|
|
760
|
+
type: output
|
|
761
|
+
position:
|
|
762
|
+
x: 1860
|
|
763
|
+
"y": 200
|
|
764
|
+
data:
|
|
765
|
+
label: Create report
|
|
766
|
+
outputType: entity_create
|
|
767
|
+
config:
|
|
768
|
+
profileSlug: report
|
|
769
|
+
title: Workspace report — {{steps.now.output.result}}
|
|
770
|
+
properties:
|
|
771
|
+
reportPeriod: "{{steps.now.output.result}}"
|
|
772
|
+
generatedAt: "{{steps.now.output.result}}"
|
|
773
|
+
reportStatus: ready
|
|
774
|
+
summary: "{{steps.summarize.output}}"
|
|
775
|
+
reportSources:
|
|
776
|
+
- Tasks
|
|
777
|
+
- Notes
|
|
778
|
+
- People
|
|
779
|
+
- Companies
|
|
780
|
+
body: "{{steps.assemble.output}}"
|
|
781
|
+
systemData:
|
|
782
|
+
renderer:
|
|
783
|
+
kind: cell
|
|
784
|
+
cellKey: "{{trigger.payload.renderer}}"
|
|
785
|
+
edges:
|
|
786
|
+
- id: e-trigger-now
|
|
787
|
+
source: trigger
|
|
788
|
+
target: now
|
|
789
|
+
- id: e-now-tasks
|
|
790
|
+
source: now
|
|
791
|
+
target: gather-tasks
|
|
792
|
+
- id: e-now-notes
|
|
793
|
+
source: now
|
|
794
|
+
target: gather-notes
|
|
795
|
+
- id: e-now-people
|
|
796
|
+
source: now
|
|
797
|
+
target: gather-people
|
|
798
|
+
- id: e-now-companies
|
|
799
|
+
source: now
|
|
800
|
+
target: gather-companies
|
|
801
|
+
- id: e-tasks-project
|
|
802
|
+
source: gather-tasks
|
|
803
|
+
target: project-tasks
|
|
804
|
+
- id: e-notes-project
|
|
805
|
+
source: gather-notes
|
|
806
|
+
target: project-notes
|
|
807
|
+
- id: e-people-project
|
|
808
|
+
source: gather-people
|
|
809
|
+
target: project-people
|
|
810
|
+
- id: e-companies-project
|
|
811
|
+
source: gather-companies
|
|
812
|
+
target: project-companies
|
|
813
|
+
- id: e-tasks-analyze
|
|
814
|
+
source: project-tasks
|
|
815
|
+
target: analyze
|
|
816
|
+
- id: e-notes-analyze
|
|
817
|
+
source: project-notes
|
|
818
|
+
target: analyze
|
|
819
|
+
- id: e-people-analyze
|
|
820
|
+
source: project-people
|
|
821
|
+
target: analyze
|
|
822
|
+
- id: e-companies-analyze
|
|
823
|
+
source: project-companies
|
|
824
|
+
target: analyze
|
|
825
|
+
- id: e-analyze-relate
|
|
826
|
+
source: analyze
|
|
827
|
+
target: relate
|
|
828
|
+
- id: e-relate-assemble
|
|
829
|
+
source: relate
|
|
830
|
+
target: assemble
|
|
831
|
+
- id: e-assemble-summarize
|
|
832
|
+
source: assemble
|
|
833
|
+
target: summarize
|
|
834
|
+
- id: e-summarize-guard
|
|
835
|
+
source: summarize
|
|
836
|
+
target: guard-assembled
|
|
837
|
+
- id: e-guard-create
|
|
838
|
+
source: guard-assembled
|
|
839
|
+
target: create-report
|