@hailer/mcp 1.1.11 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +12 -1
- package/dist/bot/bot-config.js +98 -14
- package/dist/bot/bot-manager.d.ts +13 -3
- package/dist/bot/bot-manager.js +80 -25
- package/dist/bot/bot.d.ts +46 -0
- package/dist/bot/bot.js +542 -166
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +157 -20
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +12 -12
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +6 -0
- package/dist/mcp/webhook-handler.js +11 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -111
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/package.json +2 -1
- package/.claude/.context-watchdog.json +0 -1
- package/.claude/.session-checked +0 -1
- package/.claude/CLAUDE.md +0 -370
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.hailer-mcp-port +0 -1
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -204
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -21
- package/inbox/failures.log +0 -1
- package/inbox/usage.jsonl +0 -4
- package/scripts/postinstall.cjs +0 -64
- package/scripts/test-hal-tools.ts +0 -154
|
@@ -1,1139 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: SDK-ws-config-skill
|
|
3
|
-
description: Comprehensive workspace configuration - workflows, fields, phases
|
|
4
|
-
version: 1.7.1
|
|
5
|
-
triggers: Create workflow, add field, configure phase, field visibility, phase transitions
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Workspace Configuration
|
|
9
|
-
|
|
10
|
-
## File Structure
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
workspace/
|
|
14
|
-
├── workflows.ts ← Workflow registry
|
|
15
|
-
├── enums.ts ← AUTO-GENERATED (never edit)
|
|
16
|
-
├── teams.ts, groups.ts ← Access control
|
|
17
|
-
├── insights.ts ← SQL-like reports
|
|
18
|
-
└── [Workflow]_[id]/
|
|
19
|
-
├── main.ts ← Workflow metadata
|
|
20
|
-
├── fields.ts ← Field definitions
|
|
21
|
-
├── phases.ts ← Phase configuration
|
|
22
|
-
├── templates.ts ← Document templates registry
|
|
23
|
-
└── functions/*.ts ← Calculated field code
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## Workflows
|
|
29
|
-
|
|
30
|
-
### workflows.ts
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
export const workflows: WorkflowEntry[] = [
|
|
34
|
-
// NEW workflow - omit _id and folder
|
|
35
|
-
{
|
|
36
|
-
name: "Tasks",
|
|
37
|
-
enableUnlinkedMode: false
|
|
38
|
-
},
|
|
39
|
-
// EXISTING workflow - has _id and folder
|
|
40
|
-
{
|
|
41
|
-
_id: "682ac815fba468d857d498f7",
|
|
42
|
-
name: "Tasks",
|
|
43
|
-
enableUnlinkedMode: false,
|
|
44
|
-
folder: "tasks_682ac815fba468d857d498f7"
|
|
45
|
-
}
|
|
46
|
-
];
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
| Property | Description |
|
|
50
|
-
|----------|-------------|
|
|
51
|
-
| `name` | Display name |
|
|
52
|
-
| `enableUnlinkedMode` | `true` = standalone items, `false` = must link to other activities |
|
|
53
|
-
| `_id` | Server-generated (omit for new) |
|
|
54
|
-
| `folder` | Auto-generated on pull (omit for new) |
|
|
55
|
-
|
|
56
|
-
<critical-rules>
|
|
57
|
-
## CRITICAL: Workflows Require a Default Team
|
|
58
|
-
|
|
59
|
-
**This is the #1 cause of "can't create activities" errors.**
|
|
60
|
-
|
|
61
|
-
When you create a new workflow, it has NO default team. Without a default team:
|
|
62
|
-
- `mcp__hailer__create_activity` fails
|
|
63
|
-
- Apps using `hailer.activity.create()` fail
|
|
64
|
-
- You get confusing permission/validation errors
|
|
65
|
-
|
|
66
|
-
**Solution:** Always set a default team immediately after creating a workflow:
|
|
67
|
-
|
|
68
|
-
1. Create the workflow
|
|
69
|
-
2. In Hailer UI: Workflow Settings → Default Team → Select any team
|
|
70
|
-
3. Or via SDK: Add `defaultTeam: TeamIds.your_team` to workflow config
|
|
71
|
-
|
|
72
|
-
**For development/testing:** Any team works. Just pick one so activity creation functions properly.
|
|
73
|
-
</critical-rules>
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### Workflow Creation Flow
|
|
78
|
-
|
|
79
|
-
**Order matters: Workflow → Phases → Fields → Phase Links → Workflow Settings**
|
|
80
|
-
|
|
81
|
-
All items get server-generated IDs. You must push each step and pull to get the IDs before referring to them.
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
1. npm run pull
|
|
85
|
-
2. Edit workflows.ts (add new entry without _id/folder)
|
|
86
|
-
3. Return ["npm run workflows-sync:force"]
|
|
87
|
-
4. npm run pull (generates folder + enums with workflow ID)
|
|
88
|
-
|
|
89
|
-
5. Edit phases.ts (add phases without _id, empty fields array)
|
|
90
|
-
6. Return ["npm run phases-push:force"]
|
|
91
|
-
7. npm run pull (generates phase IDs in enums)
|
|
92
|
-
|
|
93
|
-
8. Edit fields.ts (add fields without _id)
|
|
94
|
-
9. Return ["npm run fields-push:force"]
|
|
95
|
-
10. npm run pull (generates field IDs in enums)
|
|
96
|
-
|
|
97
|
-
11. Edit phases.ts:
|
|
98
|
-
- Add field IDs to phase.fields arrays
|
|
99
|
-
- Add possibleNextPhase arrays (phase transition links)
|
|
100
|
-
12. Return ["npm run phases-push:force"]
|
|
101
|
-
|
|
102
|
-
13. Edit main.ts: Set primaryDateField and primaryNumberField
|
|
103
|
-
14. Return ["npm run push:force"]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Workflow Permissions (main.ts)
|
|
107
|
-
|
|
108
|
-
Control who can access the workflow and what they can do.
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
// In main.ts
|
|
112
|
-
export const workflowConfig = {
|
|
113
|
-
_id: WorkflowIds.tasks_abc,
|
|
114
|
-
name: "Tasks",
|
|
115
|
-
|
|
116
|
-
// WHO CAN ACCESS THIS WORKFLOW
|
|
117
|
-
members: [
|
|
118
|
-
{
|
|
119
|
-
id: HailerMembers.timo_ahonen_94d, // User ID
|
|
120
|
-
permissions: ["admin"] // Full control
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
id: WorkspaceTeams.sales_team_fc0, // Team ID
|
|
124
|
-
permissions: ["any"] // Standard access
|
|
125
|
-
}
|
|
126
|
-
],
|
|
127
|
-
|
|
128
|
-
// DEFAULT TEAM for new activities
|
|
129
|
-
preselectedTeam: {
|
|
130
|
-
account: "workspace_account_id",
|
|
131
|
-
team: WorkspaceTeams.default_team_abc
|
|
132
|
-
},
|
|
133
|
-
enablePreselectedTeam: true,
|
|
134
|
-
|
|
135
|
-
// GUEST ACCESS
|
|
136
|
-
allowGuests: false, // Allow external guests
|
|
137
|
-
enableGuestEditing: false, // Can guests edit?
|
|
138
|
-
|
|
139
|
-
// ... other config
|
|
140
|
-
};
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
**Permission levels:**
|
|
144
|
-
| Level | Meaning |
|
|
145
|
-
|-------|---------|
|
|
146
|
-
| `"admin"` | Full control - can edit workflow settings, delete activities |
|
|
147
|
-
| `"any"` | Standard access - can view, create, edit based on phase settings |
|
|
148
|
-
|
|
149
|
-
**Member types:**
|
|
150
|
-
- Users: `HailerMembers.user_name_id`
|
|
151
|
-
- Teams: `WorkspaceTeams.team_name_id`
|
|
152
|
-
|
|
153
|
-
### Phase Permissions (phases.ts)
|
|
154
|
-
|
|
155
|
-
Control visibility and behavior per phase.
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
// In phases.ts
|
|
159
|
-
export const phases = [
|
|
160
|
-
{
|
|
161
|
-
_id: PhaseIds.in_progress_def,
|
|
162
|
-
name: "In Progress",
|
|
163
|
-
|
|
164
|
-
// FIELD VISIBILITY - only these fields visible in this phase
|
|
165
|
-
fields: [
|
|
166
|
-
FieldIds.title_abc,
|
|
167
|
-
FieldIds.assignee_def,
|
|
168
|
-
FieldIds.due_date_ghi
|
|
169
|
-
],
|
|
170
|
-
|
|
171
|
-
// AUTO-FOLLOWERS - notified when activity enters this phase
|
|
172
|
-
followers: [
|
|
173
|
-
WorkspaceTeams.managers_team_abc, // Team
|
|
174
|
-
"user_id_123" // Or specific user
|
|
175
|
-
],
|
|
176
|
-
|
|
177
|
-
// PHASE TRANSITIONS - where activities can move TO from here
|
|
178
|
-
possibleNextPhase: [
|
|
179
|
-
PhaseIds.review_ghi,
|
|
180
|
-
PhaseIds.done_jkl
|
|
181
|
-
],
|
|
182
|
-
|
|
183
|
-
// TRANSITION SETTINGS (optional)
|
|
184
|
-
possibleNextPhaseSettings: {
|
|
185
|
-
[PhaseIds.done_jkl]: {
|
|
186
|
-
requireComment: true // Must add comment when moving to Done
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
// WEBHOOKS - trigger external systems
|
|
191
|
-
webhooksEnabled: true,
|
|
192
|
-
webhookUrl: "https://hooks.example.com/phase-entered"
|
|
193
|
-
}
|
|
194
|
-
];
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
**Key phase permissions:**
|
|
198
|
-
| Setting | Purpose |
|
|
199
|
-
|---------|---------|
|
|
200
|
-
| `fields[]` | Which fields are visible/editable in this phase |
|
|
201
|
-
| `followers[]` | Users/teams auto-notified for activities in this phase |
|
|
202
|
-
| `possibleNextPhase[]` | Allowed phase transitions (empty = cannot move out) |
|
|
203
|
-
| `isInitial` | New activities start here |
|
|
204
|
-
| `isEndpoint` | Activities here are "complete" |
|
|
205
|
-
|
|
206
|
-
### Teams & Groups (workspace root)
|
|
207
|
-
|
|
208
|
-
Teams and groups are defined at workspace root level (not per-workflow).
|
|
209
|
-
|
|
210
|
-
**Location:** `workspace/teams.ts` and `workspace/groups.ts`
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
// workspace/teams.ts
|
|
214
|
-
import { WorkspaceMembers, WorkspaceTeams } from "./enums";
|
|
215
|
-
|
|
216
|
-
export const teams: HailerTeamUpdatePayload[] = [
|
|
217
|
-
{
|
|
218
|
-
_id: WorkspaceTeams.sales_team_abc,
|
|
219
|
-
name: "Sales Team",
|
|
220
|
-
description: "Sales department members",
|
|
221
|
-
members: [
|
|
222
|
-
WorkspaceMembers.john_doe_123,
|
|
223
|
-
WorkspaceMembers.jane_smith_456
|
|
224
|
-
],
|
|
225
|
-
public: false, // Only members can see team activities
|
|
226
|
-
defaultView: {
|
|
227
|
-
type: "app", // "app" | "workflow" | "phase"
|
|
228
|
-
value: "app_id_here" // ID of default view
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
];
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
**Team properties:**
|
|
235
|
-
| Property | Purpose |
|
|
236
|
-
|----------|---------|
|
|
237
|
-
| `_id` | Team ID (from enums after first push) |
|
|
238
|
-
| `name` | Display name |
|
|
239
|
-
| `description` | Team description |
|
|
240
|
-
| `members[]` | Array of user IDs (WorkspaceMembers enum) |
|
|
241
|
-
| `public` | If true, visible to all workspace users |
|
|
242
|
-
| `defaultView` | What opens when team is selected |
|
|
243
|
-
|
|
244
|
-
**Groups** (less common) are collections of teams:
|
|
245
|
-
```typescript
|
|
246
|
-
// workspace/groups.ts
|
|
247
|
-
export const groups: HailerGroupUpdatePayload[] = [
|
|
248
|
-
{
|
|
249
|
-
_id: GroupIds.all_managers_abc,
|
|
250
|
-
name: "All Managers",
|
|
251
|
-
teams: [TeamIds.sales_managers, TeamIds.tech_managers],
|
|
252
|
-
users: [WorkspaceMembers.ceo_123] // Direct user members
|
|
253
|
-
}
|
|
254
|
-
];
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**Push commands:**
|
|
258
|
-
```bash
|
|
259
|
-
npm run teams-push:force # Push team changes
|
|
260
|
-
npm run groups-push:force # Push group changes
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Where teams are used:**
|
|
264
|
-
- Workflow `members[]` - who can access workflow
|
|
265
|
-
- Workflow `preselectedTeam` - default team for new activities
|
|
266
|
-
- Phase `followers[]` - auto-notify teams
|
|
267
|
-
- App permissions - grant team access to apps
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
<critical-rules>
|
|
272
|
-
### CRITICAL: Phase Links (possibleNextPhase)
|
|
273
|
-
|
|
274
|
-
**Problem:** Without phase links, users cannot move activities between phases.
|
|
275
|
-
|
|
276
|
-
Every phase needs `possibleNextPhase` array defining allowed transitions:
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
{
|
|
280
|
-
_id: Tasks_PhaseIds.lead_abc,
|
|
281
|
-
name: "Lead",
|
|
282
|
-
isInitial: true,
|
|
283
|
-
possibleNextPhase: [
|
|
284
|
-
Tasks_PhaseIds.qualified_def,
|
|
285
|
-
Tasks_PhaseIds.lost_ghi
|
|
286
|
-
]
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**Without this:** Activities are stuck in their initial phase forever.
|
|
291
|
-
</critical-rules>
|
|
292
|
-
|
|
293
|
-
<critical-rules>
|
|
294
|
-
### CRITICAL: Primary Date and Number Fields
|
|
295
|
-
|
|
296
|
-
**Set in main.ts** to enable calendar view and number aggregation:
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
// workspace/[Workflow]_[id]/main.ts
|
|
300
|
-
export const workflowConfig = {
|
|
301
|
-
// ... other config
|
|
302
|
-
primaryDateField: Tasks_FieldIds.due_date_abc, // For calendar view
|
|
303
|
-
primaryNumberField: Tasks_FieldIds.amount_def, // For aggregations
|
|
304
|
-
};
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
**Without primaryDateField:** Activities won't appear on the calendar.
|
|
308
|
-
**Without primaryNumberField:** Number aggregations won't work in views.
|
|
309
|
-
</critical-rules>
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Fields
|
|
314
|
-
|
|
315
|
-
### Basic Field Structure
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
// In fields.ts
|
|
319
|
-
import { Tasks_FieldIds, WorkflowIds } from "../enums";
|
|
320
|
-
|
|
321
|
-
export const fields: HailerFieldGeneric[] = [
|
|
322
|
-
// NEW field - omit _id
|
|
323
|
-
{
|
|
324
|
-
label: "Due Date",
|
|
325
|
-
type: "date",
|
|
326
|
-
required: false
|
|
327
|
-
},
|
|
328
|
-
// EXISTING field - use enum for _id
|
|
329
|
-
{
|
|
330
|
-
_id: Tasks_FieldIds.due_date_abc,
|
|
331
|
-
label: "Due Date",
|
|
332
|
-
type: "date",
|
|
333
|
-
required: false
|
|
334
|
-
}
|
|
335
|
-
];
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Common Field Properties
|
|
339
|
-
|
|
340
|
-
| Property | Type | Description |
|
|
341
|
-
|----------|------|-------------|
|
|
342
|
-
| `_id` | string | Field ID (omit for new, use enum for existing) |
|
|
343
|
-
| `label` | string | Display name shown in UI |
|
|
344
|
-
| `type` | string | Field type (see below) |
|
|
345
|
-
| `key` | string | URL-safe identifier (see Key Naming below) |
|
|
346
|
-
| `description` | string | Help text shown below field in forms |
|
|
347
|
-
| `required` | boolean | Is field required |
|
|
348
|
-
| `defaultTo` | boolean | Use default value |
|
|
349
|
-
| `defaultValue` | string | Default value |
|
|
350
|
-
| `inviteToDiscussionOnChange` | boolean | Notify discussion on change |
|
|
351
|
-
|
|
352
|
-
### Field Key Naming
|
|
353
|
-
|
|
354
|
-
The `key` property is an optional URL-safe identifier for the field. If omitted, Hailer auto-generates one from the label.
|
|
355
|
-
|
|
356
|
-
**Conventions:**
|
|
357
|
-
- Use `snake_case` (lowercase with underscores)
|
|
358
|
-
- Keep it short but descriptive
|
|
359
|
-
- Use consistent prefixes for related fields
|
|
360
|
-
- Avoid reserved words
|
|
361
|
-
|
|
362
|
-
**Examples:**
|
|
363
|
-
```typescript
|
|
364
|
-
// Good keys
|
|
365
|
-
{ label: "Customer Name", key: "customer_name" }
|
|
366
|
-
{ label: "Invoice #", key: "invoice_number" }
|
|
367
|
-
{ label: "Due Date", key: "due_date" }
|
|
368
|
-
{ label: "Total (€)", key: "total_eur" }
|
|
369
|
-
|
|
370
|
-
// Prefixed for grouping
|
|
371
|
-
{ label: "Billing Address", key: "billing_address" }
|
|
372
|
-
{ label: "Billing City", key: "billing_city" }
|
|
373
|
-
{ label: "Shipping Address", key: "shipping_address" }
|
|
374
|
-
{ label: "Shipping City", key: "shipping_city" }
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
**When to specify `key`:**
|
|
378
|
-
- Always for fields used in integrations (APIs, Zapier, webhooks)
|
|
379
|
-
- Always for fields referenced in insights or functions
|
|
380
|
-
- Optional for internal-only fields (auto-generated is fine)
|
|
381
|
-
|
|
382
|
-
### Field Descriptions
|
|
383
|
-
|
|
384
|
-
The `description` property adds help text below the field input in Hailer forms.
|
|
385
|
-
|
|
386
|
-
**When to use:**
|
|
387
|
-
- Fields with non-obvious formats ("Enter date as DD.MM.YYYY")
|
|
388
|
-
- Fields requiring specific values ("Must be 8-digit code")
|
|
389
|
-
- Fields with business rules ("Leave empty to use default pricing")
|
|
390
|
-
- Fields that differ from similar-named fields in other workflows
|
|
391
|
-
|
|
392
|
-
**Examples:**
|
|
393
|
-
```typescript
|
|
394
|
-
{
|
|
395
|
-
label: "Project Code",
|
|
396
|
-
type: "text",
|
|
397
|
-
key: "project_code",
|
|
398
|
-
description: "8-character code from the ERP system (e.g., PRJ-12345)"
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
{
|
|
402
|
-
label: "Discount %",
|
|
403
|
-
type: "numeric",
|
|
404
|
-
key: "discount_pct",
|
|
405
|
-
description: "Enter 0-100. Applied to subtotal before VAT."
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Field Types Reference
|
|
410
|
-
|
|
411
|
-
Complete reference of all Hailer field types. Source: `hailer.d.ts` HailerFieldType.
|
|
412
|
-
|
|
413
|
-
#### Quick Reference Table
|
|
414
|
-
|
|
415
|
-
| Type | Use For | Value Format (CRUD) | Notes |
|
|
416
|
-
|------|---------|---------------------|-------|
|
|
417
|
-
| `text` | Short text (single line) | `"string"` | |
|
|
418
|
-
| `textarea` | Long text (multi-line) | `"string"` | Plain text |
|
|
419
|
-
| `textunit` | Text with unit suffix | `"string"` | Display unit |
|
|
420
|
-
| `numeric` | Numbers | `123` or `45.67` | |
|
|
421
|
-
| `numericunit` | Numbers with unit | `123` | Unit in display |
|
|
422
|
-
| `date` | Single date | `1730937600000` (ms) | Unix timestamp |
|
|
423
|
-
| `datetime` | Date + time | `1730937600000` (ms) | Unix timestamp |
|
|
424
|
-
| `daterange` | Date span | `{start, end}` | Both timestamps |
|
|
425
|
-
| `datetimerange` | Date+time span | `{start, end}` | Both timestamps |
|
|
426
|
-
| `time` | Time only | `1765863000000` (ms) | Timestamp, but only time shown (e.g., "17:00") |
|
|
427
|
-
| `timerange` | Time span | `{start, end}` | Timestamps, shown as "17:00 - 18:00" |
|
|
428
|
-
| `textpredefinedoptions` | Dropdown select | `"Option"` | **STRING not array!** See section below. |
|
|
429
|
-
| `users` | Single user | `"userId"` | STRING not array! |
|
|
430
|
-
| `teams` | Team | `"teamId"` | Team selector |
|
|
431
|
-
| `activitylink` | Link to activity | `"activityId"` | STRING not array! |
|
|
432
|
-
| `linkedfrom` | Backlink (read-only) | N/A | Auto-populated |
|
|
433
|
-
| `country` | Country picker | `"FI"` | ISO country code |
|
|
434
|
-
| `subheader` | Section divider | N/A | UI only, no data |
|
|
435
|
-
| `numeric` + `modifier.checkbox` | Checkbox/boolean | `1` or `0` | See Modified Fields |
|
|
436
|
-
| `text` + `modifier.file` | File/picture upload | Object | See Modified Fields |
|
|
437
|
-
|
|
438
|
-
**Note:** No multi-select types exist. For multiple values, use multiple fields or comma-separated text.
|
|
439
|
-
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
#### Text Fields
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
// Short text (single line)
|
|
446
|
-
{ type: "text", label: "Title" }
|
|
447
|
-
|
|
448
|
-
// Long text (multi-line, plain)
|
|
449
|
-
{ type: "textarea", label: "Description" }
|
|
450
|
-
|
|
451
|
-
// Text with unit suffix (e.g., "100 pcs")
|
|
452
|
-
{ type: "textunit", label: "Quantity Text", unit: "pcs" }
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
**When to use:**
|
|
456
|
-
- `text` - Names, titles, short answers
|
|
457
|
-
- `textarea` - Descriptions, comments, notes
|
|
458
|
-
- `textunit` - Text that needs a unit suffix displayed
|
|
459
|
-
|
|
460
|
-
---
|
|
461
|
-
|
|
462
|
-
#### Number Fields
|
|
463
|
-
|
|
464
|
-
```typescript
|
|
465
|
-
// Plain number
|
|
466
|
-
{ type: "numeric", label: "Quantity" }
|
|
467
|
-
|
|
468
|
-
// Number with display unit
|
|
469
|
-
{ type: "numericunit", label: "Price", unit: "€" }
|
|
470
|
-
{ type: "numericunit", label: "Weight", unit: "kg" }
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
**CRITICAL:** Type is `numeric`, NOT `number`!
|
|
474
|
-
|
|
475
|
-
**When to use:**
|
|
476
|
-
- `numeric` - Quantities, counts, raw numbers
|
|
477
|
-
- `numericunit` - Prices, measurements, amounts with units
|
|
478
|
-
|
|
479
|
-
---
|
|
480
|
-
|
|
481
|
-
#### Date/Time Fields
|
|
482
|
-
|
|
483
|
-
```typescript
|
|
484
|
-
// Single date (no time)
|
|
485
|
-
{ type: "date", label: "Due Date" }
|
|
486
|
-
|
|
487
|
-
// Date with time
|
|
488
|
-
{ type: "datetime", label: "Meeting Start" }
|
|
489
|
-
|
|
490
|
-
// Date range (start + end dates)
|
|
491
|
-
{ type: "daterange", label: "Project Period" }
|
|
492
|
-
|
|
493
|
-
// Date+time range
|
|
494
|
-
{ type: "datetimerange", label: "Event" }
|
|
495
|
-
|
|
496
|
-
// Time only (no date)
|
|
497
|
-
{ type: "time", label: "Start Time" }
|
|
498
|
-
|
|
499
|
-
// Time range
|
|
500
|
-
{ type: "timerange", label: "Working Hours" }
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
**Value format (CRUD):**
|
|
504
|
-
- `date`, `datetime`: Unix timestamp in milliseconds → `1730937600000`
|
|
505
|
-
- `daterange`, `datetimerange`: `{ start: 1730937600000, end: 1731024000000 }`
|
|
506
|
-
- `time`: Unix timestamp (ms) → `1765863000000` (date arbitrary, only time displayed as "09:30")
|
|
507
|
-
- `timerange`: `{ start: 1765863000000, end: 1765892400000 }` (shown as "09:30 - 17:30")
|
|
508
|
-
|
|
509
|
-
**Time field note:** Stored as full timestamp but UI only shows time. Extract time portion when displaying:
|
|
510
|
-
```javascript
|
|
511
|
-
const date = new Date(timeValue);
|
|
512
|
-
const hours = date.getUTCHours();
|
|
513
|
-
const minutes = date.getUTCMinutes();
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
**When to use:**
|
|
517
|
-
- `date` - Due dates, deadlines, birthdays
|
|
518
|
-
- `datetime` - Meetings, appointments with specific time
|
|
519
|
-
- `daterange` - Projects, events, bookings with duration
|
|
520
|
-
- `datetimerange` - Events with specific start/end times
|
|
521
|
-
- `time` - Daily schedules, opening hours
|
|
522
|
-
- `timerange` - Shifts, time slots
|
|
523
|
-
|
|
524
|
-
---
|
|
525
|
-
|
|
526
|
-
#### Selection Fields
|
|
527
|
-
|
|
528
|
-
```typescript
|
|
529
|
-
// Dropdown - MUST use "textpredefinedoptions"
|
|
530
|
-
{
|
|
531
|
-
type: "textpredefinedoptions",
|
|
532
|
-
label: "Priority",
|
|
533
|
-
data: ["High", "Medium", "Low"] // String array only!
|
|
534
|
-
}
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
**CRITICAL FORMAT RULES:**
|
|
538
|
-
- Type: `textpredefinedoptions` (NOT `dropdown`, `predefinedoptions`, `select`)
|
|
539
|
-
- Property: `data` (NOT `options`)
|
|
540
|
-
- Format: `["A", "B"]` (NOT `[{label: "A", value: "a"}]`)
|
|
541
|
-
- **No multi-select exists** - use multiple fields if needed
|
|
542
|
-
|
|
543
|
-
**Value format (CRUD):** `"High"` (STRING, not array!)
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
// ✅ CORRECT
|
|
547
|
-
{ type: "textpredefinedoptions", data: ["High", "Medium", "Low"] }
|
|
548
|
-
|
|
549
|
-
// ❌ WRONG
|
|
550
|
-
{ type: "dropdown", ... } // Wrong type name
|
|
551
|
-
{ type: "predefinedoptions", ... } // Missing "text" prefix
|
|
552
|
-
{ options: ["A", "B"] } // Use data, not options
|
|
553
|
-
{ data: [{label: "A", value: "a"}] } // Use strings only
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
---
|
|
557
|
-
|
|
558
|
-
#### User & Team Fields
|
|
559
|
-
|
|
560
|
-
```typescript
|
|
561
|
-
// Single user
|
|
562
|
-
{
|
|
563
|
-
type: "users",
|
|
564
|
-
label: "Assignee",
|
|
565
|
-
inviteToDiscussionOnChange: true // Auto-invite when assigned
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Team
|
|
569
|
-
{
|
|
570
|
-
type: "teams",
|
|
571
|
-
label: "Responsible Team"
|
|
572
|
-
}
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
**Value format (CRUD):**
|
|
576
|
-
- `users`: `"5f8a1b2c3d4e5f6a7b8c9d0e"` (STRING user ID)
|
|
577
|
-
- `teams`: `"teamId"` (STRING team ID)
|
|
578
|
-
|
|
579
|
-
**Note:** No multi-select for users/teams. Use multiple fields if needed.
|
|
580
|
-
|
|
581
|
-
---
|
|
582
|
-
|
|
583
|
-
#### Link Fields
|
|
584
|
-
|
|
585
|
-
```typescript
|
|
586
|
-
// Activity link (THIS → other activity)
|
|
587
|
-
{
|
|
588
|
-
type: "activitylink",
|
|
589
|
-
label: "Customer",
|
|
590
|
-
data: [WorkflowIds.customers_abc] // Plain string array!
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// Multiple workflow link (can link to different workflow types)
|
|
594
|
-
{
|
|
595
|
-
type: "activitylink",
|
|
596
|
-
label: "Related Item",
|
|
597
|
-
data: [WorkflowIds.customers, WorkflowIds.projects]
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Linked from (backlink - others → THIS) - READ ONLY
|
|
601
|
-
{
|
|
602
|
-
type: "linkedfrom",
|
|
603
|
-
label: "Orders",
|
|
604
|
-
data: [WorkflowIds.orders_def],
|
|
605
|
-
modifier: {
|
|
606
|
-
quickAdd: {
|
|
607
|
-
fieldIds: [], // Fields in quick-add form
|
|
608
|
-
targetFieldId: Orders_FieldIds.customer_link
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
**CRITICAL FORMAT RULES:**
|
|
615
|
-
```typescript
|
|
616
|
-
// ✅ CORRECT - plain string array
|
|
617
|
-
data: ["697b9b54477b7e412ee08b9d"]
|
|
618
|
-
data: [WorkflowIds.customers_abc]
|
|
619
|
-
|
|
620
|
-
// ❌ WRONG - objects (causes API error)
|
|
621
|
-
data: [{ workflowId: "697b9b54477b7e412ee08b9d" }]
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
**Value format (CRUD):**
|
|
625
|
-
- `activitylink`: `"692abc123def456"` (STRING activity ID, not array!)
|
|
626
|
-
|
|
627
|
-
**When to use:**
|
|
628
|
-
- `activitylink` - Customer on order, project on task, parent-child relations
|
|
629
|
-
- `linkedfrom` - Show related items (read-only, auto-populated from links)
|
|
630
|
-
|
|
631
|
-
---
|
|
632
|
-
|
|
633
|
-
#### Modified Fields (Checkbox & File Upload)
|
|
634
|
-
|
|
635
|
-
Fields can have a `modifier` object that changes their behavior:
|
|
636
|
-
|
|
637
|
-
```typescript
|
|
638
|
-
// Checkbox field = numeric + modifier.checkbox: true
|
|
639
|
-
{
|
|
640
|
-
label: "Is Active",
|
|
641
|
-
type: "numeric",
|
|
642
|
-
modifier: {
|
|
643
|
-
checkbox: true,
|
|
644
|
-
file: false
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// File/Picture upload = text + modifier.file: true
|
|
649
|
-
{
|
|
650
|
-
label: "Attachments",
|
|
651
|
-
type: "text",
|
|
652
|
-
modifier: {
|
|
653
|
-
checkbox: false,
|
|
654
|
-
file: true
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
**Value format (CRUD):**
|
|
660
|
-
- Checkbox: `1` (true) or `0` (false) - stored as number
|
|
661
|
-
- File: File reference object (handled by Hailer UI)
|
|
662
|
-
|
|
663
|
-
**When to use:**
|
|
664
|
-
- `modifier.checkbox: true` - Boolean yes/no, toggles
|
|
665
|
-
- `modifier.file: true` - File uploads, images, attachments
|
|
666
|
-
|
|
667
|
-
---
|
|
668
|
-
|
|
669
|
-
#### Other Fields
|
|
670
|
-
|
|
671
|
-
```typescript
|
|
672
|
-
// Country picker
|
|
673
|
-
{ type: "country", label: "Country" }
|
|
674
|
-
|
|
675
|
-
// UI organization (section divider)
|
|
676
|
-
{ type: "subheader", label: "Financial Details", collapsedByDefault: true }
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
**Value format (CRUD):**
|
|
680
|
-
- `country`: ISO country code → `"FI"`, `"SE"`, `"US"`
|
|
681
|
-
- `subheader`: No data (UI only)
|
|
682
|
-
|
|
683
|
-
**When to use:**
|
|
684
|
-
- `country` - Address country, nationality
|
|
685
|
-
- `subheader` - Group related fields visually
|
|
686
|
-
|
|
687
|
-
**Note:** For contact info (email, phone, URL), use `text` fields.
|
|
688
|
-
|
|
689
|
-
---
|
|
690
|
-
|
|
691
|
-
#### Calculated Function Fields
|
|
692
|
-
|
|
693
|
-
```typescript
|
|
694
|
-
{
|
|
695
|
-
_id: Tasks_FieldIds.total_abc,
|
|
696
|
-
label: "Total",
|
|
697
|
-
type: "numericunit", // Output type
|
|
698
|
-
unit: "€",
|
|
699
|
-
functionEnabled: true,
|
|
700
|
-
function: "@function:total_abc", // Function reference
|
|
701
|
-
functionVariables: { // Input dependencies
|
|
702
|
-
quantity: {
|
|
703
|
-
type: "=",
|
|
704
|
-
data: [Tasks_FieldIds.quantity_def]
|
|
705
|
-
},
|
|
706
|
-
price: {
|
|
707
|
-
type: "=",
|
|
708
|
-
data: [Tasks_FieldIds.unit_price_ghi]
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
**See `SDK-function-fields` skill for complete function field guide.**
|
|
715
|
-
|
|
716
|
-
---
|
|
717
|
-
|
|
718
|
-
#### Field Type Limitations
|
|
719
|
-
|
|
720
|
-
| Limitation | Details |
|
|
721
|
-
|------------|---------|
|
|
722
|
-
| **Cannot change type** | Once created, field type cannot be changed via API. Must delete and recreate (loses data) or change in Hailer UI. |
|
|
723
|
-
| **linkedfrom is read-only** | Cannot set values - auto-populated from links |
|
|
724
|
-
| **richtext size** | Large HTML can slow down activity loading |
|
|
725
|
-
| **attachment storage** | Files stored in Hailer, count toward workspace storage |
|
|
726
|
-
|
|
727
|
-
### Calculated Function Fields
|
|
728
|
-
|
|
729
|
-
```typescript
|
|
730
|
-
{
|
|
731
|
-
_id: Tasks_FieldIds.total_abc,
|
|
732
|
-
label: "Total",
|
|
733
|
-
type: "numericunit",
|
|
734
|
-
unit: "€",
|
|
735
|
-
functionEnabled: true,
|
|
736
|
-
function: "@function:total_abc",
|
|
737
|
-
functionVariables: {
|
|
738
|
-
quantity: {
|
|
739
|
-
type: "=",
|
|
740
|
-
data: [Tasks_FieldIds.quantity_def]
|
|
741
|
-
},
|
|
742
|
-
price: {
|
|
743
|
-
type: "=",
|
|
744
|
-
data: [Tasks_FieldIds.unit_price_ghi]
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
See `SDK-function-field-variables` skill for variable types (=, >, <, ?).
|
|
751
|
-
|
|
752
|
-
---
|
|
753
|
-
|
|
754
|
-
## Phases
|
|
755
|
-
|
|
756
|
-
### Basic Phase Structure
|
|
757
|
-
|
|
758
|
-
```typescript
|
|
759
|
-
// In phases.ts
|
|
760
|
-
import { Tasks_FieldIds, Tasks_PhaseIds } from "../enums";
|
|
761
|
-
|
|
762
|
-
export const phases: HailerPhaseUpdatePayload[] = [
|
|
763
|
-
// NEW phase - omit _id
|
|
764
|
-
{
|
|
765
|
-
name: "Todo",
|
|
766
|
-
color: "#57636E",
|
|
767
|
-
fields: [],
|
|
768
|
-
possibleNextPhase: []
|
|
769
|
-
},
|
|
770
|
-
// EXISTING phase - use enum for _id
|
|
771
|
-
{
|
|
772
|
-
_id: Tasks_PhaseIds.todo_abc,
|
|
773
|
-
name: "Todo",
|
|
774
|
-
color: "#57636E",
|
|
775
|
-
fields: [
|
|
776
|
-
Tasks_FieldIds.title_def,
|
|
777
|
-
Tasks_FieldIds.assignee_ghi
|
|
778
|
-
],
|
|
779
|
-
possibleNextPhase: [
|
|
780
|
-
Tasks_PhaseIds.in_progress_jkl,
|
|
781
|
-
Tasks_PhaseIds.done_mno
|
|
782
|
-
]
|
|
783
|
-
}
|
|
784
|
-
];
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
### Phase Properties Reference
|
|
788
|
-
|
|
789
|
-
| Property | Type | Description |
|
|
790
|
-
|----------|------|-------------|
|
|
791
|
-
| `_id` | string | Phase ID (omit for new) |
|
|
792
|
-
| `name` | string | Display name |
|
|
793
|
-
| `color` | string | Hex color (e.g., "#57636E") |
|
|
794
|
-
| `description` | string | Phase description |
|
|
795
|
-
|
|
796
|
-
### Phases Can Represent Different Things
|
|
797
|
-
|
|
798
|
-
**Not all phases are lifecycle stages.** Check semantics before assuming flow:
|
|
799
|
-
|
|
800
|
-
- **Lifecycle phases:** Draft → Review → Active → Archived
|
|
801
|
-
- **Category phases:** Products workflow might use phases as categories (Doors, Windows, etc.)
|
|
802
|
-
- **Status phases:** Support tickets with Open, Waiting, Resolved
|
|
803
|
-
|
|
804
|
-
Always ask the user about phase semantics when creating workflows.
|
|
805
|
-
|
|
806
|
-
#### Field Visibility (IMPORTANT)
|
|
807
|
-
|
|
808
|
-
**Fields are NOT automatically visible in all phases.**
|
|
809
|
-
|
|
810
|
-
Each phase has a `fields` array that controls which fields are visible when an activity is in that phase. A field can be:
|
|
811
|
-
- Visible in ALL phases (add to every phase's `fields` array)
|
|
812
|
-
- Visible in SOME phases (add only to specific phases)
|
|
813
|
-
- Hidden in certain phases (omit from those phases)
|
|
814
|
-
|
|
815
|
-
```typescript
|
|
816
|
-
// Phase 1: Only basic fields visible
|
|
817
|
-
{
|
|
818
|
-
_id: Tasks_PhaseIds.draft_abc,
|
|
819
|
-
name: "Draft",
|
|
820
|
-
fields: [
|
|
821
|
-
Tasks_FieldIds.title_def,
|
|
822
|
-
Tasks_FieldIds.description_ghi
|
|
823
|
-
// assignee and due_date NOT visible in draft
|
|
824
|
-
]
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// Phase 2: More fields become visible
|
|
828
|
-
{
|
|
829
|
-
_id: Tasks_PhaseIds.active_jkl,
|
|
830
|
-
name: "Active",
|
|
831
|
-
fields: [
|
|
832
|
-
Tasks_FieldIds.title_def,
|
|
833
|
-
Tasks_FieldIds.description_ghi,
|
|
834
|
-
Tasks_FieldIds.assignee_mno, // NOW visible
|
|
835
|
-
Tasks_FieldIds.due_date_pqr // NOW visible
|
|
836
|
-
]
|
|
837
|
-
}
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
**When adding a new field:**
|
|
841
|
-
1. Push the field first (`fields-push:force`)
|
|
842
|
-
2. Pull to get the field ID in enums
|
|
843
|
-
3. Decide which phases should show this field
|
|
844
|
-
4. Add the field ID to those phases' `fields` arrays
|
|
845
|
-
5. Push phases (`phases-push:force`)
|
|
846
|
-
|
|
847
|
-
#### Phase Flow Control
|
|
848
|
-
```typescript
|
|
849
|
-
{
|
|
850
|
-
isInitial: true, // Starting phase (activities created here)
|
|
851
|
-
isEndpoint: true, // Final phase (workflow complete)
|
|
852
|
-
possibleNextPhase: [ // Allowed transitions
|
|
853
|
-
Tasks_PhaseIds.review_abc,
|
|
854
|
-
Tasks_PhaseIds.done_def
|
|
855
|
-
],
|
|
856
|
-
possibleNextPhaseSettings: {
|
|
857
|
-
// Phase transition settings (optional)
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
```
|
|
861
|
-
|
|
862
|
-
#### Calendar Integration
|
|
863
|
-
```typescript
|
|
864
|
-
{
|
|
865
|
-
primaryDateField: Tasks_FieldIds.due_date_abc // Show on calendar
|
|
866
|
-
// OR
|
|
867
|
-
primaryDateField: "updated" // Use last updated timestamp
|
|
868
|
-
}
|
|
869
|
-
```
|
|
870
|
-
|
|
871
|
-
#### Auto-Follow
|
|
872
|
-
```typescript
|
|
873
|
-
{
|
|
874
|
-
followers: [
|
|
875
|
-
"user_id_abc", // User ID
|
|
876
|
-
"team_id_def" // Team ID
|
|
877
|
-
]
|
|
878
|
-
// These users/teams auto-follow activities in this phase
|
|
879
|
-
}
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
#### Webhooks
|
|
883
|
-
```typescript
|
|
884
|
-
{
|
|
885
|
-
webhooksEnabled: true,
|
|
886
|
-
webhookUrl: "https://hooks.zapier.com/..."
|
|
887
|
-
// Triggers when activity enters this phase
|
|
888
|
-
}
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
#### Announcements
|
|
892
|
-
```typescript
|
|
893
|
-
{
|
|
894
|
-
announcementFields: [
|
|
895
|
-
Tasks_FieldIds.title_abc,
|
|
896
|
-
Tasks_FieldIds.due_date_def
|
|
897
|
-
],
|
|
898
|
-
announcementFieldsOrder: [
|
|
899
|
-
Tasks_FieldIds.title_abc,
|
|
900
|
-
Tasks_FieldIds.due_date_def
|
|
901
|
-
],
|
|
902
|
-
announcementRecipients: [
|
|
903
|
-
"team_id_abc"
|
|
904
|
-
]
|
|
905
|
-
// Send notification when activity enters phase
|
|
906
|
-
}
|
|
907
|
-
```
|
|
908
|
-
|
|
909
|
-
---
|
|
910
|
-
|
|
911
|
-
## Push Commands
|
|
912
|
-
|
|
913
|
-
| Command | What It Does |
|
|
914
|
-
|---------|--------------|
|
|
915
|
-
| `npm run workflows-sync:force` | Create/update/delete workflows |
|
|
916
|
-
| `npm run fields-push:force` | Create/update/delete fields |
|
|
917
|
-
| `npm run phases-push:force` | Create/update/delete phases |
|
|
918
|
-
| `npm run push:force` | Push all changes |
|
|
919
|
-
|
|
920
|
-
**:force variants** may delete resources not in local files.
|
|
921
|
-
**Non-force variants** only update, never delete.
|
|
922
|
-
|
|
923
|
-
### CRITICAL: Push/Pull Order
|
|
924
|
-
|
|
925
|
-
**Push FIRST, then pull.** Running pull before push overwrites local changes.
|
|
926
|
-
|
|
927
|
-
```
|
|
928
|
-
# CORRECT order when adding new fields locally:
|
|
929
|
-
1. Edit fields.ts (add new field)
|
|
930
|
-
2. npm run fields-push:force
|
|
931
|
-
3. npm run pull ← Now safe, gets generated IDs
|
|
932
|
-
|
|
933
|
-
# WRONG order (loses your work):
|
|
934
|
-
1. Edit fields.ts
|
|
935
|
-
2. npm run pull ← Overwrites your changes!
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
**Why?** Pull fetches server state and overwrites local files. Your unpushed edits are lost.
|
|
939
|
-
|
|
940
|
-
---
|
|
941
|
-
|
|
942
|
-
## Common Patterns
|
|
943
|
-
|
|
944
|
-
### Organizing Large Workflows with Subheaders
|
|
945
|
-
|
|
946
|
-
For workflows with 15+ fields, use `subheader` fields to group related fields into collapsible sections.
|
|
947
|
-
|
|
948
|
-
```typescript
|
|
949
|
-
// In fields.ts - organize fields into logical groups
|
|
950
|
-
export const fields: HailerFieldGeneric[] = [
|
|
951
|
-
// === BASIC INFO (always visible) ===
|
|
952
|
-
{ label: "Customer Name", type: "text", required: true },
|
|
953
|
-
{ label: "Contact Email", type: "text" },
|
|
954
|
-
|
|
955
|
-
// === FINANCIAL SECTION (collapsible) ===
|
|
956
|
-
{
|
|
957
|
-
type: "subheader",
|
|
958
|
-
label: "Financial Details",
|
|
959
|
-
collapsedByDefault: true // Starts collapsed
|
|
960
|
-
},
|
|
961
|
-
{ label: "Unit Price", type: "numericunit", unit: "€" },
|
|
962
|
-
{ label: "Quantity", type: "numeric" },
|
|
963
|
-
{ label: "Discount %", type: "numeric" },
|
|
964
|
-
{ label: "Total", type: "numericunit", unit: "€", functionEnabled: true, ... },
|
|
965
|
-
|
|
966
|
-
// === DELIVERY SECTION (collapsible) ===
|
|
967
|
-
{
|
|
968
|
-
type: "subheader",
|
|
969
|
-
label: "Delivery Information",
|
|
970
|
-
collapsedByDefault: true
|
|
971
|
-
},
|
|
972
|
-
{ label: "Delivery Address", type: "textarea" },
|
|
973
|
-
{ label: "Delivery Date", type: "date" },
|
|
974
|
-
{ label: "Delivery Notes", type: "textarea" },
|
|
975
|
-
|
|
976
|
-
// === INTERNAL/HELPER FIELDS (hidden by default) ===
|
|
977
|
-
{
|
|
978
|
-
type: "subheader",
|
|
979
|
-
label: "Internal Fields",
|
|
980
|
-
collapsedByDefault: true
|
|
981
|
-
},
|
|
982
|
-
{ label: "Integration ID", type: "text" },
|
|
983
|
-
{ label: "Last Sync", type: "datetime" }
|
|
984
|
-
];
|
|
985
|
-
```
|
|
986
|
-
|
|
987
|
-
**When to use subheaders:**
|
|
988
|
-
- Workflows with 15+ fields
|
|
989
|
-
- Logical groupings exist (financial, delivery, contact, internal)
|
|
990
|
-
- Some fields are rarely edited after initial entry
|
|
991
|
-
- Helper/integration fields that clutter the UI
|
|
992
|
-
|
|
993
|
-
**Subheader properties:**
|
|
994
|
-
| Property | Description |
|
|
995
|
-
|----------|-------------|
|
|
996
|
-
| `type: "subheader"` | Required - marks as section divider |
|
|
997
|
-
| `label` | Section title shown in UI |
|
|
998
|
-
| `collapsedByDefault` | `true` = starts collapsed, `false` = starts expanded |
|
|
999
|
-
|
|
1000
|
-
**Common section patterns:**
|
|
1001
|
-
- "Basic Information" - core fields (often expanded)
|
|
1002
|
-
- "Financial Details" - prices, totals, discounts
|
|
1003
|
-
- "Delivery/Shipping" - addresses, dates
|
|
1004
|
-
- "Contact Information" - emails, phones
|
|
1005
|
-
- "Internal/System" - IDs, sync fields, helper fields (collapsed)
|
|
1006
|
-
|
|
1007
|
-
**Note:** Subheaders have no data - they're UI-only for organization.
|
|
1008
|
-
|
|
1009
|
-
---
|
|
1010
|
-
|
|
1011
|
-
### Add Field to Existing Workflow
|
|
1012
|
-
|
|
1013
|
-
```
|
|
1014
|
-
1. npm run pull
|
|
1015
|
-
2. Edit workspace/[Workflow]_[id]/fields.ts
|
|
1016
|
-
- Add new field (omit _id)
|
|
1017
|
-
3. Return ["npm run fields-push:force"]
|
|
1018
|
-
4. npm run pull (to get generated field ID in enums)
|
|
1019
|
-
5. Edit phases.ts - add new field ID to phase.fields arrays using enum
|
|
1020
|
-
6. Return ["npm run phases-push:force"]
|
|
1021
|
-
```
|
|
1022
|
-
|
|
1023
|
-
### Progressive Field Visibility
|
|
1024
|
-
|
|
1025
|
-
Common pattern: reveal more fields as activity progresses through phases.
|
|
1026
|
-
|
|
1027
|
-
```typescript
|
|
1028
|
-
// Draft: minimal fields
|
|
1029
|
-
{ name: "Draft", fields: [title, description] }
|
|
1030
|
-
|
|
1031
|
-
// Review: add reviewer
|
|
1032
|
-
{ name: "Review", fields: [title, description, reviewer] }
|
|
1033
|
-
|
|
1034
|
-
// Active: full visibility
|
|
1035
|
-
{ name: "Active", fields: [title, description, reviewer, assignee, due_date, priority] }
|
|
1036
|
-
|
|
1037
|
-
// Done: keep all visible for reference
|
|
1038
|
-
{ name: "Done", fields: [title, description, reviewer, assignee, due_date, priority] }
|
|
1039
|
-
```
|
|
1040
|
-
|
|
1041
|
-
### Linear Phase Flow
|
|
1042
|
-
|
|
1043
|
-
```typescript
|
|
1044
|
-
{
|
|
1045
|
-
_id: Tasks_PhaseIds.todo_abc,
|
|
1046
|
-
name: "Todo",
|
|
1047
|
-
isInitial: true,
|
|
1048
|
-
possibleNextPhase: [Tasks_PhaseIds.in_progress_def]
|
|
1049
|
-
},
|
|
1050
|
-
{
|
|
1051
|
-
_id: Tasks_PhaseIds.in_progress_def,
|
|
1052
|
-
name: "In Progress",
|
|
1053
|
-
possibleNextPhase: [Tasks_PhaseIds.done_ghi]
|
|
1054
|
-
},
|
|
1055
|
-
{
|
|
1056
|
-
_id: Tasks_PhaseIds.done_ghi,
|
|
1057
|
-
name: "Done",
|
|
1058
|
-
isEndpoint: true,
|
|
1059
|
-
possibleNextPhase: []
|
|
1060
|
-
}
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
### Kanban-Style (Any to Any)
|
|
1064
|
-
|
|
1065
|
-
```typescript
|
|
1066
|
-
const allPhases = [
|
|
1067
|
-
Tasks_PhaseIds.backlog_abc,
|
|
1068
|
-
Tasks_PhaseIds.todo_def,
|
|
1069
|
-
Tasks_PhaseIds.doing_ghi,
|
|
1070
|
-
Tasks_PhaseIds.done_jkl
|
|
1071
|
-
];
|
|
1072
|
-
|
|
1073
|
-
// Each phase can move to any other
|
|
1074
|
-
{
|
|
1075
|
-
_id: Tasks_PhaseIds.todo_def,
|
|
1076
|
-
name: "Todo",
|
|
1077
|
-
possibleNextPhase: allPhases.filter(p => p !== Tasks_PhaseIds.todo_def)
|
|
1078
|
-
}
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
---
|
|
1082
|
-
|
|
1083
|
-
## Common Mistakes
|
|
1084
|
-
|
|
1085
|
-
| Wrong | Right |
|
|
1086
|
-
|-------|-------|
|
|
1087
|
-
| Creating fields before phases | Order: Workflow → Phases → Fields |
|
|
1088
|
-
| Adding `_id` to new items | Omit `_id` for new (server generates) |
|
|
1089
|
-
| Referencing IDs before pull | Push → Pull → Then use new IDs from enums |
|
|
1090
|
-
| Hardcoding IDs | Use enums from `enums.ts` |
|
|
1091
|
-
| Editing `enums.ts` | Never edit - auto-generated |
|
|
1092
|
-
| Running push commands directly | Return to orchestrator (hooks) |
|
|
1093
|
-
| Pulling after local edits | Push first, then pull |
|
|
1094
|
-
| Assuming fields auto-visible | Fields only visible if in `phase.fields` array |
|
|
1095
|
-
| Adding field to all phases | Ask which phases need this field visible |
|
|
1096
|
-
| Forgetting to add field to phases | New fields need to be in `phase.fields` |
|
|
1097
|
-
| Changing field type via API | Field types CANNOT be changed - see below |
|
|
1098
|
-
| Including `key` on existing phases | `key` only for NEW phases - push fails on existing |
|
|
1099
|
-
| Using `predefinedoptions` or `dropdown` | Use `textpredefinedoptions` (with "text" prefix) |
|
|
1100
|
-
| Using `options: [...]` for dropdowns | Use `data: [...]` (not options) |
|
|
1101
|
-
| `data: [{workflowId: "..."}]` for links | Use `data: ["workflowId"]` (plain string array) |
|
|
1102
|
-
| Forgetting `possibleNextPhase` | Activities stuck without phase transition links |
|
|
1103
|
-
| Missing `primaryDateField` | Activities won't show on calendar |
|
|
1104
|
-
|
|
1105
|
-
---
|
|
1106
|
-
|
|
1107
|
-
## CRITICAL: Field Types Cannot Be Changed via API
|
|
1108
|
-
|
|
1109
|
-
**Problem:** Once a field is created, you cannot change its `type` via the SDK push commands.
|
|
1110
|
-
|
|
1111
|
-
**Example:** Cannot change `textarea` → `activitylink` via `npm run fields-push`
|
|
1112
|
-
|
|
1113
|
-
**Workarounds:**
|
|
1114
|
-
1. Create a **new field** with the correct type (deprecate old one)
|
|
1115
|
-
2. Change field type manually in **Hailer UI** (Settings → Workflow → Edit field)
|
|
1116
|
-
3. Delete and recreate the field (loses existing data)
|
|
1117
|
-
|
|
1118
|
-
**What CAN be changed via API:**
|
|
1119
|
-
- `label`, `description`, `required`
|
|
1120
|
-
- `data` (options for predefinedoptions, linked workflows for activitylink)
|
|
1121
|
-
- Field order in workflow
|
|
1122
|
-
|
|
1123
|
-
---
|
|
1124
|
-
|
|
1125
|
-
## Checklist
|
|
1126
|
-
|
|
1127
|
-
Before pushing workflow changes:
|
|
1128
|
-
|
|
1129
|
-
- [ ] Used enums for all existing IDs
|
|
1130
|
-
- [ ] Omitted `_id` for new items
|
|
1131
|
-
- [ ] **Asked user which phases should show new fields**
|
|
1132
|
-
- [ ] New fields added ONLY to relevant `phase.fields` arrays
|
|
1133
|
-
- [ ] Phase transitions make sense (`possibleNextPhase`)
|
|
1134
|
-
- [ ] Required fields have `required: true`
|
|
1135
|
-
- [ ] Link fields have correct workflow IDs in `data`
|
|
1136
|
-
- [ ] Using `:force` variant for commands that need it
|
|
1137
|
-
|
|
1138
|
-
When adding a new field, ALWAYS ask:
|
|
1139
|
-
> "Which phases should this field be visible in? All phases or specific ones?"
|