@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/src/templates.ts CHANGED
@@ -596,7 +596,7 @@ const DATA = {
596
596
  "displayName": "Person",
597
597
  "icon": "user",
598
598
  "color": "#0891B2",
599
- "scope": "WORKSPACE",
599
+ "entityScope": "pod",
600
600
  "description": "Team member or agent owner",
601
601
  "properties": [
602
602
  {
@@ -2734,268 +2734,957 @@ const DATA = {
2734
2734
  "doneWhen": "The live agents exist with their model/provider, the providers are captured with budgets, key skills are catalogued, and agents are linked to active projects."
2735
2735
  }
2736
2736
  },
2737
- "blockchain-ecosystem": {
2737
+ "base": {
2738
2738
  "meta": {
2739
- "slug": "blockchain-ecosystem",
2740
- "domain": "strategy",
2741
- "name": "Blockchain Ecosystem",
2742
- "description": "Web3 market actors — market-makers, auditors, dev shops, KOLs — and how they connect. Overlays the Ecosystem workspace.",
2743
- "icon": "boxes",
2744
- "color": "#8B5CF6",
2739
+ "slug": "base",
2740
+ "domain": "core",
2741
+ "name": "Base",
2742
+ "description": "Universal operational defaults every workspace inherits — the report automation, the default relation vocabulary, and the default command pack.",
2743
+ "icon": "layers",
2744
+ "color": "#6B7280",
2745
2745
  "tags": [
2746
- "blockchain",
2747
- "web3",
2748
- "ecosystem",
2749
- "market-makers"
2746
+ "core",
2747
+ "defaults",
2748
+ "operational"
2750
2749
  ],
2751
- "isPublic": false
2750
+ "isPublic": true
2752
2751
  },
2753
- "dependencies": [
2754
- {
2755
- "slug": "ecosystem",
2756
- "kind": "workspace",
2757
- "relation": "compose",
2758
- "reason": "Adds web3 market actors + relations onto your Ecosystem."
2759
- }
2760
- ],
2761
2752
  "workspace": {
2762
- "name": "Ecosystem",
2763
- "description": "Web3 market actors and the relationships between them, layered onto your Ecosystem.",
2764
- "subtype": "ecosystem"
2753
+ "name": "Base",
2754
+ "description": "The pod-wide operational defaults — carried as config so one reconcile door seeds them into every workspace instead of three hardcoded seeders.",
2755
+ "proposalId": "base-v1",
2756
+ "visibility": "pod_visible"
2765
2757
  },
2766
- "profiles": [
2758
+ "profiles": [],
2759
+ "relationDefs": [
2760
+ {
2761
+ "slug": "assigned_to",
2762
+ "displayName": "Assigned To",
2763
+ "description": "Person assigned to task/project",
2764
+ "isDirectional": true,
2765
+ "uiHints": {
2766
+ "category": "workflow"
2767
+ }
2768
+ },
2767
2769
  {
2768
- "slug": "market-maker",
2769
- "displayName": "Market Maker",
2770
- "icon": "candlestick-chart",
2771
- "color": "sky",
2772
- "description": "A liquidity provider — makes markets and provides depth for a token.",
2773
- "scope": "shared",
2774
- "profileKind": "role",
2775
- "applicableKinds": [
2776
- "company",
2777
- "person"
2778
- ],
2779
- "properties": [
2780
- {
2781
- "slug": "status",
2782
- "label": "Status",
2783
- "valueType": "string",
2784
- "inputType": "select",
2785
- "enumValues": [
2786
- "prospect",
2787
- "active",
2788
- "paused",
2789
- "churned"
2790
- ]
2791
- },
2792
- {
2793
- "slug": "exchanges",
2794
- "label": "Exchanges / Venues",
2795
- "valueType": "array",
2796
- "inputType": "text"
2797
- },
2798
- {
2799
- "slug": "engagementStartDate",
2800
- "label": "Engagement Start",
2801
- "valueType": "date",
2802
- "inputType": "date"
2803
- },
2804
- {
2805
- "slug": "notes",
2806
- "label": "Notes",
2807
- "valueType": "string",
2808
- "inputType": "textarea"
2809
- }
2810
- ]
2770
+ "slug": "blocks",
2771
+ "displayName": "Blocks",
2772
+ "description": "Prevents progress on another task",
2773
+ "isDirectional": true,
2774
+ "uiHints": {
2775
+ "category": "workflow"
2776
+ }
2811
2777
  },
2812
2778
  {
2813
- "slug": "security-auditor",
2814
- "displayName": "Security Auditor",
2815
- "icon": "shield-check",
2816
- "color": "red",
2817
- "description": "A security firm or individual that audits smart contracts and protocols.",
2818
- "scope": "shared",
2819
- "profileKind": "role",
2820
- "applicableKinds": [
2821
- "company",
2822
- "person"
2823
- ],
2824
- "properties": [
2825
- {
2826
- "slug": "status",
2827
- "label": "Status",
2828
- "valueType": "string",
2829
- "inputType": "select",
2830
- "enumValues": [
2831
- "prospect",
2832
- "active",
2833
- "paused",
2834
- "churned"
2835
- ]
2836
- },
2837
- {
2838
- "slug": "specialty",
2839
- "label": "Specialty",
2840
- "valueType": "string",
2841
- "inputType": "text"
2842
- },
2843
- {
2844
- "slug": "engagementStartDate",
2845
- "label": "Engagement Start",
2846
- "valueType": "date",
2847
- "inputType": "date"
2848
- },
2849
- {
2850
- "slug": "notes",
2851
- "label": "Notes",
2852
- "valueType": "string",
2853
- "inputType": "textarea"
2854
- }
2855
- ]
2779
+ "slug": "depends_on",
2780
+ "displayName": "Depends On",
2781
+ "description": "Requires completion of another task",
2782
+ "isDirectional": true,
2783
+ "uiHints": {
2784
+ "category": "workflow"
2785
+ }
2856
2786
  },
2857
2787
  {
2858
- "slug": "dev-shop",
2859
- "displayName": "Dev Shop",
2860
- "icon": "code",
2861
- "color": "violet",
2862
- "description": "An outsourced engineering studio or build partner.",
2863
- "scope": "shared",
2864
- "profileKind": "role",
2865
- "applicableKinds": [
2866
- "company",
2867
- "person"
2868
- ],
2869
- "properties": [
2870
- {
2871
- "slug": "status",
2872
- "label": "Status",
2873
- "valueType": "string",
2874
- "inputType": "select",
2875
- "enumValues": [
2876
- "prospect",
2877
- "active",
2878
- "paused",
2879
- "churned"
2880
- ]
2881
- },
2882
- {
2883
- "slug": "specialty",
2884
- "label": "Specialty",
2885
- "valueType": "string",
2886
- "inputType": "text"
2887
- },
2888
- {
2889
- "slug": "engagementStartDate",
2890
- "label": "Engagement Start",
2891
- "valueType": "date",
2892
- "inputType": "date"
2893
- },
2894
- {
2895
- "slug": "notes",
2896
- "label": "Notes",
2897
- "valueType": "string",
2898
- "inputType": "textarea"
2899
- }
2900
- ]
2788
+ "slug": "relates_to",
2789
+ "displayName": "Relates To",
2790
+ "description": "General relationship between entities",
2791
+ "isDirectional": false,
2792
+ "uiHints": {
2793
+ "category": "reference"
2794
+ }
2901
2795
  },
2902
2796
  {
2903
- "slug": "marketing-agency",
2904
- "displayName": "Marketing Agency",
2905
- "icon": "megaphone",
2906
- "color": "pink",
2907
- "description": "A brand, growth, or content marketing agency.",
2908
- "scope": "shared",
2909
- "profileKind": "role",
2910
- "applicableKinds": [
2911
- "company",
2912
- "person"
2913
- ],
2914
- "properties": [
2915
- {
2916
- "slug": "status",
2917
- "label": "Status",
2918
- "valueType": "string",
2919
- "inputType": "select",
2920
- "enumValues": [
2921
- "prospect",
2922
- "active",
2923
- "paused",
2924
- "churned"
2925
- ]
2926
- },
2927
- {
2928
- "slug": "specialty",
2929
- "label": "Specialty",
2930
- "valueType": "string",
2931
- "inputType": "text"
2932
- },
2933
- {
2934
- "slug": "engagementStartDate",
2935
- "label": "Engagement Start",
2936
- "valueType": "date",
2937
- "inputType": "date"
2938
- },
2939
- {
2940
- "slug": "notes",
2941
- "label": "Notes",
2942
- "valueType": "string",
2943
- "inputType": "textarea"
2944
- }
2945
- ]
2797
+ "slug": "mentions",
2798
+ "displayName": "Mentions",
2799
+ "description": "Referenced in content",
2800
+ "isDirectional": true,
2801
+ "uiHints": {
2802
+ "category": "reference"
2803
+ }
2946
2804
  },
2947
2805
  {
2948
- "slug": "paid-ads-agency",
2949
- "displayName": "Paid-Ads Agency",
2950
- "icon": "badge-dollar-sign",
2951
- "color": "orange",
2952
- "description": "A performance / paid-media buying agency.",
2953
- "scope": "shared",
2954
- "profileKind": "role",
2955
- "applicableKinds": [
2956
- "company",
2957
- "person"
2958
- ],
2959
- "properties": [
2960
- {
2961
- "slug": "status",
2962
- "label": "Status",
2963
- "valueType": "string",
2964
- "inputType": "select",
2965
- "enumValues": [
2966
- "prospect",
2967
- "active",
2968
- "paused",
2969
- "churned"
2970
- ]
2971
- },
2972
- {
2973
- "slug": "channels",
2974
- "label": "Channels",
2975
- "valueType": "array",
2976
- "inputType": "text"
2977
- },
2978
- {
2979
- "slug": "engagementStartDate",
2980
- "label": "Engagement Start",
2981
- "valueType": "date",
2982
- "inputType": "date"
2983
- },
2984
- {
2985
- "slug": "notes",
2986
- "label": "Notes",
2987
- "valueType": "string",
2988
- "inputType": "textarea"
2989
- }
2990
- ]
2806
+ "slug": "links_to",
2807
+ "displayName": "Links To",
2808
+ "description": "Hyperlink or reference",
2809
+ "isDirectional": true,
2810
+ "uiHints": {
2811
+ "category": "reference"
2812
+ }
2991
2813
  },
2992
2814
  {
2993
- "slug": "kol",
2994
- "displayName": "KOL",
2995
- "icon": "star",
2996
- "color": "yellow",
2997
- "description": "A key opinion leader or influencer who amplifies reach.",
2998
- "scope": "shared",
2815
+ "slug": "parent_of",
2816
+ "displayName": "Parent Of",
2817
+ "description": "Hierarchical parent relationship",
2818
+ "isDirectional": true,
2819
+ "uiHints": {
2820
+ "category": "hierarchy"
2821
+ }
2822
+ },
2823
+ {
2824
+ "slug": "tagged_with",
2825
+ "displayName": "Tagged With",
2826
+ "description": "Categorization tag",
2827
+ "isDirectional": true,
2828
+ "uiHints": {
2829
+ "category": "reference"
2830
+ }
2831
+ },
2832
+ {
2833
+ "slug": "created_by",
2834
+ "displayName": "Created By",
2835
+ "description": "Author or creator",
2836
+ "isDirectional": true,
2837
+ "uiHints": {
2838
+ "category": "social"
2839
+ }
2840
+ },
2841
+ {
2842
+ "slug": "attended_by",
2843
+ "displayName": "Attended By",
2844
+ "description": "Participant in event",
2845
+ "isDirectional": true,
2846
+ "uiHints": {
2847
+ "category": "social"
2848
+ }
2849
+ },
2850
+ {
2851
+ "slug": "belongs_to_project",
2852
+ "displayName": "Belongs To Project",
2853
+ "description": "Project membership",
2854
+ "isDirectional": true,
2855
+ "uiHints": {
2856
+ "category": "hierarchy"
2857
+ }
2858
+ },
2859
+ {
2860
+ "slug": "founder_brand_of",
2861
+ "displayName": "Founder Brand Of",
2862
+ "description": "A personal-brand project is the founder brand of a company project — links a person's brand to the company it expresses",
2863
+ "isDirectional": true,
2864
+ "uiHints": {
2865
+ "category": "hierarchy",
2866
+ "inverseLabel": "Expression of"
2867
+ }
2868
+ },
2869
+ {
2870
+ "slug": "references",
2871
+ "displayName": "References",
2872
+ "description": "Cites or refers to",
2873
+ "isDirectional": true,
2874
+ "uiHints": {
2875
+ "category": "reference"
2876
+ }
2877
+ },
2878
+ {
2879
+ "slug": "works_at",
2880
+ "displayName": "Works At",
2881
+ "description": "Person works at an organization",
2882
+ "isDirectional": true,
2883
+ "uiHints": {
2884
+ "category": "social",
2885
+ "inverseLabel": "Employs"
2886
+ }
2887
+ },
2888
+ {
2889
+ "slug": "deal_for",
2890
+ "displayName": "Deal For",
2891
+ "description": "Sales deal associated with a contact",
2892
+ "isDirectional": true,
2893
+ "uiHints": {
2894
+ "category": "workflow",
2895
+ "inverseLabel": "Has Deal"
2896
+ }
2897
+ },
2898
+ {
2899
+ "slug": "advances",
2900
+ "displayName": "Advances",
2901
+ "description": "advances the company's Foundation mission/vision goal",
2902
+ "isDirectional": true,
2903
+ "uiHints": {
2904
+ "category": "workflow",
2905
+ "inverseLabel": "Advanced by"
2906
+ }
2907
+ },
2908
+ {
2909
+ "slug": "met_at",
2910
+ "displayName": "Met at",
2911
+ "description": "Person met at an event or gathering",
2912
+ "isDirectional": true,
2913
+ "uiHints": {
2914
+ "category": "social",
2915
+ "inverseLabel": "Attendees"
2916
+ }
2917
+ },
2918
+ {
2919
+ "slug": "works_on",
2920
+ "displayName": "Works on",
2921
+ "description": "Person contributes to a project",
2922
+ "isDirectional": true,
2923
+ "uiHints": {
2924
+ "category": "workflow",
2925
+ "inverseLabel": "Contributors"
2926
+ }
2927
+ },
2928
+ {
2929
+ "slug": "has_skill",
2930
+ "displayName": "Has skill",
2931
+ "description": "Person possesses a skill or expertise",
2932
+ "isDirectional": true,
2933
+ "uiHints": {
2934
+ "category": "reference",
2935
+ "inverseLabel": "Skilled people"
2936
+ }
2937
+ },
2938
+ {
2939
+ "slug": "affiliated_with",
2940
+ "displayName": "Affiliated with",
2941
+ "description": "Person affiliated with an organization",
2942
+ "isDirectional": true,
2943
+ "uiHints": {
2944
+ "category": "social",
2945
+ "inverseLabel": "Members"
2946
+ }
2947
+ },
2948
+ {
2949
+ "slug": "knows",
2950
+ "displayName": "Knows",
2951
+ "description": "Two people know each other",
2952
+ "isDirectional": false,
2953
+ "uiHints": {
2954
+ "category": "social"
2955
+ }
2956
+ },
2957
+ {
2958
+ "slug": "discussed_with",
2959
+ "displayName": "Discussed with",
2960
+ "description": "Had a discussion or conversation with",
2961
+ "isDirectional": false,
2962
+ "uiHints": {
2963
+ "category": "social"
2964
+ }
2965
+ }
2966
+ ],
2967
+ "commands": [
2968
+ {
2969
+ "title": "Create View from Description",
2970
+ "promptTemplate": "You are helping the user create a new view from a natural language description.\n\n**User's goal:** @{arg:goal}\n\nCreate a proposal for a new view that matches this description. Include:\n- View type (table, kanban, list, etc.)\n- Filters, sorts, and scope\n- Layout configuration\n\nOutput your proposal clearly so it can be turned into a Synap view.",
2971
+ "derivedInputs": [
2972
+ {
2973
+ "name": "goal",
2974
+ "label": "Goal",
2975
+ "type": "text"
2976
+ }
2977
+ ],
2978
+ "outputMode": "view",
2979
+ "canCreateViews": true,
2980
+ "permissionsProfile": "propose_writes"
2981
+ },
2982
+ {
2983
+ "title": "Extract Entities from Text",
2984
+ "promptTemplate": "You are helping the user extract entities and relationships from text.\n\n**User's text or selection:**\n\n@{context:text}\n\nAnalyze this content and propose:\n1. New entities to create (Notes, Tasks, People, Companies, etc.) with their properties\n2. Relationships between entities (blocks, related_to, parent_of, etc.)\n\nOutput proposals that the user can review and approve. Do not create anything directly—always propose.",
2985
+ "derivedInputs": [],
2986
+ "outputMode": "proposal",
2987
+ "canCreateViews": false,
2988
+ "permissionsProfile": "propose_writes"
2989
+ },
2990
+ {
2991
+ "title": "Summarize Selection",
2992
+ "promptTemplate": "You are helping the user get a concise summary of their selection.\n\n**User's text or selection:**\n\n@{context:text}\n\nProvide a clear, structured summary. Highlight key points, dates, and actionable items if relevant. Keep it concise but comprehensive.",
2993
+ "derivedInputs": [],
2994
+ "outputMode": "text",
2995
+ "canCreateViews": false,
2996
+ "permissionsProfile": "read_only"
2997
+ },
2998
+ {
2999
+ "title": "Analyze Entities",
3000
+ "promptTemplate": "You are helping the user analyze selected entities.\n\n**Current entity context:**\n\n@{context:entity}\n\nAnalyze this entity and provide:\n- Overview and key properties\n- Insights, gaps, or opportunities\n- Suggestions for relationships or next steps\n\nBe concise and actionable.",
3001
+ "derivedInputs": [],
3002
+ "outputMode": "text",
3003
+ "canCreateViews": false,
3004
+ "permissionsProfile": "read_only"
3005
+ },
3006
+ {
3007
+ "title": "Create Task from Text",
3008
+ "promptTemplate": "You are helping the user turn text into tasks.\n\n**User's text (meeting notes, pasted content, or selection):**\n\n@{context:text}\n\nExtract actionable tasks. For each task propose:\n- Title\n- Optional: due date, priority, assignee, status\n- Link to source if applicable\n\nOutput proposals only—do not create entities directly. The user will review and approve.",
3009
+ "derivedInputs": [],
3010
+ "outputMode": "proposal",
3011
+ "canCreateViews": false,
3012
+ "permissionsProfile": "propose_writes"
3013
+ },
3014
+ {
3015
+ "title": "Create Event from Text",
3016
+ "promptTemplate": "You are helping the user turn text into events.\n\n**User's text (meeting notes, calendar copy, or selection):**\n\n@{context:text}\n\nExtract events with dates/times. For each event propose:\n- Title\n- Start time and end time\n- Optional: description, tags\n\nOutput proposals only—do not create entities directly. The user will review and approve.",
3017
+ "derivedInputs": [],
3018
+ "outputMode": "proposal",
3019
+ "canCreateViews": false,
3020
+ "permissionsProfile": "propose_writes"
3021
+ }
3022
+ ],
3023
+ "automations": [
3024
+ {
3025
+ "name": "Generate report",
3026
+ "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.",
3027
+ "triggerType": "manual",
3028
+ "triggerConfig": {},
3029
+ "status": "active",
3030
+ "flow": {
3031
+ "nodes": [
3032
+ {
3033
+ "id": "trigger",
3034
+ "type": "trigger",
3035
+ "position": {
3036
+ "x": 0,
3037
+ "y": 200
3038
+ },
3039
+ "data": {
3040
+ "triggerType": "manual",
3041
+ "label": "Run report",
3042
+ "config": {}
3043
+ }
3044
+ },
3045
+ {
3046
+ "id": "now",
3047
+ "type": "compute",
3048
+ "position": {
3049
+ "x": 200,
3050
+ "y": 200
3051
+ },
3052
+ "data": {
3053
+ "label": "Timestamp",
3054
+ "operation": "now"
3055
+ }
3056
+ },
3057
+ {
3058
+ "id": "gather-tasks",
3059
+ "type": "query",
3060
+ "position": {
3061
+ "x": 420,
3062
+ "y": 20
3063
+ },
3064
+ "data": {
3065
+ "label": "Tasks",
3066
+ "profileSlug": "task",
3067
+ "filter": "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}",
3068
+ "orderBy": "updatedAt",
3069
+ "orderDir": "desc",
3070
+ "limit": 25,
3071
+ "errorHandling": {
3072
+ "continueOnError": true
3073
+ }
3074
+ }
3075
+ },
3076
+ {
3077
+ "id": "gather-notes",
3078
+ "type": "query",
3079
+ "position": {
3080
+ "x": 420,
3081
+ "y": 140
3082
+ },
3083
+ "data": {
3084
+ "label": "Notes",
3085
+ "profileSlug": "note",
3086
+ "filter": "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}",
3087
+ "orderBy": "updatedAt",
3088
+ "orderDir": "desc",
3089
+ "limit": 15,
3090
+ "errorHandling": {
3091
+ "continueOnError": true
3092
+ }
3093
+ }
3094
+ },
3095
+ {
3096
+ "id": "gather-people",
3097
+ "type": "query",
3098
+ "position": {
3099
+ "x": 420,
3100
+ "y": 260
3101
+ },
3102
+ "data": {
3103
+ "label": "People",
3104
+ "profileSlug": "person",
3105
+ "filter": "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}",
3106
+ "orderBy": "updatedAt",
3107
+ "orderDir": "desc",
3108
+ "limit": 15,
3109
+ "errorHandling": {
3110
+ "continueOnError": true
3111
+ }
3112
+ }
3113
+ },
3114
+ {
3115
+ "id": "gather-companies",
3116
+ "type": "query",
3117
+ "position": {
3118
+ "x": 420,
3119
+ "y": 380
3120
+ },
3121
+ "data": {
3122
+ "label": "Companies",
3123
+ "profileSlug": "company",
3124
+ "filter": "{\"updatedAt\": {\"$gte\": \"{{trigger.payload.since}}\"}}",
3125
+ "orderBy": "updatedAt",
3126
+ "orderDir": "desc",
3127
+ "limit": 15,
3128
+ "errorHandling": {
3129
+ "continueOnError": true
3130
+ }
3131
+ }
3132
+ },
3133
+ {
3134
+ "id": "project-tasks",
3135
+ "type": "transform",
3136
+ "position": {
3137
+ "x": 540,
3138
+ "y": 20
3139
+ },
3140
+ "data": {
3141
+ "label": "Project tasks",
3142
+ "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}}",
3143
+ "errorHandling": {
3144
+ "continueOnError": true
3145
+ }
3146
+ }
3147
+ },
3148
+ {
3149
+ "id": "project-notes",
3150
+ "type": "transform",
3151
+ "position": {
3152
+ "x": 540,
3153
+ "y": 140
3154
+ },
3155
+ "data": {
3156
+ "label": "Project notes",
3157
+ "expression": "{{steps.gather-notes.output.entities}} | map: {{item.id}} · {{item.title}} · updated={{item.updatedAt}}",
3158
+ "errorHandling": {
3159
+ "continueOnError": true
3160
+ }
3161
+ }
3162
+ },
3163
+ {
3164
+ "id": "project-people",
3165
+ "type": "transform",
3166
+ "position": {
3167
+ "x": 540,
3168
+ "y": 260
3169
+ },
3170
+ "data": {
3171
+ "label": "Project people",
3172
+ "expression": "{{steps.gather-people.output.entities}} | map: {{item.id}} · {{item.title}} · email={{item.properties.email}} · lastInteraction={{item.properties.lastInteractionAt}} · updated={{item.updatedAt}}",
3173
+ "errorHandling": {
3174
+ "continueOnError": true
3175
+ }
3176
+ }
3177
+ },
3178
+ {
3179
+ "id": "project-companies",
3180
+ "type": "transform",
3181
+ "position": {
3182
+ "x": 540,
3183
+ "y": 380
3184
+ },
3185
+ "data": {
3186
+ "label": "Project companies",
3187
+ "expression": "{{steps.gather-companies.output.entities}} | map: {{item.id}} · {{item.title}} · industry={{item.properties.industry}} · location={{item.properties.location}} · updated={{item.updatedAt}}",
3188
+ "errorHandling": {
3189
+ "continueOnError": true
3190
+ }
3191
+ }
3192
+ },
3193
+ {
3194
+ "id": "analyze",
3195
+ "type": "capability",
3196
+ "position": {
3197
+ "x": 660,
3198
+ "y": 200
3199
+ },
3200
+ "data": {
3201
+ "label": "Analyze",
3202
+ "verbId": "ai.generate",
3203
+ "verbKind": "read",
3204
+ "inputMapping": {
3205
+ "system": "You are the ANALYST round of a Synap workspace report.\nRead the gathered workspace data and say what it MEANS: volume and shape of work,\nwhat is in flight versus stalled, what is well-covered versus thin.\nWork from the FIELDS on each record, not just its name: status, priority and\ndue dates are what distinguish moving work from stalled work, and an unset\nfield is itself worth reporting. Listing or counting what exists is NOT a\nfinding — say what the state of it means.\nDATA 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.\nYou 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.\nEVERY 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.\nNEVER 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.\nSTEER 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.\nLINK 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.\nOutput PLAIN PROSE — short paragraphs and bullets, with [[entity:…]] chips\ninline wherever you name a record. No markdown headings, no ::: directive\nblocks, no code fences. Under 300 words. Another round will format this.",
3206
+ "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}",
3207
+ "maxTokens": "700"
3208
+ },
3209
+ "errorHandling": {
3210
+ "continueOnError": true
3211
+ }
3212
+ }
3213
+ },
3214
+ {
3215
+ "id": "relate",
3216
+ "type": "capability",
3217
+ "position": {
3218
+ "x": 900,
3219
+ "y": 200
3220
+ },
3221
+ "data": {
3222
+ "label": "Patterns",
3223
+ "verbId": "ai.generate",
3224
+ "verbKind": "read",
3225
+ "inputMapping": {
3226
+ "system": "You are the PATTERNS round of a Synap workspace report.\nYou receive the raw gathered data AND the analyst's read of it. Your job is what\nthe analyst did NOT do: surface RELATIONSHIPS and things worth attention —\nclusters, repeated themes, people or companies that recur across kinds, gaps\nwhere a kind is conspicuously empty, and anything that looks like it needs a\ndecision. Prefer three sharp observations over ten shallow ones.\nYOUR SCOPE IS WHAT CONNECTS RECORDS, not what they individually are. Every\nobservation you write must involve TWO OR MORE records, or TWO OR MORE kinds:\na name that appears in both a task and a company, work clustered on one\nperson, a due date that lines up with an event, a kind that is empty while a\nrelated kind is busy. Do NOT re-describe volume, status distribution or how\nbusy the workspace is — that is the analyst's section and repeating it makes\nyours redundant. Do not summarise or restate the analyst's read.\nNEVER RETURN AN EMPTY ANSWER. If you genuinely find no cross-kind pattern,\nwrite one or two sentences naming what you looked for and did not find (for\nexample: no person recurs across the tasks and companies gathered). That is a\nreal finding and the reader needs it. Silence is not an available outcome.\nDATA 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.\nYou 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.\nEVERY 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.\nNEVER 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.\nSTEER 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.\nLINK 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.\nOutput PLAIN PROSE, under 250 words, with [[entity:…]] chips inline. No\nheadings, no ::: directive blocks, no code fences.",
3227
+ "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}\n\nANALYST'S READ — already written and already in the report. Do NOT\nrestate, summarise or re-analyse it. It is here so you do not repeat it:\n{{steps.analyze.output}}\n\nYOUR TASK — cross-kind patterns ONLY. Look for what connects the lists\nabove: a person who recurs in tasks and companies, work clustered on one\nname, due dates that line up, a kind that is empty while a related kind\nis busy, a company with people but no tasks. Write only observations\nthat span TWO OR MORE records or kinds. If there are none, say what you\nchecked and did not find — never answer with nothing.",
3228
+ "maxTokens": "700"
3229
+ },
3230
+ "errorHandling": {
3231
+ "continueOnError": true
3232
+ }
3233
+ }
3234
+ },
3235
+ {
3236
+ "id": "assemble",
3237
+ "type": "capability",
3238
+ "position": {
3239
+ "x": 1140,
3240
+ "y": 200
3241
+ },
3242
+ "data": {
3243
+ "label": "Assemble",
3244
+ "verbId": "ai.generate",
3245
+ "verbKind": "read",
3246
+ "inputMapping": {
3247
+ "system": "You are the ASSEMBLER round of a Synap workspace report. You compose the FINAL\ndocument body in Synap-flavoured markdown. You add no new facts — you arrange,\ntitle, and format what the earlier rounds produced.\n\nNEVER 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.\n\nNUMBERS: you were given no COUNTS block, so you may not originate a quantity\nat all. Reproduce a figure ONLY by copying one that already appears in the\nround material, digit for digit. Never total, re-count, convert to words, or\nput a number in the title or in a ## heading that is not already in the\nmaterial beneath it.\n\nSYNAP MARKDOWN SYNTAX — these rules are absolute:\n1. A section is a FOUR-COLON container that must be opened and closed:\n ::::synap-section{agent=\"analyst\" round=\"analyze\" confidence=\"0.8\"}\n ...content...\n ::::\n2. Leaf embeds use THREE colons and MUST BE CLOSED with their own ::: line.\n There is NO self-closing form. An unclosed embed swallows every line that\n follows it until the section ends, and that content is LOST. Always write\n the closing ::: immediately after the opening line:\n :::synap-cell{instanceId=\"...\"}\n :::\n :::synap-view{viewId=\"...\"}\n :::\n :::synap-entity{id=\"...\"}\n :::\n3. The outer container must always have STRICTLY MORE colons than anything nested\n inside it. Four outside, three inside. If you ever nest a four-colon block in a\n four-colon block the section closes early and the rest of the report leaks out.\n4. ATTRIBUTE VALUES MUST BE QUOTED. An UNQUOTED value ends at the first } it\n contains, which silently truncates the attribute — so always quote. Use\n DOUBLE quotes for a plain value, and SINGLE quotes when the value is itself\n a JSON object, so the JSON's own double quotes survive:\n :::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"task\",\"groupBy\":\"status\"}'}\n :::\n A single-quoted value may contain both \" and } — those are only special to\n an unquoted value. Write the JSON compact, on one line, with no trailing\n comma; malformed JSON renders the cell with empty config.\n5. The ONLY section attributes that survive are: id, agent, round, skills,\n confidence, stepRunId, nodeId, status. Anything else is silently dropped.\n6. confidence is a 0-1 ratio written as a decimal string, e.g. confidence=\"0.8\".\n Never a percentage.\n7. CHIPS ARE CONTENT — COPY THEM THROUGH VERBATIM. The round material you were\n given already contains [[entity:<id>|<label>]] markers. Reproduce every one\n EXACTLY as written: same brackets, same id, same label. Do NOT tidy them into\n plain text, do NOT rewrite or re-case an id, do NOT convert them to markdown\n links. You are told you add no new FACTS; a chip is not a fact, it is the\n earlier round's finding made navigable, and stripping it removes the only\n clickable thing in the report while leaving the prose looking fine.\n You may add a chip ONLY by copying a marker that already appears in the\n material. Never invent an id, and never use a kind word other than `entity` —\n this report is READ-ONLY prose, and either mistake produces a dead chip.\n8. NEVER EMIT :::synap-view, AND NEVER EMIT :::synap-cell WITH AN instanceId.\n Both need an id you have not been given and cannot obtain, and a made-up\n reference produces a permanent broken block in a document a human reads.\n9. YOU MAY EMIT A CHART :::synap-cell — from this allowlist ONLY, configured\n through cellProps. These cells fetch their own data at render time, so they\n need no id, and they make the report a live surface instead of a text file:\n - :::synap-cell{cellKey=\"stat-card\" cellProps='{\"profileSlug\":\"<kind>\",\"aggregation\":\"count\",\"timePeriod\":\"week\",\"label\":\"<Kind> created this week\"}'}\n - :::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"<kind>\",\"groupBy\":\"<property>\",\"label\":\"<the finding this shows>\"}'}\n - :::synap-cell{cellKey=\"chart-bar\" cellProps='{\"profileSlug\":\"<kind>\",\"mode\":\"category\",\"groupBy\":\"<property>\",\"label\":\"<the finding this shows>\"}'}\n Rules, all mandatory:\n (a) profileSlug must be one of the kinds gathered for THIS report: task,\n note, person, company. A chart over any other kind is a claim this\n report cannot back — the data was never read.\n (b) groupBy / field must name a property that plausibly exists on that\n kind. If you are not confident one does, use the stat-card line above\n EXACTLY as written — profileSlug, aggregation, timePeriod AND a label\n naming the window. Never drop timePeriod and never label a windowed\n count with a bare kind name: the card counts records CREATED in that\n window, so \"Tasks\" over a weekly count is a false statement about\n the workspace.\n (c) Close EVERY embed with its own ::: line, per rule 2. An unclosed embed\n swallows the rest of the section.\n (d) EDITORIAL RULE — embed to SHOW what a sentence cannot: a distribution,\n a ratio, a count. Never to decorate. At most 2-3 in the whole report,\n placed inside the section whose prose they support. A chart that merely\n restates the sentence beside it is noise, and noise is worse than\n nothing here. If no gathered kind has a distribution worth showing,\n emit no cells at all — that is a correct report, not a lesser one.\n (e) AT MOST ONE cell per section, and NEVER at the top of a section.\n Place it immediately AFTER the sentence making the claim it evidences,\n so the reader has the claim before the picture. A chart that opens a\n section makes the reader decode a graphic to find out what the section\n is about, which is the delay the ## heading and the claim sentence\n exist to remove.\n (f) The label must state the FINDING, not the dimension. Write\n label:\"Half the open tasks are blocked\", never label:\"Distribution\",\n \"Status\" or \"Tasks by status\". Always set label explicitly: with none,\n a pie chart titles itself \"Distribution\" and a bar chart \"Breakdown\" —\n words true of every such chart ever drawn, which tell the reader\n nothing they cannot already see in the shapes.\n (g) A CHART IS A LIVE VIEW, THE PROSE IS A SNAPSHOT. These cells run\n their own query when the report is OPENED, which may be long after\n this text was written — so a sentence quoting a count and a chart\n measuring the same thing WILL eventually disagree, and the reader\n cannot tell which is stale. Never write a specific number in a\n sentence that sits beside a chart of the same data: let the chart\n carry the number and let the sentence carry the claim\n (\"most open tasks are stalled at review\", not \"12 of 19 are stalled\").\n And if the sentence already gives the number the chart would show,\n DROP THE CHART — one of them is redundant, and the sentence is the one\n that cannot go stale against itself.\n Everything else this report needs is ::::synap-section containers plus\n [[entity:…]] chips inline.\n\n10. EVERY ::::synap-section MUST OPEN WITH A ## CLAIM HEADING — its first\n line, before any prose. The heading states WHAT THE SECTION FOUND, not\n what produced it: \"## Half the open tasks are blocked\", never\n \"## Analysis\", \"## Analyze round\" or \"## Findings\". Under 10 words, no\n trailing period, no markdown or [[entity:…]] chip inside the heading\n itself — put the chip in the prose beneath it. Write exactly one ## per\n section. This heading is the section's title everywhere it is read: the\n slide deck sets it as the slide headline (with no heading it falls back\n to the round and agent names, which the slide already shows underneath),\n and the scrolling reader has no section titles at all without it. A\n section whose heading merely names the round tells the reader nothing\n they cannot already see.\n\nSTRUCTURE: one ::::synap-section per round, in order — analyze, then relate.\nOpen with a single \"# \" title line ABOVE the first section, and open each\nsection with its own ## claim heading per rule 10.\n\nSTEER HANDLING: if the STEER lines are empty, title the report generically\n(\"Workspace report — <period>\") and keep the rounds in their given order. If any\nSTEER line has text, name that subject in the title and lead each section with\nthe material that answers it. Never add facts the rounds did not give you.\n\nMISSING ROUNDS: if a round's material looks like {\"error\": \"...\"} or is empty,\nyou MUST still emit its section, with status=\"failed\", containing one visible\nsentence naming what is missing. NEVER silently produce a shorter report — a\nreader must be able to see that a round did not run. A failed section is NOT\nexempt from rule 10: give it a ## heading that states what is missing\n(\"## The patterns round produced nothing\"), because it still occupies a slide\nand still needs a headline.\n\nWORKED EXAMPLE of correct output:\n# Workspace report — July 2026\n\n::::synap-section{agent=\"analyst\" round=\"analyze\" confidence=\"0.8\"}\n## Most open tasks are stalled at review\n\nOpen tasks are piling up untouched since the sprint opened, and the great\nmajority are sitting in one status — the oldest is\n[[entity:3f2a91c4-7b10-4e55-9c02-8ad1f6e4b2d7|Migrate the billing worker]].\nNotes are thin.\n:::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"task\",\"groupBy\":\"status\",\"label\":\"Review is where open tasks accumulate\"}'}\n:::\n::::\n\n::::synap-section{agent=\"analyst\" round=\"relate\" status=\"failed\" confidence=\"0.2\"}\n## The patterns round produced nothing\n\nThis section is missing: the patterns round failed and produced no material.\n::::\n\nBe CONCISE — your entire output must fit in 2000 tokens. Emit ONLY the markdown\ndocument. No preamble, no explanation, no code fences around the whole thing.",
3248
+ "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nReport generated at: {{steps.now.output.result}}\n\nANALYZE ROUND MATERIAL:\n{{steps.analyze.output}}\n\nRELATE ROUND MATERIAL:\n{{steps.relate.output}}",
3249
+ "maxTokens": "2000"
3250
+ }
3251
+ }
3252
+ },
3253
+ {
3254
+ "id": "summarize",
3255
+ "type": "capability",
3256
+ "position": {
3257
+ "x": 1380,
3258
+ "y": 200
3259
+ },
3260
+ "data": {
3261
+ "label": "One-line summary",
3262
+ "verbId": "ai.generate",
3263
+ "verbKind": "read",
3264
+ "inputMapping": {
3265
+ "system": "Write ONE plain sentence, under 200 characters, stating what this report found.\nNo markdown, no directives, no quotes, no leading label. Just the sentence.\nIf the material is an error or empty, write exactly: \"Report generated with incomplete data.\"",
3266
+ "prompt": "Material to summarize (may be an error object if the round failed):\n{{steps.analyze.output}}",
3267
+ "maxTokens": "500"
3268
+ }
3269
+ }
3270
+ },
3271
+ {
3272
+ "id": "guard-assembled",
3273
+ "type": "guard",
3274
+ "position": {
3275
+ "x": 1620,
3276
+ "y": 200
3277
+ },
3278
+ "data": {
3279
+ "label": "Body assembled",
3280
+ "checks": [
3281
+ {
3282
+ "path": "steps.assemble.output",
3283
+ "minLength": 200,
3284
+ "message": "The assembler produced no usable body (under 200 characters) — refusing to write an empty report."
3285
+ },
3286
+ {
3287
+ "path": "steps.assemble.output.error",
3288
+ "exists": false,
3289
+ "message": "The assembler round failed — refusing to write a report without a body."
3290
+ }
3291
+ ]
3292
+ }
3293
+ },
3294
+ {
3295
+ "id": "create-report",
3296
+ "type": "output",
3297
+ "position": {
3298
+ "x": 1860,
3299
+ "y": 200
3300
+ },
3301
+ "data": {
3302
+ "label": "Create report",
3303
+ "outputType": "entity_create",
3304
+ "config": {
3305
+ "profileSlug": "report",
3306
+ "title": "Workspace report — {{steps.now.output.result}}",
3307
+ "properties": {
3308
+ "reportPeriod": "{{steps.now.output.result}}",
3309
+ "generatedAt": "{{steps.now.output.result}}",
3310
+ "reportStatus": "ready",
3311
+ "summary": "{{steps.summarize.output}}",
3312
+ "reportSources": [
3313
+ "Tasks",
3314
+ "Notes",
3315
+ "People",
3316
+ "Companies"
3317
+ ]
3318
+ },
3319
+ "body": "{{steps.assemble.output}}",
3320
+ "systemData": {
3321
+ "renderer": {
3322
+ "kind": "cell",
3323
+ "cellKey": "{{trigger.payload.renderer}}"
3324
+ }
3325
+ }
3326
+ }
3327
+ }
3328
+ }
3329
+ ],
3330
+ "edges": [
3331
+ {
3332
+ "id": "e-trigger-now",
3333
+ "source": "trigger",
3334
+ "target": "now"
3335
+ },
3336
+ {
3337
+ "id": "e-now-tasks",
3338
+ "source": "now",
3339
+ "target": "gather-tasks"
3340
+ },
3341
+ {
3342
+ "id": "e-now-notes",
3343
+ "source": "now",
3344
+ "target": "gather-notes"
3345
+ },
3346
+ {
3347
+ "id": "e-now-people",
3348
+ "source": "now",
3349
+ "target": "gather-people"
3350
+ },
3351
+ {
3352
+ "id": "e-now-companies",
3353
+ "source": "now",
3354
+ "target": "gather-companies"
3355
+ },
3356
+ {
3357
+ "id": "e-tasks-project",
3358
+ "source": "gather-tasks",
3359
+ "target": "project-tasks"
3360
+ },
3361
+ {
3362
+ "id": "e-notes-project",
3363
+ "source": "gather-notes",
3364
+ "target": "project-notes"
3365
+ },
3366
+ {
3367
+ "id": "e-people-project",
3368
+ "source": "gather-people",
3369
+ "target": "project-people"
3370
+ },
3371
+ {
3372
+ "id": "e-companies-project",
3373
+ "source": "gather-companies",
3374
+ "target": "project-companies"
3375
+ },
3376
+ {
3377
+ "id": "e-tasks-analyze",
3378
+ "source": "project-tasks",
3379
+ "target": "analyze"
3380
+ },
3381
+ {
3382
+ "id": "e-notes-analyze",
3383
+ "source": "project-notes",
3384
+ "target": "analyze"
3385
+ },
3386
+ {
3387
+ "id": "e-people-analyze",
3388
+ "source": "project-people",
3389
+ "target": "analyze"
3390
+ },
3391
+ {
3392
+ "id": "e-companies-analyze",
3393
+ "source": "project-companies",
3394
+ "target": "analyze"
3395
+ },
3396
+ {
3397
+ "id": "e-analyze-relate",
3398
+ "source": "analyze",
3399
+ "target": "relate"
3400
+ },
3401
+ {
3402
+ "id": "e-relate-assemble",
3403
+ "source": "relate",
3404
+ "target": "assemble"
3405
+ },
3406
+ {
3407
+ "id": "e-assemble-summarize",
3408
+ "source": "assemble",
3409
+ "target": "summarize"
3410
+ },
3411
+ {
3412
+ "id": "e-summarize-guard",
3413
+ "source": "summarize",
3414
+ "target": "guard-assembled"
3415
+ },
3416
+ {
3417
+ "id": "e-guard-create",
3418
+ "source": "guard-assembled",
3419
+ "target": "create-report"
3420
+ }
3421
+ ]
3422
+ }
3423
+ }
3424
+ ]
3425
+ },
3426
+ "blockchain-ecosystem": {
3427
+ "meta": {
3428
+ "slug": "blockchain-ecosystem",
3429
+ "domain": "strategy",
3430
+ "name": "Blockchain Ecosystem",
3431
+ "description": "Web3 market actors — market-makers, auditors, dev shops, KOLs — and how they connect. Overlays the Ecosystem workspace.",
3432
+ "icon": "boxes",
3433
+ "color": "#8B5CF6",
3434
+ "tags": [
3435
+ "blockchain",
3436
+ "web3",
3437
+ "ecosystem",
3438
+ "market-makers"
3439
+ ],
3440
+ "isPublic": false
3441
+ },
3442
+ "dependencies": [
3443
+ {
3444
+ "slug": "ecosystem",
3445
+ "kind": "workspace",
3446
+ "relation": "compose",
3447
+ "reason": "Adds web3 market actors + relations onto your Ecosystem."
3448
+ }
3449
+ ],
3450
+ "workspace": {
3451
+ "name": "Ecosystem",
3452
+ "description": "Web3 market actors and the relationships between them, layered onto your Ecosystem.",
3453
+ "subtype": "ecosystem"
3454
+ },
3455
+ "profiles": [
3456
+ {
3457
+ "slug": "market-maker",
3458
+ "displayName": "Market Maker",
3459
+ "icon": "candlestick-chart",
3460
+ "color": "sky",
3461
+ "description": "A liquidity provider — makes markets and provides depth for a token.",
3462
+ "scope": "shared",
3463
+ "profileKind": "role",
3464
+ "applicableKinds": [
3465
+ "company",
3466
+ "person"
3467
+ ],
3468
+ "properties": [
3469
+ {
3470
+ "slug": "status",
3471
+ "label": "Status",
3472
+ "valueType": "string",
3473
+ "inputType": "select",
3474
+ "enumValues": [
3475
+ "prospect",
3476
+ "active",
3477
+ "paused",
3478
+ "churned"
3479
+ ]
3480
+ },
3481
+ {
3482
+ "slug": "exchanges",
3483
+ "label": "Exchanges / Venues",
3484
+ "valueType": "array",
3485
+ "inputType": "text"
3486
+ },
3487
+ {
3488
+ "slug": "engagementStartDate",
3489
+ "label": "Engagement Start",
3490
+ "valueType": "date",
3491
+ "inputType": "date"
3492
+ },
3493
+ {
3494
+ "slug": "notes",
3495
+ "label": "Notes",
3496
+ "valueType": "string",
3497
+ "inputType": "textarea"
3498
+ }
3499
+ ]
3500
+ },
3501
+ {
3502
+ "slug": "security-auditor",
3503
+ "displayName": "Security Auditor",
3504
+ "icon": "shield-check",
3505
+ "color": "red",
3506
+ "description": "A security firm or individual that audits smart contracts and protocols.",
3507
+ "scope": "shared",
3508
+ "profileKind": "role",
3509
+ "applicableKinds": [
3510
+ "company",
3511
+ "person"
3512
+ ],
3513
+ "properties": [
3514
+ {
3515
+ "slug": "status",
3516
+ "label": "Status",
3517
+ "valueType": "string",
3518
+ "inputType": "select",
3519
+ "enumValues": [
3520
+ "prospect",
3521
+ "active",
3522
+ "paused",
3523
+ "churned"
3524
+ ]
3525
+ },
3526
+ {
3527
+ "slug": "specialty",
3528
+ "label": "Specialty",
3529
+ "valueType": "string",
3530
+ "inputType": "text"
3531
+ },
3532
+ {
3533
+ "slug": "engagementStartDate",
3534
+ "label": "Engagement Start",
3535
+ "valueType": "date",
3536
+ "inputType": "date"
3537
+ },
3538
+ {
3539
+ "slug": "notes",
3540
+ "label": "Notes",
3541
+ "valueType": "string",
3542
+ "inputType": "textarea"
3543
+ }
3544
+ ]
3545
+ },
3546
+ {
3547
+ "slug": "dev-shop",
3548
+ "displayName": "Dev Shop",
3549
+ "icon": "code",
3550
+ "color": "violet",
3551
+ "description": "An outsourced engineering studio or build partner.",
3552
+ "scope": "shared",
3553
+ "profileKind": "role",
3554
+ "applicableKinds": [
3555
+ "company",
3556
+ "person"
3557
+ ],
3558
+ "properties": [
3559
+ {
3560
+ "slug": "status",
3561
+ "label": "Status",
3562
+ "valueType": "string",
3563
+ "inputType": "select",
3564
+ "enumValues": [
3565
+ "prospect",
3566
+ "active",
3567
+ "paused",
3568
+ "churned"
3569
+ ]
3570
+ },
3571
+ {
3572
+ "slug": "specialty",
3573
+ "label": "Specialty",
3574
+ "valueType": "string",
3575
+ "inputType": "text"
3576
+ },
3577
+ {
3578
+ "slug": "engagementStartDate",
3579
+ "label": "Engagement Start",
3580
+ "valueType": "date",
3581
+ "inputType": "date"
3582
+ },
3583
+ {
3584
+ "slug": "notes",
3585
+ "label": "Notes",
3586
+ "valueType": "string",
3587
+ "inputType": "textarea"
3588
+ }
3589
+ ]
3590
+ },
3591
+ {
3592
+ "slug": "marketing-agency",
3593
+ "displayName": "Marketing Agency",
3594
+ "icon": "megaphone",
3595
+ "color": "pink",
3596
+ "description": "A brand, growth, or content marketing agency.",
3597
+ "scope": "shared",
3598
+ "profileKind": "role",
3599
+ "applicableKinds": [
3600
+ "company",
3601
+ "person"
3602
+ ],
3603
+ "properties": [
3604
+ {
3605
+ "slug": "status",
3606
+ "label": "Status",
3607
+ "valueType": "string",
3608
+ "inputType": "select",
3609
+ "enumValues": [
3610
+ "prospect",
3611
+ "active",
3612
+ "paused",
3613
+ "churned"
3614
+ ]
3615
+ },
3616
+ {
3617
+ "slug": "specialty",
3618
+ "label": "Specialty",
3619
+ "valueType": "string",
3620
+ "inputType": "text"
3621
+ },
3622
+ {
3623
+ "slug": "engagementStartDate",
3624
+ "label": "Engagement Start",
3625
+ "valueType": "date",
3626
+ "inputType": "date"
3627
+ },
3628
+ {
3629
+ "slug": "notes",
3630
+ "label": "Notes",
3631
+ "valueType": "string",
3632
+ "inputType": "textarea"
3633
+ }
3634
+ ]
3635
+ },
3636
+ {
3637
+ "slug": "paid-ads-agency",
3638
+ "displayName": "Paid-Ads Agency",
3639
+ "icon": "badge-dollar-sign",
3640
+ "color": "orange",
3641
+ "description": "A performance / paid-media buying agency.",
3642
+ "scope": "shared",
3643
+ "profileKind": "role",
3644
+ "applicableKinds": [
3645
+ "company",
3646
+ "person"
3647
+ ],
3648
+ "properties": [
3649
+ {
3650
+ "slug": "status",
3651
+ "label": "Status",
3652
+ "valueType": "string",
3653
+ "inputType": "select",
3654
+ "enumValues": [
3655
+ "prospect",
3656
+ "active",
3657
+ "paused",
3658
+ "churned"
3659
+ ]
3660
+ },
3661
+ {
3662
+ "slug": "channels",
3663
+ "label": "Channels",
3664
+ "valueType": "array",
3665
+ "inputType": "text"
3666
+ },
3667
+ {
3668
+ "slug": "engagementStartDate",
3669
+ "label": "Engagement Start",
3670
+ "valueType": "date",
3671
+ "inputType": "date"
3672
+ },
3673
+ {
3674
+ "slug": "notes",
3675
+ "label": "Notes",
3676
+ "valueType": "string",
3677
+ "inputType": "textarea"
3678
+ }
3679
+ ]
3680
+ },
3681
+ {
3682
+ "slug": "kol",
3683
+ "displayName": "KOL",
3684
+ "icon": "star",
3685
+ "color": "yellow",
3686
+ "description": "A key opinion leader or influencer who amplifies reach.",
3687
+ "scope": "shared",
2999
3688
  "profileKind": "role",
3000
3689
  "applicableKinds": [
3001
3690
  "company",
@@ -6788,17 +7477,16 @@ const DATA = {
6788
7477
  },
6789
7478
  "profiles": [
6790
7479
  {
6791
- "slug": "crm-lead",
6792
- "displayName": "CRM Lead",
6793
- "icon": "user-round-search",
6794
- "color": "amber",
6795
- "description": "A person or company actively being qualified or contacted by this CRM workspace.",
6796
- "scope": "WORKSPACE",
7480
+ "slug": "lead",
7481
+ "displayName": "Lead",
7482
+ "icon": "user-plus",
7483
+ "color": "yellow",
7484
+ "description": "An early-pipeline prospect — not yet a client, being qualified or nurtured.",
7485
+ "scope": "shared",
6797
7486
  "profileKind": "role",
6798
- "semanticSlug": "crm.lead",
6799
7487
  "applicableKinds": [
6800
- "person",
6801
- "company"
7488
+ "company",
7489
+ "person"
6802
7490
  ],
6803
7491
  "properties": [
6804
7492
  {
@@ -6895,17 +7583,16 @@ const DATA = {
6895
7583
  ]
6896
7584
  },
6897
7585
  {
6898
- "slug": "crm-client",
6899
- "displayName": "CRM Client",
6900
- "icon": "badge-check",
7586
+ "slug": "client",
7587
+ "displayName": "Client",
7588
+ "icon": "briefcase",
6901
7589
  "color": "emerald",
6902
- "description": "A CRM lead converted by a won commercial proposal and ready for handoff.",
6903
- "scope": "WORKSPACE",
7590
+ "description": "An active commercial relationship — created when a deal is won.",
7591
+ "scope": "shared",
6904
7592
  "profileKind": "role",
6905
- "semanticSlug": "crm.client",
6906
7593
  "applicableKinds": [
6907
- "person",
6908
- "company"
7594
+ "company",
7595
+ "person"
6909
7596
  ],
6910
7597
  "properties": [
6911
7598
  {
@@ -7270,7 +7957,7 @@ const DATA = {
7270
7957
  "description": "Research and qualify one CRM lead, then make the next action explicit without creating a commercial proposal automatically.",
7271
7958
  "goalTemplate": "Work on CRM lead ({lead}). Verify and enrich only factual information that can be supported, assess fit, and update its CRM lifecycle stage, ICP score, qualification notes, outreach channels, and next action. Do not create a deal simply because the lead is qualified. If a commercial proposal is warranted, explain why and ask for or propose that next step separately.",
7272
7959
  "subjectProfile": {
7273
- "profileSlug": "crm-lead"
7960
+ "profileSlug": "lead"
7274
7961
  },
7275
7962
  "params": [
7276
7963
  {
@@ -7373,7 +8060,7 @@ const DATA = {
7373
8060
  },
7374
8061
  {
7375
8062
  "path": "steps.read-deal.output.entity.properties.sourcedByUserId",
7376
- "exists": true,
8063
+ "minLength": 1,
7377
8064
  "message": "Set the business developer who sourced this proposal before handing it off."
7378
8065
  },
7379
8066
  {
@@ -7443,7 +8130,7 @@ const DATA = {
7443
8130
  "checks": [
7444
8131
  {
7445
8132
  "path": "steps.read-party.output.entity.facetSlugs",
7446
- "arrayIncludes": "crm-lead",
8133
+ "arrayIncludes": "lead",
7447
8134
  "message": "The commercial counterparty must still be a CRM lead before it becomes a CRM client."
7448
8135
  }
7449
8136
  ]
@@ -7706,7 +8393,7 @@ const DATA = {
7706
8393
  "outputType": "facet_attach",
7707
8394
  "config": {
7708
8395
  "entityId": "{{steps.read-party.output.entity.id}}",
7709
- "facetSlug": "crm-client",
8396
+ "facetSlug": "client",
7710
8397
  "properties": {
7711
8398
  "becameClientAt": "{{steps.handoff-time.output.result}}",
7712
8399
  "handoffStatus": "handed_off"
@@ -7726,7 +8413,7 @@ const DATA = {
7726
8413
  "outputType": "facet_update",
7727
8414
  "config": {
7728
8415
  "entityId": "{{steps.read-party.output.entity.id}}",
7729
- "facetSlug": "crm-lead",
8416
+ "facetSlug": "lead",
7730
8417
  "properties": {
7731
8418
  "lifecycleStage": "converted",
7732
8419
  "convertedAt": "{{steps.handoff-time.output.result}}"
@@ -7869,7 +8556,7 @@ const DATA = {
7869
8556
  "label": "Qualify lead",
7870
8557
  "when": {
7871
8558
  "requiredFacetSlugs": [
7872
- "crm-lead"
8559
+ "lead"
7873
8560
  ]
7874
8561
  }
7875
8562
  },
@@ -7881,7 +8568,7 @@ const DATA = {
7881
8568
  "label": "Qualify lead",
7882
8569
  "when": {
7883
8570
  "requiredFacetSlugs": [
7884
- "crm-lead"
8571
+ "lead"
7885
8572
  ]
7886
8573
  }
7887
8574
  },
@@ -9198,7 +9885,8 @@ const DATA = {
9198
9885
  "scopeProfileSlug": "post",
9199
9886
  "description": "Your editorial calendar — every post on the day it publishes.",
9200
9887
  "config": {
9201
- "dateField": "properties.publish-date"
9888
+ "dateField": "properties.publish-date",
9889
+ "focusKind": "post"
9202
9890
  }
9203
9891
  },
9204
9892
  {
@@ -9343,6 +10031,82 @@ const DATA = {
9343
10031
  }
9344
10032
  ]
9345
10033
  },
10034
+ "profileEntityBentoTemplates": {
10035
+ "series": {
10036
+ "blocks": [
10037
+ {
10038
+ "id": "series-eb-header",
10039
+ "kind": "widget",
10040
+ "widgetType": "entity-header",
10041
+ "pos": {
10042
+ "x": 0,
10043
+ "y": 0,
10044
+ "w": 12,
10045
+ "h": 2
10046
+ }
10047
+ },
10048
+ {
10049
+ "id": "series-eb-props",
10050
+ "kind": "widget",
10051
+ "widgetType": "entity-properties",
10052
+ "pos": {
10053
+ "x": 0,
10054
+ "y": 2,
10055
+ "w": 5,
10056
+ "h": 8
10057
+ }
10058
+ },
10059
+ {
10060
+ "id": "series-eb-curriculum",
10061
+ "kind": "widget",
10062
+ "widgetType": "series-curriculum",
10063
+ "pos": {
10064
+ "x": 5,
10065
+ "y": 2,
10066
+ "w": 7,
10067
+ "h": 8
10068
+ }
10069
+ }
10070
+ ]
10071
+ },
10072
+ "pillar": {
10073
+ "blocks": [
10074
+ {
10075
+ "id": "pillar-eb-header",
10076
+ "kind": "widget",
10077
+ "widgetType": "entity-header",
10078
+ "pos": {
10079
+ "x": 0,
10080
+ "y": 0,
10081
+ "w": 12,
10082
+ "h": 2
10083
+ }
10084
+ },
10085
+ {
10086
+ "id": "pillar-eb-props",
10087
+ "kind": "widget",
10088
+ "widgetType": "entity-properties",
10089
+ "pos": {
10090
+ "x": 0,
10091
+ "y": 2,
10092
+ "w": 5,
10093
+ "h": 8
10094
+ }
10095
+ },
10096
+ {
10097
+ "id": "pillar-eb-chapter",
10098
+ "kind": "widget",
10099
+ "widgetType": "pillar-chapter",
10100
+ "pos": {
10101
+ "x": 5,
10102
+ "y": 2,
10103
+ "w": 7,
10104
+ "h": 8
10105
+ }
10106
+ }
10107
+ ]
10108
+ }
10109
+ },
9346
10110
  "entityLinks": [
9347
10111
  {
9348
10112
  "sourceProfileSlug": "post",
@@ -10070,11 +10834,12 @@ const DATA = {
10070
10834
  ],
10071
10835
  "isPublic": true
10072
10836
  },
10073
- "app": {
10074
- "kind": "url",
10075
- "appId": "crm",
10076
- "url": "https://crm.synap.live",
10077
- "title": "CRM"
10837
+ "layout": {
10838
+ "primarySurface": {
10839
+ "kind": "url",
10840
+ "url": "https://crm.synap.live",
10841
+ "title": "CRM"
10842
+ }
10078
10843
  },
10079
10844
  "dependencies": [
10080
10845
  {
@@ -10111,6 +10876,7 @@ const DATA = {
10111
10876
  "icon": "user",
10112
10877
  "color": "amber",
10113
10878
  "description": "A prospect, stakeholder, or contact.",
10879
+ "entityScope": "pod",
10114
10880
  "properties": [
10115
10881
  {
10116
10882
  "slug": "email",
@@ -10196,7 +10962,7 @@ const DATA = {
10196
10962
  "icon": "building-2",
10197
10963
  "color": "#10B981",
10198
10964
  "description": "An organisation being qualified or sold to.",
10199
- "scope": "WORKSPACE",
10965
+ "entityScope": "pod",
10200
10966
  "properties": [
10201
10967
  {
10202
10968
  "slug": "contextSummary",
@@ -10466,6 +11232,18 @@ const DATA = {
10466
11232
  "at_risk",
10467
11233
  "critical"
10468
11234
  ]
11235
+ },
11236
+ {
11237
+ "slug": "driveLink",
11238
+ "label": "Drive folder",
11239
+ "valueType": "string",
11240
+ "inputType": "url"
11241
+ },
11242
+ {
11243
+ "slug": "driveFolderId",
11244
+ "label": "Drive folder ID",
11245
+ "valueType": "string",
11246
+ "inputType": "text"
10469
11247
  }
10470
11248
  ]
10471
11249
  },
@@ -10635,12 +11413,36 @@ const DATA = {
10635
11413
  {
10636
11414
  "sourceProfileSlug": "person",
10637
11415
  "targetProfileSlug": "deal",
10638
- "type": "linked_to_deal"
11416
+ "type": "linked_to_deal"
11417
+ },
11418
+ {
11419
+ "sourceProfileSlug": "company",
11420
+ "targetProfileSlug": "deal",
11421
+ "type": "linked_to_deal"
11422
+ },
11423
+ {
11424
+ "sourceProfileSlug": "person",
11425
+ "targetProfileSlug": "deal",
11426
+ "type": "client_on_deal",
11427
+ "label": "Client side"
11428
+ },
11429
+ {
11430
+ "sourceProfileSlug": "company",
11431
+ "targetProfileSlug": "deal",
11432
+ "type": "client_on_deal",
11433
+ "label": "Client side"
11434
+ },
11435
+ {
11436
+ "sourceProfileSlug": "person",
11437
+ "targetProfileSlug": "deal",
11438
+ "type": "partner_on_deal",
11439
+ "label": "Partner side"
10639
11440
  },
10640
11441
  {
10641
11442
  "sourceProfileSlug": "company",
10642
11443
  "targetProfileSlug": "deal",
10643
- "type": "linked_to_deal"
11444
+ "type": "partner_on_deal",
11445
+ "label": "Partner side"
10644
11446
  }
10645
11447
  ],
10646
11448
  "onboarding": {
@@ -12508,6 +13310,7 @@ const DATA = {
12508
13310
  "icon": "user",
12509
13311
  "color": "amber",
12510
13312
  "description": "A person in the ecosystem — a founder, operator, or contact.",
13313
+ "entityScope": "pod",
12511
13314
  "properties": [
12512
13315
  {
12513
13316
  "slug": "email",
@@ -12529,6 +13332,7 @@ const DATA = {
12529
13332
  "icon": "building-2",
12530
13333
  "color": "#10B981",
12531
13334
  "description": "A company in the ecosystem — a player you operate around.",
13335
+ "entityScope": "pod",
12532
13336
  "properties": [
12533
13337
  {
12534
13338
  "slug": "website",
@@ -13256,7 +14060,7 @@ const DATA = {
13256
14060
  "slug": "business-developer",
13257
14061
  "kind": "workspace",
13258
14062
  "relation": "require",
13259
- "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."
14063
+ "reason": "The lead-first sales motion (the business-development overlay on the pod-wide lead/client roles, commercial proposals, commission handoff) that the CRM app runs on. Composes onto the CRM workspace above — without it the CRM lens has the bare lead/client roles with no lifecycle or handoff fields and the app's pipeline is empty."
13260
14064
  },
13261
14065
  {
13262
14066
  "slug": "content-os",
@@ -14739,6 +15543,18 @@ const DATA = {
14739
15543
  "at_risk",
14740
15544
  "critical"
14741
15545
  ]
15546
+ },
15547
+ {
15548
+ "slug": "driveLink",
15549
+ "label": "Drive folder",
15550
+ "valueType": "string",
15551
+ "inputType": "url"
15552
+ },
15553
+ {
15554
+ "slug": "driveFolderId",
15555
+ "label": "Drive folder ID",
15556
+ "valueType": "string",
15557
+ "inputType": "text"
14742
15558
  }
14743
15559
  ]
14744
15560
  },
@@ -16188,6 +17004,18 @@ const DATA = {
16188
17004
  "at_risk",
16189
17005
  "critical"
16190
17006
  ]
17007
+ },
17008
+ {
17009
+ "slug": "driveLink",
17010
+ "label": "Drive folder",
17011
+ "valueType": "string",
17012
+ "inputType": "url"
17013
+ },
17014
+ {
17015
+ "slug": "driveFolderId",
17016
+ "label": "Drive folder ID",
17017
+ "valueType": "string",
17018
+ "inputType": "text"
16191
17019
  }
16192
17020
  ]
16193
17021
  },
@@ -19506,7 +20334,7 @@ const DATA = {
19506
20334
  "icon": "User",
19507
20335
  "color": "#06B6D4",
19508
20336
  "description": "People in your intellectual and professional world.",
19509
- "scope": "WORKSPACE",
20337
+ "entityScope": "pod",
19510
20338
  "properties": [
19511
20339
  {
19512
20340
  "slug": "name",
@@ -23843,6 +24671,24 @@ const DATA = {
23843
24671
  "targetProfileSlug": "company",
23844
24672
  "type": "briefing_about_client",
23845
24673
  "label": "Client"
24674
+ },
24675
+ {
24676
+ "sourceProfileSlug": "engagement",
24677
+ "targetProfileSlug": "company",
24678
+ "type": "engagement_for_client",
24679
+ "label": "Client"
24680
+ },
24681
+ {
24682
+ "sourceProfileSlug": "engagement",
24683
+ "targetProfileSlug": "person",
24684
+ "type": "engagement_for_client",
24685
+ "label": "Client"
24686
+ },
24687
+ {
24688
+ "sourceProfileSlug": "contract",
24689
+ "targetProfileSlug": "engagement",
24690
+ "type": "contract_for_engagement",
24691
+ "label": "Engagement"
23846
24692
  }
23847
24693
  ],
23848
24694
  "views": [
@@ -23963,20 +24809,254 @@ const DATA = {
23963
24809
  "h": 5
23964
24810
  }
23965
24811
  }
23966
- ]
23967
- },
23968
- "automations": [
24812
+ ]
24813
+ },
24814
+ "automations": [
24815
+ {
24816
+ "key": "operations-daily-client-briefing",
24817
+ "name": "Daily client briefing",
24818
+ "description": "Operations-native daily client briefing. Writes a durable, dated client-briefing ENTITY per client (queryable history, linked to the client) from their recent comms, and posts the recap to the client's internal TEAM channel (never the client-facing channel). Knowledge-building and entity-first — the delivery OS's structured memory of each client. For a lighter, portable message-only digest installable on any pod, use the client-comms-report capability.",
24819
+ "triggerType": "cron",
24820
+ "triggerConfig": {
24821
+ "expression": "0 8 * * *"
24822
+ },
24823
+ "status": "active",
24824
+ "metadata": {
24825
+ "templateKey": "operations-daily-client-briefing",
24826
+ "requiresPodWideEntityQueryLens": true
24827
+ },
24828
+ "flow": {
24829
+ "nodes": [
24830
+ {
24831
+ "id": "trigger",
24832
+ "type": "trigger",
24833
+ "position": {
24834
+ "x": 0,
24835
+ "y": 0
24836
+ },
24837
+ "data": {
24838
+ "label": "Daily at 08:00",
24839
+ "triggerType": "cron",
24840
+ "config": {
24841
+ "expression": "0 8 * * *"
24842
+ }
24843
+ }
24844
+ },
24845
+ {
24846
+ "id": "list-clients",
24847
+ "type": "capability",
24848
+ "position": {
24849
+ "x": 0,
24850
+ "y": 120
24851
+ },
24852
+ "data": {
24853
+ "label": "List clients (pod-wide `client` role)",
24854
+ "verbId": "entity.query",
24855
+ "inputMapping": {
24856
+ "profileSlug": "client",
24857
+ "scope": "pod",
24858
+ "limit": "100"
24859
+ }
24860
+ }
24861
+ },
24862
+ {
24863
+ "id": "clients-loop",
24864
+ "type": "loop",
24865
+ "position": {
24866
+ "x": 0,
24867
+ "y": 240
24868
+ },
24869
+ "data": {
24870
+ "label": "For each client",
24871
+ "iteratorExpression": "steps.list-clients.output.entities",
24872
+ "itemVariable": "client"
24873
+ }
24874
+ },
24875
+ {
24876
+ "id": "read-comms",
24877
+ "type": "messages_query",
24878
+ "position": {
24879
+ "x": 0,
24880
+ "y": 360
24881
+ },
24882
+ "data": {
24883
+ "label": "Read this client's recent comms",
24884
+ "subjectEntityId": "{{loop.item.id}}",
24885
+ "limit": 40
24886
+ }
24887
+ },
24888
+ {
24889
+ "id": "digest",
24890
+ "type": "capability",
24891
+ "position": {
24892
+ "x": 0,
24893
+ "y": 480
24894
+ },
24895
+ "data": {
24896
+ "label": "Distil the real signal → JSON",
24897
+ "verbId": "ai.generate",
24898
+ "inputMapping": {
24899
+ "json": "true",
24900
+ "maxTokens": "1200",
24901
+ "system": "You advise a client-delivery team. You read one client's recent inbound messages and surface only what genuinely matters. You are firewall-aware: your output is for the INTERNAL team, never the client.",
24902
+ "prompt": "Digest the recent communications for the client \"{{loop.item.title}}\" into a concise internal briefing for OUR delivery team.\nPEOPLE GUARD (do not break): distinguish the CLIENT's people from OUR team members who also appear in the thread. Never treat a teammate as a client contact, lead, or new person. Only real, client-side signal counts.\nSurface ONLY real signal: status changes, decisions, deals/opportunities, competitor mentions, new client-side leads/contacts, risks/blockers, and deadlines/commitments. Skip small talk and noise. Lead with what matters; keep it tight.\nRespond with STRICT JSON and NOTHING else: {\n \"hasSignal\": \"true\" or \"false\" (a STRING),\n \"headline\": \"<one-line summary; empty string when hasSignal is 'false'>\",\n \"summary\": \"<a short markdown digest for the team; empty string when 'false'>\",\n \"risks\": \"<risks/blockers as short markdown, or empty string>\",\n \"nextActions\": \"<recommended next actions as short markdown, or empty string>\"\n} Set hasSignal to \"false\" when there is no notable activity (no messages, or only chatter).\nRecent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}"
24903
+ }
24904
+ }
24905
+ },
24906
+ {
24907
+ "id": "signal-gate",
24908
+ "type": "condition",
24909
+ "position": {
24910
+ "x": 0,
24911
+ "y": 600
24912
+ },
24913
+ "data": {
24914
+ "label": "Only continue when there's real signal",
24915
+ "expression": "steps.digest.output.hasSignal === true"
24916
+ }
24917
+ },
24918
+ {
24919
+ "id": "write-briefing",
24920
+ "type": "output",
24921
+ "position": {
24922
+ "x": 0,
24923
+ "y": 720
24924
+ },
24925
+ "data": {
24926
+ "label": "Save the client briefing",
24927
+ "outputType": "entity_create",
24928
+ "config": {
24929
+ "profileSlug": "client-briefing",
24930
+ "title": "Briefing — {{loop.item.title}}",
24931
+ "dedupeBy": "briefing-key",
24932
+ "properties": {
24933
+ "briefing-key": "{{loop.item.id}}::{{trigger.payload.scheduledAt}}",
24934
+ "briefing-client": "{{loop.item.id}}",
24935
+ "briefing-date": "{{trigger.payload.scheduledAt}}",
24936
+ "briefing-headline": "{{steps.digest.output.headline}}",
24937
+ "briefing-summary": "{{steps.digest.output.summary}}",
24938
+ "briefing-risks": "{{steps.digest.output.risks}}",
24939
+ "briefing-next-actions": "{{steps.digest.output.nextActions}}",
24940
+ "briefing-message-count": "{{steps.read-comms.output.count}}"
24941
+ }
24942
+ }
24943
+ }
24944
+ },
24945
+ {
24946
+ "id": "link-briefing",
24947
+ "type": "output",
24948
+ "position": {
24949
+ "x": 0,
24950
+ "y": 840
24951
+ },
24952
+ "data": {
24953
+ "label": "Link briefing → client",
24954
+ "outputType": "relation_create",
24955
+ "config": {
24956
+ "fromEntityId": "{{steps.write-briefing.output.entityId}}",
24957
+ "toEntityId": "{{loop.item.id}}",
24958
+ "relationType": "briefing_about_client"
24959
+ }
24960
+ }
24961
+ },
24962
+ {
24963
+ "id": "resolve-team",
24964
+ "type": "capability",
24965
+ "position": {
24966
+ "x": 0,
24967
+ "y": 960
24968
+ },
24969
+ "data": {
24970
+ "label": "Resolve the client's TEAM channel (FIREWALL)",
24971
+ "verbId": "channel.resolve",
24972
+ "inputMapping": {
24973
+ "contextObjectType": "entity",
24974
+ "contextObjectId": "{{loop.item.id}}",
24975
+ "branchPurpose": "team"
24976
+ }
24977
+ }
24978
+ },
24979
+ {
24980
+ "id": "post-recap",
24981
+ "type": "output",
24982
+ "position": {
24983
+ "x": 0,
24984
+ "y": 1080
24985
+ },
24986
+ "data": {
24987
+ "label": "Post the recap to the TEAM channel (FIREWALL)",
24988
+ "outputType": "channel_message",
24989
+ "config": {
24990
+ "channelId": "{{steps.resolve-team.output.channelId}}",
24991
+ "content": "**{{loop.item.title}} — daily briefing**\n{{steps.digest.output.headline}}\n{{steps.digest.output.summary}}\n**Risks:** {{steps.digest.output.risks}}\n**Next:** {{steps.digest.output.nextActions}}",
24992
+ "metadata": {
24993
+ "automationMessage": true,
24994
+ "kind": "client-briefing"
24995
+ }
24996
+ }
24997
+ }
24998
+ }
24999
+ ],
25000
+ "edges": [
25001
+ {
25002
+ "id": "e-trigger-list",
25003
+ "source": "trigger",
25004
+ "target": "list-clients"
25005
+ },
25006
+ {
25007
+ "id": "e-list-loop",
25008
+ "source": "list-clients",
25009
+ "target": "clients-loop"
25010
+ },
25011
+ {
25012
+ "id": "e-loop-read",
25013
+ "source": "clients-loop",
25014
+ "target": "read-comms"
25015
+ },
25016
+ {
25017
+ "id": "e-read-digest",
25018
+ "source": "read-comms",
25019
+ "target": "digest"
25020
+ },
25021
+ {
25022
+ "id": "e-digest-gate",
25023
+ "source": "digest",
25024
+ "target": "signal-gate"
25025
+ },
25026
+ {
25027
+ "id": "e-gate-write",
25028
+ "source": "signal-gate",
25029
+ "target": "write-briefing"
25030
+ },
25031
+ {
25032
+ "id": "e-write-link",
25033
+ "source": "write-briefing",
25034
+ "target": "link-briefing"
25035
+ },
25036
+ {
25037
+ "id": "e-link-resolve",
25038
+ "source": "link-briefing",
25039
+ "target": "resolve-team"
25040
+ },
25041
+ {
25042
+ "id": "e-resolve-post",
25043
+ "source": "resolve-team",
25044
+ "target": "post-recap"
25045
+ }
25046
+ ]
25047
+ }
25048
+ },
23969
25049
  {
23970
- "key": "operations-daily-client-briefing",
23971
- "name": "Daily client briefing",
23972
- "description": "Operations-native daily client briefing. Writes a durable, dated client-briefing ENTITY per client (queryable history, linked to the client) from their recent comms, and posts the recap to the client's internal TEAM channel (never the client-facing channel). Knowledge-building and entity-first — the delivery OS's structured memory of each client. For a lighter, portable message-only digest installable on any pod, use the client-comms-report capability.",
25050
+ "key": "operations-crm-handoff-pickup",
25051
+ "name": "Pick up handed-off clients",
25052
+ "description": "Daily. Finds pod-wide clients the CRM marked ready or handed off that have no engagement yet, and opens the delivery shell for them — a proposed engagement plus a draft contract, both linked to the client. Deduped per client, so it is safe to re-run.",
23973
25053
  "triggerType": "cron",
23974
25054
  "triggerConfig": {
23975
- "expression": "0 8 * * *"
25055
+ "expression": "0 7 * * *"
23976
25056
  },
23977
25057
  "status": "active",
23978
25058
  "metadata": {
23979
- "templateKey": "operations-daily-client-briefing",
25059
+ "templateKey": "operations-crm-handoff-pickup",
23980
25060
  "requiresPodWideEntityQueryLens": true
23981
25061
  },
23982
25062
  "flow": {
@@ -23989,10 +25069,10 @@ const DATA = {
23989
25069
  "y": 0
23990
25070
  },
23991
25071
  "data": {
23992
- "label": "Daily at 08:00",
25072
+ "label": "Daily at 07:00",
23993
25073
  "triggerType": "cron",
23994
25074
  "config": {
23995
- "expression": "0 8 * * *"
25075
+ "expression": "0 7 * * *"
23996
25076
  }
23997
25077
  }
23998
25078
  },
@@ -24014,7 +25094,7 @@ const DATA = {
24014
25094
  }
24015
25095
  },
24016
25096
  {
24017
- "id": "clients-loop",
25097
+ "id": "handoff-loop",
24018
25098
  "type": "loop",
24019
25099
  "position": {
24020
25100
  "x": 0,
@@ -24027,175 +25107,223 @@ const DATA = {
24027
25107
  }
24028
25108
  },
24029
25109
  {
24030
- "id": "read-comms",
24031
- "type": "messages_query",
25110
+ "id": "read-client-facets",
25111
+ "type": "capability",
24032
25112
  "position": {
24033
25113
  "x": 0,
24034
25114
  "y": 360
24035
25115
  },
24036
25116
  "data": {
24037
- "label": "Read this client's recent comms",
24038
- "subjectEntityId": "{{loop.item.id}}",
24039
- "limit": 40
25117
+ "label": "Read this party's role facets",
25118
+ "verbId": "entity_facet.list",
25119
+ "inputMapping": {
25120
+ "entityId": "{{loop.item.id}}"
25121
+ }
24040
25122
  }
24041
25123
  },
24042
25124
  {
24043
- "id": "digest",
24044
- "type": "capability",
25125
+ "id": "client-handoff-status",
25126
+ "type": "transform",
24045
25127
  "position": {
24046
25128
  "x": 0,
24047
25129
  "y": 480
24048
25130
  },
24049
25131
  "data": {
24050
- "label": "Distil the real signal → JSON",
24051
- "verbId": "ai.generate",
24052
- "inputMapping": {
24053
- "json": "true",
24054
- "maxTokens": "1200",
24055
- "system": "You advise a client-delivery team. You read one client's recent inbound messages and surface only what genuinely matters. You are firewall-aware: your output is for the INTERNAL team, never the client.",
24056
- "prompt": "Digest the recent communications for the client \"{{loop.item.title}}\" into a concise internal briefing for OUR delivery team.\nPEOPLE GUARD (do not break): distinguish the CLIENT's people from OUR team members who also appear in the thread. Never treat a teammate as a client contact, lead, or new person. Only real, client-side signal counts.\nSurface ONLY real signal: status changes, decisions, deals/opportunities, competitor mentions, new client-side leads/contacts, risks/blockers, and deadlines/commitments. Skip small talk and noise. Lead with what matters; keep it tight.\nRespond with STRICT JSON and NOTHING else: {\n \"hasSignal\": \"true\" or \"false\" (a STRING),\n \"headline\": \"<one-line summary; empty string when hasSignal is 'false'>\",\n \"summary\": \"<a short markdown digest for the team; empty string when 'false'>\",\n \"risks\": \"<risks/blockers as short markdown, or empty string>\",\n \"nextActions\": \"<recommended next actions as short markdown, or empty string>\"\n} Set hasSignal to \"false\" when there is no notable activity (no messages, or only chatter).\nRecent conversation (oldest first, JSON): {{steps.read-comms.output.messages}}"
24057
- }
25132
+ "label": "Read the client facet's handoff status",
25133
+ "expression": "{{steps.read-client-facets.output.facets}} | filter:item.profileSlug === client | map:{{item.properties.handoffStatus}}"
24058
25134
  }
24059
25135
  },
24060
25136
  {
24061
- "id": "signal-gate",
25137
+ "id": "handoff-gate",
24062
25138
  "type": "condition",
24063
25139
  "position": {
24064
25140
  "x": 0,
24065
25141
  "y": 600
24066
25142
  },
24067
25143
  "data": {
24068
- "label": "Only continue when there's real signal",
24069
- "expression": "steps.digest.output.hasSignal === true"
25144
+ "label": "Only clients the CRM marked ready or handed off",
25145
+ "expression": "steps.client-handoff-status.output.result contains-any ready,handed_off"
24070
25146
  }
24071
25147
  },
24072
25148
  {
24073
- "id": "write-briefing",
24074
- "type": "output",
25149
+ "id": "existing-engagements",
25150
+ "type": "query",
24075
25151
  "position": {
24076
25152
  "x": 0,
24077
25153
  "y": 720
24078
25154
  },
24079
25155
  "data": {
24080
- "label": "Save the client briefing",
25156
+ "label": "Does this client already have an engagement?",
25157
+ "profileSlug": "engagement",
25158
+ "filter": "{\"engagement-client\": \"{{loop.item.id}}\"}",
25159
+ "limit": 1
25160
+ }
25161
+ },
25162
+ {
25163
+ "id": "engagement-gate",
25164
+ "type": "condition",
25165
+ "position": {
25166
+ "x": 0,
25167
+ "y": 840
25168
+ },
25169
+ "data": {
25170
+ "label": "Only clients with no engagement yet",
25171
+ "expression": "steps.existing-engagements.output.count === 0"
25172
+ }
25173
+ },
25174
+ {
25175
+ "id": "handoff-time",
25176
+ "type": "compute",
25177
+ "position": {
25178
+ "x": 0,
25179
+ "y": 900
25180
+ },
25181
+ "data": {
25182
+ "label": "Record handoff time",
25183
+ "operation": "now"
25184
+ }
25185
+ },
25186
+ {
25187
+ "id": "create-engagement",
25188
+ "type": "output",
25189
+ "position": {
25190
+ "x": 0,
25191
+ "y": 960
25192
+ },
25193
+ "data": {
25194
+ "label": "Open a proposed engagement",
24081
25195
  "outputType": "entity_create",
24082
25196
  "config": {
24083
- "profileSlug": "client-briefing",
24084
- "title": "Briefing — {{loop.item.title}}",
24085
- "dedupeBy": "briefing-key",
25197
+ "profileSlug": "engagement",
25198
+ "title": "Engagement — {{loop.item.title}}",
25199
+ "dedupeBy": "engagement-client",
24086
25200
  "properties": {
24087
- "briefing-key": "{{loop.item.id}}::{{trigger.payload.scheduledAt}}",
24088
- "briefing-client": "{{loop.item.id}}",
24089
- "briefing-date": "{{trigger.payload.scheduledAt}}",
24090
- "briefing-headline": "{{steps.digest.output.headline}}",
24091
- "briefing-summary": "{{steps.digest.output.summary}}",
24092
- "briefing-risks": "{{steps.digest.output.risks}}",
24093
- "briefing-next-actions": "{{steps.digest.output.nextActions}}",
24094
- "briefing-message-count": "{{steps.read-comms.output.count}}"
25201
+ "engagement-status": "proposed",
25202
+ "engagement-client": "{{loop.item.id}}",
25203
+ "engagement-start": "{{steps.handoff-time.output.result}}",
25204
+ "engagement-notes": "Opened automatically from the CRM handoff. Confirm scope, dates, and owner before activating."
24095
25205
  }
24096
25206
  }
24097
25207
  }
24098
25208
  },
24099
25209
  {
24100
- "id": "link-briefing",
25210
+ "id": "link-engagement",
24101
25211
  "type": "output",
24102
25212
  "position": {
24103
25213
  "x": 0,
24104
- "y": 840
25214
+ "y": 1080
24105
25215
  },
24106
25216
  "data": {
24107
- "label": "Link briefing → client",
25217
+ "label": "Link engagement → client",
24108
25218
  "outputType": "relation_create",
24109
25219
  "config": {
24110
- "fromEntityId": "{{steps.write-briefing.output.entityId}}",
25220
+ "fromEntityId": "{{steps.create-engagement.output.entityId}}",
24111
25221
  "toEntityId": "{{loop.item.id}}",
24112
- "relationType": "briefing_about_client"
25222
+ "relationType": "engagement_for_client"
24113
25223
  }
24114
25224
  }
24115
25225
  },
24116
25226
  {
24117
- "id": "resolve-team",
24118
- "type": "capability",
25227
+ "id": "create-contract",
25228
+ "type": "output",
24119
25229
  "position": {
24120
25230
  "x": 0,
24121
- "y": 960
25231
+ "y": 1200
24122
25232
  },
24123
25233
  "data": {
24124
- "label": "Resolve the client's TEAM channel (FIREWALL)",
24125
- "verbId": "channel.resolve",
24126
- "inputMapping": {
24127
- "contextObjectType": "entity",
24128
- "contextObjectId": "{{loop.item.id}}",
24129
- "branchPurpose": "team"
25234
+ "label": "Open a draft contract",
25235
+ "outputType": "entity_create",
25236
+ "config": {
25237
+ "profileSlug": "contract",
25238
+ "title": "Contract — {{loop.item.title}}",
25239
+ "dedupeBy": "contract-engagement",
25240
+ "properties": {
25241
+ "contract-status": "draft",
25242
+ "contract-engagement": "{{steps.create-engagement.output.entityId}}",
25243
+ "start-date": "{{steps.handoff-time.output.result}}",
25244
+ "contract-notes": "Drafted automatically from the CRM handoff. Attach the signed document and set the term."
25245
+ }
24130
25246
  }
24131
25247
  }
24132
25248
  },
24133
25249
  {
24134
- "id": "post-recap",
25250
+ "id": "link-contract",
24135
25251
  "type": "output",
24136
25252
  "position": {
24137
25253
  "x": 0,
24138
- "y": 1080
25254
+ "y": 1320
24139
25255
  },
24140
25256
  "data": {
24141
- "label": "Post the recap to the TEAM channel (FIREWALL)",
24142
- "outputType": "channel_message",
25257
+ "label": "Link contract → engagement",
25258
+ "outputType": "relation_create",
24143
25259
  "config": {
24144
- "channelId": "{{steps.resolve-team.output.channelId}}",
24145
- "content": "**{{loop.item.title}} — daily briefing**\n{{steps.digest.output.headline}}\n{{steps.digest.output.summary}}\n**Risks:** {{steps.digest.output.risks}}\n**Next:** {{steps.digest.output.nextActions}}",
24146
- "metadata": {
24147
- "automationMessage": true,
24148
- "kind": "client-briefing"
24149
- }
25260
+ "fromEntityId": "{{steps.create-contract.output.entityId}}",
25261
+ "toEntityId": "{{steps.create-engagement.output.entityId}}",
25262
+ "relationType": "contract_for_engagement"
24150
25263
  }
24151
25264
  }
24152
25265
  }
24153
25266
  ],
24154
25267
  "edges": [
24155
25268
  {
24156
- "id": "e-trigger-list",
25269
+ "id": "h-trigger-list",
24157
25270
  "source": "trigger",
24158
25271
  "target": "list-clients"
24159
25272
  },
24160
25273
  {
24161
- "id": "e-list-loop",
25274
+ "id": "h-list-time",
24162
25275
  "source": "list-clients",
24163
- "target": "clients-loop"
25276
+ "target": "handoff-time"
24164
25277
  },
24165
25278
  {
24166
- "id": "e-loop-read",
24167
- "source": "clients-loop",
24168
- "target": "read-comms"
25279
+ "id": "h-time-loop",
25280
+ "source": "handoff-time",
25281
+ "target": "handoff-loop"
24169
25282
  },
24170
25283
  {
24171
- "id": "e-read-digest",
24172
- "source": "read-comms",
24173
- "target": "digest"
25284
+ "id": "h-loop-facets",
25285
+ "source": "handoff-loop",
25286
+ "target": "read-client-facets"
24174
25287
  },
24175
25288
  {
24176
- "id": "e-digest-gate",
24177
- "source": "digest",
24178
- "target": "signal-gate"
25289
+ "id": "h-facets-status",
25290
+ "source": "read-client-facets",
25291
+ "target": "client-handoff-status"
24179
25292
  },
24180
25293
  {
24181
- "id": "e-gate-write",
24182
- "source": "signal-gate",
24183
- "target": "write-briefing"
25294
+ "id": "h-status-gate",
25295
+ "source": "client-handoff-status",
25296
+ "target": "handoff-gate"
24184
25297
  },
24185
25298
  {
24186
- "id": "e-write-link",
24187
- "source": "write-briefing",
24188
- "target": "link-briefing"
25299
+ "id": "h-gate-existing",
25300
+ "source": "handoff-gate",
25301
+ "target": "existing-engagements"
24189
25302
  },
24190
25303
  {
24191
- "id": "e-link-resolve",
24192
- "source": "link-briefing",
24193
- "target": "resolve-team"
25304
+ "id": "h-existing-gate",
25305
+ "source": "existing-engagements",
25306
+ "target": "engagement-gate"
24194
25307
  },
24195
25308
  {
24196
- "id": "e-resolve-post",
24197
- "source": "resolve-team",
24198
- "target": "post-recap"
25309
+ "id": "h-gate-engagement",
25310
+ "source": "engagement-gate",
25311
+ "target": "create-engagement"
25312
+ },
25313
+ {
25314
+ "id": "h-engagement-link",
25315
+ "source": "create-engagement",
25316
+ "target": "link-engagement"
25317
+ },
25318
+ {
25319
+ "id": "h-link-contract",
25320
+ "source": "link-engagement",
25321
+ "target": "create-contract"
25322
+ },
25323
+ {
25324
+ "id": "h-contract-link",
25325
+ "source": "create-contract",
25326
+ "target": "link-contract"
24199
25327
  }
24200
25328
  ]
24201
25329
  }
@@ -24509,6 +25637,164 @@ const DATA = {
24509
25637
  "status": "active"
24510
25638
  }
24511
25639
  ],
25640
+ "automations": [
25641
+ {
25642
+ "key": "linkedin-conversation-ingest",
25643
+ "name": "LinkedIn conversation ingest",
25644
+ "description": "Pull LinkedIn conversations via Unipile and record each thread as an external channel + its messages through channel.ingest (idempotent, so re-runs are safe). Cron = low-frequency reconciliation; manual trigger = on-demand backfill.",
25645
+ "triggerType": "cron",
25646
+ "triggerConfig": {
25647
+ "expression": "0 6 * * *"
25648
+ },
25649
+ "status": "draft",
25650
+ "flow": {
25651
+ "nodes": [
25652
+ {
25653
+ "id": "trigger",
25654
+ "type": "trigger",
25655
+ "position": {
25656
+ "x": 0,
25657
+ "y": 0
25658
+ },
25659
+ "data": {
25660
+ "label": "Daily 06:00 reconciliation (also fired manually for backfill)",
25661
+ "triggerType": "cron",
25662
+ "config": {
25663
+ "expression": "0 6 * * *"
25664
+ }
25665
+ }
25666
+ },
25667
+ {
25668
+ "id": "list-accounts",
25669
+ "type": "capability",
25670
+ "position": {
25671
+ "x": 0,
25672
+ "y": 120
25673
+ },
25674
+ "data": {
25675
+ "label": "Resolve the connected Unipile account",
25676
+ "verbId": "unipile_list_accounts"
25677
+ }
25678
+ },
25679
+ {
25680
+ "id": "filter-linkedin-account",
25681
+ "type": "transform",
25682
+ "position": {
25683
+ "x": 0,
25684
+ "y": 180
25685
+ },
25686
+ "data": {
25687
+ "label": "Keep only LinkedIn Unipile accounts",
25688
+ "expression": "{{steps.list-accounts.output.accounts}} | filter:item.type == 'LINKEDIN'"
25689
+ }
25690
+ },
25691
+ {
25692
+ "id": "list-conversations",
25693
+ "type": "capability",
25694
+ "position": {
25695
+ "x": 0,
25696
+ "y": 240
25697
+ },
25698
+ "data": {
25699
+ "label": "List LinkedIn conversations for the account",
25700
+ "verbId": "unipile_list_conversations",
25701
+ "inputMapping": {
25702
+ "accountId": "{{steps.filter-linkedin-account.output.result.0.id}}"
25703
+ }
25704
+ }
25705
+ },
25706
+ {
25707
+ "id": "conversations-loop",
25708
+ "type": "loop",
25709
+ "position": {
25710
+ "x": 0,
25711
+ "y": 360
25712
+ },
25713
+ "data": {
25714
+ "label": "For each conversation",
25715
+ "iteratorExpression": "steps.list-conversations.output.conversations",
25716
+ "itemVariable": "conversation"
25717
+ }
25718
+ },
25719
+ {
25720
+ "id": "list-messages",
25721
+ "type": "capability",
25722
+ "position": {
25723
+ "x": 0,
25724
+ "y": 480
25725
+ },
25726
+ "data": {
25727
+ "label": "List this thread's messages",
25728
+ "verbId": "unipile_list_messages",
25729
+ "inputMapping": {
25730
+ "threadId": "{{loop.item.id}}",
25731
+ "accountId": "{{steps.filter-linkedin-account.output.result.0.id}}"
25732
+ }
25733
+ }
25734
+ },
25735
+ {
25736
+ "id": "ingest",
25737
+ "type": "capability",
25738
+ "position": {
25739
+ "x": 0,
25740
+ "y": 600
25741
+ },
25742
+ "data": {
25743
+ "label": "Ingest thread + messages (idempotent, generic)",
25744
+ "verbId": "channel.ingest",
25745
+ "inputMapping": {
25746
+ "provider": "linkedin",
25747
+ "externalId": "{{loop.item.id}}",
25748
+ "title": "{{loop.item.name}}",
25749
+ "accountExternalId": "{{steps.filter-linkedin-account.output.result.0.id}}",
25750
+ "workspaceId": "499328b7-fd35-4143-b1bc-67b9e45d9b4b",
25751
+ "suppressSideEffects": true,
25752
+ "messages": "{{steps.list-messages.output.messages}}",
25753
+ "messageMap": {
25754
+ "text": "text",
25755
+ "id": "id",
25756
+ "sentAt": "timestamp",
25757
+ "isOutbound": "is_sender"
25758
+ }
25759
+ }
25760
+ }
25761
+ }
25762
+ ],
25763
+ "edges": [
25764
+ {
25765
+ "id": "e-trigger-accounts",
25766
+ "source": "trigger",
25767
+ "target": "list-accounts"
25768
+ },
25769
+ {
25770
+ "id": "e-accounts-filter",
25771
+ "source": "list-accounts",
25772
+ "target": "filter-linkedin-account"
25773
+ },
25774
+ {
25775
+ "id": "e-filter-convos",
25776
+ "source": "filter-linkedin-account",
25777
+ "target": "list-conversations"
25778
+ },
25779
+ {
25780
+ "id": "e-convos-loop",
25781
+ "source": "list-conversations",
25782
+ "target": "conversations-loop"
25783
+ },
25784
+ {
25785
+ "id": "e-loop-messages",
25786
+ "source": "conversations-loop",
25787
+ "target": "list-messages"
25788
+ },
25789
+ {
25790
+ "id": "e-messages-ingest",
25791
+ "source": "list-messages",
25792
+ "target": "ingest"
25793
+ }
25794
+ ]
25795
+ }
25796
+ }
25797
+ ],
24512
25798
  "integrations": [
24513
25799
  {
24514
25800
  "templateKey": "unipile-linkedin"
@@ -25074,7 +26360,7 @@ const DATA = {
25074
26360
  "icon": "user",
25075
26361
  "color": "#0891B2",
25076
26362
  "description": "Team member or stakeholder",
25077
- "scope": "WORKSPACE",
26363
+ "entityScope": "pod",
25078
26364
  "properties": [
25079
26365
  {
25080
26366
  "slug": "person-role",