funifier-mcp 0.2.23 → 0.2.25

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.
Files changed (56) hide show
  1. package/.cursor/rules/funifier.mdc +1 -0
  2. package/.github/copilot-instructions.md +1 -0
  3. package/AGENTS.md +1 -0
  4. package/datasource-funifier-docs/.search-index.json +5588 -4475
  5. package/datasource-funifier-docs/.skills-map.json +3 -1
  6. package/datasource-funifier-docs/knowledge/guides/trigger-examples.md +511 -0
  7. package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +1 -1
  8. package/datasource-funifier-docs/knowledge/modules/action.md +96 -39
  9. package/datasource-funifier-docs/knowledge/modules/challenge.md +285 -48
  10. package/datasource-funifier-docs/knowledge/modules/leaderboard.md +173 -71
  11. package/datasource-funifier-docs/knowledge/modules/point.md +108 -37
  12. package/datasource-funifier-docs/knowledge/modules/trigger.md +1 -1
  13. package/dist/cli/init.d.ts +1 -1
  14. package/dist/cli/init.d.ts.map +1 -1
  15. package/dist/cli/init.js +11 -4
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/core/api-client.d.ts +9 -9
  18. package/dist/core/api-client.d.ts.map +1 -1
  19. package/dist/core/api-client.js +58 -52
  20. package/dist/core/api-client.js.map +1 -1
  21. package/dist/core/api-client.test.js +49 -0
  22. package/dist/core/api-client.test.js.map +1 -1
  23. package/dist/core/constants.d.ts +1 -0
  24. package/dist/core/constants.d.ts.map +1 -1
  25. package/dist/core/constants.js +1 -0
  26. package/dist/core/constants.js.map +1 -1
  27. package/dist/mcp/bundle.js +71 -70
  28. package/dist/mcp/index.js +2 -1
  29. package/dist/mcp/index.js.map +1 -1
  30. package/dist/mcp/tools/get.d.ts.map +1 -1
  31. package/dist/mcp/tools/get.js +22 -8
  32. package/dist/mcp/tools/get.js.map +1 -1
  33. package/dist/mcp/tools/list.d.ts.map +1 -1
  34. package/dist/mcp/tools/list.js +9 -2
  35. package/dist/mcp/tools/list.js.map +1 -1
  36. package/dist/mcp/tools/save.d.ts.map +1 -1
  37. package/dist/mcp/tools/save.js +93 -4
  38. package/dist/mcp/tools/save.js.map +1 -1
  39. package/dist/mcp/tools/save.test.js +42 -0
  40. package/dist/mcp/tools/save.test.js.map +1 -1
  41. package/package.json +3 -2
  42. package/skills/funifier-create-action/SKILL.md +58 -18
  43. package/skills/funifier-create-aggregate/SKILL.md +1 -0
  44. package/skills/funifier-create-challenge/SKILL.md +1 -0
  45. package/skills/funifier-create-custom-page/SKILL.md +1 -0
  46. package/skills/funifier-create-leaderboard/SKILL.md +90 -18
  47. package/skills/funifier-create-level/SKILL.md +1 -0
  48. package/skills/funifier-create-point/SKILL.md +60 -18
  49. package/skills/funifier-create-quiz/SKILL.md +1 -0
  50. package/skills/funifier-create-scheduler/SKILL.md +1 -0
  51. package/skills/funifier-create-trigger/SKILL.md +110 -19
  52. package/skills/funifier-create-virtual-good/SKILL.md +1 -0
  53. package/skills/funifier-debug/SKILL.md +2 -0
  54. package/skills/funifier-help/SKILL.md +1 -0
  55. package/skills/funifier-implement-frontend/SKILL.md +1 -0
  56. package/skills/funifier-index/SKILL.md +9 -1
@@ -5,55 +5,103 @@
5
5
 
6
6
  ## O que é
7
7
 
8
- Cadastro das diferentes ações que os jogadores podem realizar na gamificação. Permite criar e configurar todas as ações possíveis dentro da experiência gamificada, como "vender", "comprar", "comentar", "compartilhar", entre outras. As ações são a base da estratégia de engajamento, pois definem o que é possível fazer na gamificação e servem para disparar regras e recompensas. É possível adicionar atributos extras a cada ação, por exemplo, "produto" e "valor" na ação de venda.
8
+ Cadastro das diferentes ações que os jogadores podem realizar na gamificação. As ações são a base da estratégia de engajamento definem o que pode ser feito e servem para disparar desafios e recompensas. É possível adicionar atributos extras para detalhar o contexto (ex: `produto`, `valor` numa ação de venda).
9
9
 
10
10
  ## Quando usar
11
11
 
12
12
  - Em todo projeto de gamificação (obrigatório)
13
13
  - Para definir os comportamentos que serão rastreados
14
- - Para criar a base de eventos que disparam desafios e recompensas
14
+ - Para criar a base de eventos que disparam challenges e recompensas
15
15
 
16
- ## Checklist de Configuração no Studio
16
+ ## Ação simples vs ação com atributos
17
17
 
18
- - [ ] Definir o _id da ação (letras minúsculas, sem espaços ou acentos)
19
- - [ ] Definir o nome amigável da ação (inicial maiúscula)
20
- - [ ] Definir atributos extras se necessário (name + type: String, Number ou Boolean)
21
- - [ ] Ativar a ação (active: true)
22
- - [ ] Boas práticas: criar ações genéricas e usar atributos para detalhar contexto
18
+ **Ação simples** sem atributos. Use quando o contexto não importa, o fato de ter ocorrido:
19
+
20
+ ```json
21
+ {
22
+ "_id": "watch_video",
23
+ "action": "Watch Video",
24
+ "active": true,
25
+ "attributes": []
26
+ }
27
+ ```
28
+
29
+ Exemplos típicos: `login`, `acessar_feed`, `assistir_video`, `jogar_mini`, `compartilhar`.
30
+
31
+ **Ação com atributos** — adicione atributos quando precisar filtrar ou acumular valores dentro de um challenge. Tipos disponíveis: `String`, `Number`, `Boolean`.
32
+
33
+ ```json
34
+ {
35
+ "_id": "sell",
36
+ "action": "Sell",
37
+ "active": true,
38
+ "attributes": [
39
+ { "name": "product", "type": "String" },
40
+ { "name": "price", "type": "Number" }
41
+ ]
42
+ }
43
+ ```
44
+
45
+ Use atributos quando quiser criar challenges como "vender 10 livros" (filtro por `product = "book"`) ou "vender R$500 em eletrônicos" (soma de `price`).
46
+
47
+ ## Imagem
48
+
49
+ Actions podem ter uma imagem associada via campo `image` com três tamanhos:
50
+
51
+ ```json
52
+ "image": {
53
+ "small": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
54
+ "medium": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
55
+ "original": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 }
56
+ }
57
+ ```
58
+
59
+ Na prática basta preencher `url` nos três tamanhos com a mesma URL se não houver versões distintas.
60
+
61
+ ## Comportamento de `_id` duplicado (upsert)
62
+
63
+ O endpoint POST faz **upsert**: se o `_id` já existir, a ação é **atualizada**; se não existir, é **criada**. Não retorna erro em caso de duplicata.
64
+
65
+ Isso significa que é seguro reenviar a mesma ação para atualizar atributos ou o nome — os challenges existentes que referenciam esse `_id` continuam funcionando.
66
+
67
+ ## Boas Práticas
68
+
69
+ - `_id`: letras minúsculas, sem espaços ou acentos — use `_` como separador (`watch_video`, não `Watch Video`)
70
+ - Crie ações **genéricas** e use atributos para detalhar: prefira `comprar` com atributo `produto` a criar `comprar_sapato` e `comprar_camisa`
71
+ - Omita `attributes` (ou envie `[]`) quando o contexto não for necessário para nenhum challenge
23
72
 
24
73
  ## API Endpoints
25
74
 
26
75
  ### Listar Ações
27
- **Método:** GET
76
+ **Método:** GET
28
77
  **Endpoint:** `/v3/action`
29
- **Descrição:** Retorna a lista de ações configuradas na gamificação.
30
78
 
31
- **Exemplo de Resposta:**
32
- ```json
33
- [
34
- {
35
- "action": "Sell",
36
- "attributes": [
37
- { "name": "product", "type": "String" },
38
- { "name": "price", "type": "Number" }
39
- ],
40
- "active": true,
41
- "_id": "sell"
42
- }
43
- ]
79
+ **Busca múltipla com funifier_list:** o parâmetro `search` aceita termos separados por vírgula (OR):
80
+ ```
81
+ funifier_list type=action search="assistir video, jogar mini, acessar feed"
44
82
  ```
45
83
 
46
- ### Criar Ação
47
- **Método:** POST
84
+ ### Criar / Atualizar Ação
85
+ **Método:** POST
48
86
  **Endpoint:** `/v3/action`
49
- **Descrição:** Cria uma nova ação com nome, atributos e status de ativação.
50
87
 
51
- **Exemplo de Body:**
88
+ POST com `_id` existente = update (upsert). POST sem `_id` = cria com ID gerado.
89
+
90
+ **Ação simples:**
91
+ ```json
92
+ {
93
+ "_id": "watch_video",
94
+ "action": "Watch Video",
95
+ "active": true
96
+ }
97
+ ```
98
+
99
+ **Ação com atributos:**
52
100
  ```json
53
101
  {
54
102
  "_id": "sell",
55
- "active": true,
56
103
  "action": "Sell",
104
+ "active": true,
57
105
  "attributes": [
58
106
  { "name": "product", "type": "String" },
59
107
  { "name": "price", "type": "Number" }
@@ -62,19 +110,28 @@ Cadastro das diferentes ações que os jogadores podem realizar na gamificação
62
110
  ```
63
111
 
64
112
  ### Excluir Ação
65
- **Método:** DELETE
113
+ **Método:** DELETE
66
114
  **Endpoint:** `/v3/action/:id`
67
- **Descrição:** Remove uma ação da gamificação.
68
115
 
69
- ## Boas Práticas
116
+ > Deletar uma ação que é referenciada em challenges ativos pode causar inconsistências. Verifique antes quais challenges usam essa ação:
117
+ > ```
118
+ > funifier_database action=aggregate collection=challenge pipeline='[{"$match": {"rules.actionId": "<action_id>"}}]'
119
+ > ```
120
+
121
+ ## Validação após criar
122
+
123
+ Use `funifier_list` para confirmar que a ação foi criada:
124
+
125
+ ```
126
+ funifier_list type=action search=<_id ou nome>
127
+ ```
70
128
 
71
- - Crie ações genéricas como "comprar", "vender", "responder", "visitar"
72
- - Use os atributos para detalhar o contexto da ação (ex: produto, local, valor)
73
- - Evite criar uma ação diferente para cada variação. Exemplo errado: "comprar sapato", "comprar camisa". Exemplo certo: ação "comprar" com atributo "produto"
129
+ Não use `funifier_logs` para validar actions esse comando é específico para triggers e schedulers.
74
130
 
75
- ## Validações e Testes
131
+ ## Checklist
76
132
 
77
- - [ ] Ação aparece na lista GET /v3/action
78
- - [ ] Ação tem _id único e sem caracteres especiais
79
- - [ ] Atributos estão com tipos corretos (String, Number, Boolean)
80
- - [ ] Ação está ativa (active: true)
133
+ - [ ] `_id` definido: minúsculas, sem espaços (`watch_video`, não `Watch Video`)
134
+ - [ ] `action` (nome amigável) preenchido
135
+ - [ ] `active: true`
136
+ - [ ] Atributos com tipos corretos: `String`, `Number` ou `Boolean`
137
+ - [ ] Confirmado com `funifier_list type=action search=<_id>`
@@ -19,73 +19,310 @@ Configuração de desafios a serem completados por jogadores ou equipes. Permite
19
19
  - **Point**: tipos de ponto devem existir antes de criar challenges que os recompensam
20
20
  - **Action**: ações devem existir antes de criar challenges que as referenciam
21
21
 
22
- ## Checklist de Configuração no Studio
22
+ ## Campos Obrigatórios vs Opcionais
23
23
 
24
- - [ ] Definir título e descrição do desafio
25
- - [ ] Definir regras (rules): qual ação, quantas vezes, filtros por atributo
26
- - [ ] Definir recompensas (points): tipo de ponto e quantidade
27
- - [ ] Definir frequência (uma vez, diário, semanal, ilimitado)
28
- - [ ] Definir desbloqueio (quando fica disponível)
29
- - [ ] Configurar notificações de conclusão
30
- - [ ] Vincular técnicas de jogo (techniques)
24
+ | Campo | Obrigatório | Descrição |
25
+ |---|---|---|
26
+ | `challenge` | sim | Nome/título do desafio |
27
+ | `techniques` | sim | Array com pelo menos um código GT (ex: `["GT35"]`) |
28
+ | `rules` | sim | Regras que definem o que o jogador deve fazer |
29
+ | `points` | recomendado | Recompensas em pontos ao completar |
30
+ | `description` | não | Descrição exibida ao jogador |
31
+ | `active` | não | `true` por padrão |
32
+ | `_id` | não | Gerado automaticamente se omitido |
33
+ | `range` | não | Obrigatório quando há múltiplas rules; padrão `0` |
34
+ | `teamChallenge` | não | `true` para desafios de equipe |
35
+ | `limitTotal` | não | Limite de vezes que pode ser completado |
36
+ | `limitPerType` | não | A quem se aplica o limite |
37
+ | `limitTimeAmount` | não | Número de períodos para o limite |
38
+ | `limitTimeScale` | não | Escala de tempo do limite |
39
+ | `notifications` | não | Notificações ao completar/criar/alterar |
40
+ | `requirements` | não | Pré-requisitos para desbloquear |
41
+ | `rewards` | não | Recompensas adicionais (itens de catálogo) |
42
+ | `when` | não | Restrição de data/hora de disponibilidade |
43
+ | `principals` | não | Restringe o desafio a jogadores/equipes específicos |
44
+ | `trackFullProgress` | não | Rastrear progresso completo de cada rule |
45
+ | `extra` | não | Campos customizados livres |
46
+ | `i18n` | não | Traduções do título/descrição |
47
+
48
+ ## Imagem (badge)
49
+
50
+ Challenges usam o campo `badge` (não `image`) para a imagem do desafio:
51
+
52
+ ```json
53
+ "badge": {
54
+ "small": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
55
+ "medium": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 },
56
+ "original": { "url": "https://...", "size": 0, "width": 0, "height": 0, "depth": 0 }
57
+ }
58
+ ```
59
+
60
+ Na prática basta preencher `url` nos três tamanhos com a mesma URL se não houver versões distintas.
61
+
62
+ ## Técnicas de Jogo (techniques)
63
+
64
+ `techniques` é **obrigatório**. Cada código GT representa uma mecânica de gamificação. Para challenges, os códigos válidos são:
65
+
66
+ `GT35`, `GT22`, `GT02`, `GT07`, `GT17`, `GT14`, `GT48`, `GT12`, `GT40`, `GT89`, `GT68`, `GT66`, `GT67`, `GT21`, `GT30`, `GT86`
67
+
68
+ O mais comum para challenges é **`GT35` (Quest List)** — exibe uma lista de tarefas/missões para o jogador cumprir.
69
+
70
+ ```json
71
+ "techniques": ["GT35"]
72
+ ```
73
+
74
+ ## Tabela de Referência dos Enums
75
+
76
+ ### `range` — como as rules são avaliadas
77
+
78
+ | Valor | Label |
79
+ |---|---|
80
+ | `0` | Complete All (completar todas as rules) |
81
+ | `1` | Complete Any (completar qualquer uma) |
82
+ | `2` | Complete All in Order (completar todas em ordem) |
83
+
84
+ Padrão: `0` (Complete All). Necessário quando há mais de uma rule.
85
+
86
+ ### `rules[].operator` — critério de contagem da rule
87
+
88
+ | Valor | Label | Uso |
89
+ |---|---|---|
90
+ | `5` | `>=` (Greater Than or Equal) | Padrão — contar N ou mais vezes |
91
+ | `1` | `=` (Equal) | Exatamente N vezes |
92
+ | `40` | `%` (Percentage) | Percentual |
93
+
94
+ Exemplo: `operator: 5, total: 3` = executar a ação 3 ou mais vezes.
95
+
96
+ ### `rules[].filters[].operator` — operador dos filtros de atributo
97
+
98
+ | Valor | Label |
99
+ |---|---|
100
+ | `1` | Is (igual a) |
101
+ | `3` | Contains (contém) |
102
+ | `4` | Greater Than (maior que) |
103
+ | `5` | Greater Than or Equal To |
104
+ | `6` | Less Than (menor que) |
105
+ | `7` | Less Than or Equal To |
106
+ | `8` | Is Not (diferente de) |
107
+ | `9` | Exists (campo existe) |
108
+ | `10` | Does Not Exist (campo não existe) |
109
+ | `20` | Near in Meters (geolocalização) |
110
+
111
+ ### `points[].operation` — operação sobre atributo numérico da ação
112
+
113
+ | Valor | Label | Uso |
114
+ |---|---|---|
115
+ | `0` | None | Pontos fixos (padrão) |
116
+ | `1` | Multiply By | Multiplica `total` pelo valor do atributo da ação |
117
+ | `2` | Divide By | Divide `total` pelo valor do atributo da ação |
118
+
119
+ `operation > 0` só faz sentido quando a rule tem exatamente 1 action com atributo numérico. Nesse caso, `points[].value` deve conter o nome do atributo.
120
+
121
+ ### `limitPerType` — a quem o limite se aplica
122
+
123
+ | Valor | Label |
124
+ |---|---|
125
+ | `0` | Player |
126
+ | `1` | Team |
127
+ | `2` | Gamification (global) |
128
+
129
+ ### `limitTimeScale` — escala de tempo do limite
130
+
131
+ | Valor | Label |
132
+ |---|---|
133
+ | `0` | None (sem janela de tempo) |
134
+ | `2` | Second |
135
+ | `3` | Minute |
136
+ | `4` | Hour |
137
+ | `5` | Day |
138
+ | `6` | Week |
139
+ | `7` | Month |
140
+ | `8` | Year |
141
+
142
+ ### `notifications[].event`
143
+
144
+ | Valor | Label |
145
+ |---|---|
146
+ | `0` | On Win (ao completar o desafio) |
147
+ | `1` | On Create (ao criar o progresso) |
148
+ | `2` | On Change (ao avançar no progresso) |
149
+
150
+ ### `notifications[].scope`
151
+
152
+ | Valor | Label |
153
+ |---|---|
154
+ | `0` | Private (apenas o jogador) |
155
+ | `1` | Newsfeed (visível no feed) |
156
+ | `99` | Custom |
157
+
158
+ ### `requirements[].type` / `rewards[].type`
159
+
160
+ | Valor | Label |
161
+ |---|---|
162
+ | `0` | Point |
163
+ | `1` | Challenge |
164
+ | `2` | Catalog Item |
165
+ | `3` | Level |
166
+ | `5` | Lottery |
167
+ | `9` | Competition |
168
+ | `50` | Lottery Ticket (apenas em rewards) |
31
169
 
32
170
  ## API Endpoints
33
171
 
34
172
  ### Listar Desafios
35
- **Método:** GET
173
+ **Método:** GET
36
174
  **Endpoint:** `/v3/challenge`
37
175
 
38
- **Exemplo de Resposta:**
39
- ```json
40
- [
41
- {
42
- "challenge": "Sell 10 books",
43
- "description": "Sell 10 books to earn 25 xp and 5 coins",
44
- "rules": [
45
- {
46
- "actionId": "sell",
47
- "filters": [
48
- { "value": "book", "operator": 1, "param": "product" }
49
- ],
50
- "total": 10
51
- }
52
- ],
53
- "points": [
54
- { "total": 25, "category": "xp" },
55
- { "total": 5, "category": "coin" }
56
- ],
57
- "_id": "DTkhJHV"
58
- }
59
- ]
60
- ```
176
+ ### Buscar por ID
177
+ **Método:** GET
178
+ **Endpoint:** `/v3/challenge/:id`
61
179
 
62
- ### Criar Desafio
63
- **Método:** POST
180
+ ### Criar / Atualizar Desafio
181
+ **Método:** POST
64
182
  **Endpoint:** `/v3/challenge`
65
183
 
66
- **Exemplo de Body:**
184
+ Criar e atualizar usam o mesmo endpoint POST. Se `_id` for enviado e o documento existir, é atualizado.
185
+
186
+ ### Excluir Desafio
187
+ **Método:** DELETE
188
+ **Endpoint:** `/v3/challenge/:id`
189
+
190
+ ## Exemplos
191
+
192
+ ### Challenge simples — executar uma ação N vezes
193
+
67
194
  ```json
68
195
  {
69
196
  "challenge": "Watch Video",
70
- "description": "Complete this challenge by watching a video, and earn 10 xp",
197
+ "description": "Watch a video to earn 10 xp",
198
+ "techniques": ["GT35"],
199
+ "rules": [
200
+ { "actionId": "watch_video", "operator": 5, "total": 1 }
201
+ ],
202
+ "points": [
203
+ { "total": 10, "category": "xp", "operation": 0 }
204
+ ]
205
+ }
206
+ ```
207
+
208
+ ### Challenge com filtro de atributo
209
+
210
+ ```json
211
+ {
212
+ "challenge": "Sell 10 Books",
213
+ "description": "Sell 10 books to earn 25 xp",
214
+ "techniques": ["GT35"],
215
+ "rules": [
216
+ {
217
+ "actionId": "sell",
218
+ "operator": 5,
219
+ "total": 10,
220
+ "filters": [
221
+ { "param": "product", "operator": 1, "value": "book" }
222
+ ]
223
+ }
224
+ ],
225
+ "points": [
226
+ { "total": 25, "category": "xp", "operation": 0 }
227
+ ]
228
+ }
229
+ ```
230
+
231
+ ### Challenge com múltiplas rules (completar todas)
232
+
233
+ ```json
234
+ {
235
+ "challenge": "Daily Routine",
236
+ "techniques": ["GT35"],
237
+ "range": 0,
71
238
  "rules": [
72
- { "actionId": "watch_video", "operator": 5, "total": 0 }
239
+ { "actionId": "login", "operator": 5, "total": 1 },
240
+ { "actionId": "comment", "operator": 5, "total": 3 }
73
241
  ],
74
242
  "points": [
75
- { "total": 10.0, "category": "xp", "operation": 0 }
243
+ { "total": 50, "category": "xp", "operation": 0 }
244
+ ]
245
+ }
246
+ ```
247
+
248
+ ### Challenge com limite de frequência (1x por dia por jogador)
249
+
250
+ ```json
251
+ {
252
+ "challenge": "Daily Check-in",
253
+ "techniques": ["GT35"],
254
+ "rules": [
255
+ { "actionId": "checkin", "operator": 5, "total": 1 }
76
256
  ],
77
- "techniques": ["GT35"]
257
+ "points": [{ "total": 5, "category": "xp", "operation": 0 }],
258
+ "limitTotal": 1,
259
+ "limitPerType": 0,
260
+ "limitTimeAmount": 1,
261
+ "limitTimeScale": 5
78
262
  }
79
263
  ```
80
264
 
81
- ### Excluir Desafio
82
- **Método:** DELETE
83
- **Endpoint:** `/v3/challenge/:id`
265
+ ### Challenge com notificação ao completar
266
+
267
+ ```json
268
+ {
269
+ "challenge": "First Comment",
270
+ "techniques": ["GT35"],
271
+ "rules": [{ "actionId": "comment", "operator": 5, "total": 1 }],
272
+ "points": [{ "total": 10, "category": "xp", "operation": 0 }],
273
+ "notifications": [
274
+ {
275
+ "event": 0,
276
+ "type": 0,
277
+ "scope": 0,
278
+ "content": "{{player.name}} completed {{item.name}}"
279
+ }
280
+ ]
281
+ }
282
+ ```
283
+
284
+ ### Challenge com pré-requisito (desbloqueia após completar outro desafio)
285
+
286
+ ```json
287
+ {
288
+ "challenge": "Advanced Task",
289
+ "techniques": ["GT35"],
290
+ "rules": [{ "actionId": "upload", "operator": 5, "total": 5 }],
291
+ "points": [{ "total": 100, "category": "xp", "operation": 0 }],
292
+ "requirements": [
293
+ { "type": 1, "total": 1, "item": "first_comment_challenge_id" }
294
+ ]
295
+ }
296
+ ```
297
+
298
+ ## Como Validar se foi Criado Corretamente
299
+
300
+ Após salvar com `funifier_save`, confirme com `funifier_get`:
301
+
302
+ ```
303
+ funifier_get type=challenge id=<id_retornado>
304
+ ```
305
+
306
+ Ou liste e filtre:
307
+
308
+ ```
309
+ funifier_list type=challenge search=<nome_do_challenge>
310
+ ```
311
+
312
+ Para testar se o challenge está funcionando, registre a ação e verifique o progresso do jogador:
313
+
314
+ ```
315
+ POST /v3/action/log { "actionId": "...", "player": "player_id" }
316
+ GET /v3/challenge/progress?player=player_id
317
+ ```
84
318
 
85
- ## Validações e Testes
319
+ ## Checklist de Configuração
86
320
 
87
- - [ ] Desafio aparece na lista GET /v3/challenge
88
- - [ ] Ações referenciadas nas rules existem (GET /v3/action)
89
- - [ ] Pontos referenciados nas recompensas existem (GET /v3/point)
90
- - [ ] Ao registrar ação (POST /v3/action/log), o desafio progride corretamente
91
- - [ ] Ao completar o desafio, jogador recebe os pontos definidos
321
+ - [ ] `challenge` (nome) preenchido
322
+ - [ ] `techniques` com pelo menos um código GT (ex: `["GT35"]`)
323
+ - [ ] `rules` com `actionId` existente, `operator` e `total` definidos
324
+ - [ ] `range` definido quando mais de uma rule
325
+ - [ ] `points` com `category` existente
326
+ - [ ] Ações referenciadas nas rules existem (`funifier_list type=action`)
327
+ - [ ] Pontos referenciados existem (`funifier_list type=point`)
328
+ - [ ] Validado com `funifier_get` após salvar