diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b21796a..ba9007b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,7 +13,7 @@ "name": "fluig", "source": "./fluig", "description": "Plugin para desenvolvimento TOTVS Fluig — ciclo completo: design (brainstorm), scaffolding (widget/dataset/form/workflow), revisão estática + testes (review) e deploy (verify).", - "version": "2.2.0", + "version": "2.3.0", "author": { "name": "DataAgile" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 87875c0..340aebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,61 @@ Histórico das versões públicas do `dataagile-agent-kit`. --- +## [2.9.0] — 2026-08-08 + +### Dataset e evento agora têm teste unitário de verdade (fluig 2.3.0) + +O server-side do Fluig rodava sem rede: dataset e evento só eram exercidos depois do +deploy, no servidor. Agora a `/fluig:test` traz um **harness Node** (`fluig-mock.cjs`) que +carrega o fonte `ds_*`/`wf_*` **sem alterar nada** num contexto com os globais Fluig +mockados — o motor do Fluig é Rhino/ES5, subconjunto do que o Node executa. Gate mecânico +por `node --test` com JUnit e cobertura, mais a checagem que a cobertura não faz: **fonte +que nenhum teste carrega é invisível no relatório**, então todo artefato tocado pela task +precisa ter arquivo de teste, independente do percentual. O limite fica dito em voz alta: +o Node valida a lógica, não o Rhino — interop Java só o QA pega. + +O `fluig-lint.sh` passou a **rotear por mundo**: `ds_*`, `wf_*`, `events/` e `Util/` são +lintados como ES5 (`let`/`const`/arrow/`class` viram erro de parse, porque quebram no +servidor) e isso roda mesmo em repositório sem `@po-ui`; `.ts` de widget continua no +ESLint do projeto, com fallback para o config do plugin. Sem ESLint disponível, greps +mínimos seguram o gate em vez de deixá-lo passar em silêncio. + +### E2E saiu da /fluig:test e virou skill própria + +Nova **`/fluig:test-web`**: roteiro aprovado antes de executar, screenshot por passo em +`evidencias/`, critério verificável **literal** (não "a tela parece certa"), tratamento de +erro com o log do servidor, e — o pulo — a sessão exploratória validada vira **spec de +regressão versionada** rodada com `--reporter=junit`. Dois modos, uma fonte: a sessão dá a +evidência, o spec dá o gate determinístico que o `/fluig:qa` consome. + +O `/fluig:qa` deixou de aceitar "a suíte está verde": confere artefato por artefato da +task qual spec o cobre, e artefato sem spec **para o QA** e aciona a `/fluig:test-web`. +Ganhou também uma **lista fechada de risco ALTO** — seis itens que não se julgam nem se +rebaixam por impressão visual. + +### Memória do ambiente do cliente + +Nova **`/fluig:arqueologia`**, a variante de ambiente: sem código-fonte no repositório, o +mapa da fatia sai do que está **publicado** (REST autenticada do Fluig ou exportações do +Studio), e o teto de confiança muda junto — LACUNA frequente aqui é informação, não falha. +Divergência entre repositório e servidor é achado próprio. O `/fluig:plan` ganhou o Passo +3.5 (o que não pode quebrar, com forma de verificar) e o `/fluig:verify` o writeback do +Passo 5.5, que só roda **com o deploy concluído** — entrega fantasma no mapa seria uma +regra CONFIRMADO falsa. + +### gates.json unificado e brainstorm por rodadas + +O `gates.json` do Fluig passou a viver em `docs/fluig/plans/.gates.json` com esquema +único, `tests_unit` dividido em `widget` e `server` (metade sem artefato grava `n/a` +**explícito**, nunca chave ausente) e um **lint gate global** antes dos reviews — máquina +antes do LLM. `deploy`, `qa` e `verify` leem o arquivo em vez da conversa. + +O `/fluig:brainstorm` ganhou o Passo 0 (levantar os fatos do projeto antes de perguntar +qualquer coisa), rodadas de fronteira e recomendação rotulada com fonte — com o teto +correto: a base fluig vem de documentação, então MCP sozinho não passa de INFERIDO. A +`/fluig:api-ref` foi reescrita sobre as tools que o seu tier realmente expõe, com o +vocabulário real dos dados. + ## [2.8.0] — 2026-08-08 ### O ciclo Protheus passou a lembrar do legado do cliente (protheus 2.16.0) diff --git a/README.md b/README.md index be9d34b..561b300 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # DA DataAgile Agent Kit -[![Versão](https://img.shields.io/badge/versão-2.8.0-1a4d5c?style=flat-square&logoColor=white)](https://github.com/tbc-servicos/dataagile-agent-kit/releases) +[![Versão](https://img.shields.io/badge/versão-2.9.0-1a4d5c?style=flat-square&logoColor=white)](https://github.com/tbc-servicos/dataagile-agent-kit/releases) [![Claude Code](https://img.shields.io/badge/Claude_Code-plugin-CC785C?style=flat-square&logoColor=white)](https://claude.ai/code) [![Protheus](https://img.shields.io/badge/Protheus-ADVPL%2FTLPP-1a4d5c?style=flat-square&logoColor=white)](https://dataagile-agent-kit.dataagile.com.br) [![PO--UI MCP](https://img.shields.io/badge/PO--UI-MCP-22C55E?style=flat-square&logoColor=white)](https://po-ui.io) @@ -146,7 +146,7 @@ mkdir -p ~/.config/dataagile && echo '{"api_key":"SUA_CHAVE"}' > ~/.config/dataa | Server | Função | |--------|--------| -| `tbc-knowledge` | Base ADVPL remota — `searchFunction`, `findEndpoint`, `findSmartView`, `findExecAuto`, `findMvcPattern` e mais 4 tools | +| `tbc-knowledge` | Base ADVPL remota — `searchFunction`, `findEndpoint`, `findSmartView`, `searchKnowledge`, `submitFeedback` + busca vetorial `ragSearchKnowledge`/`ragSearchDocs` | | `local-knowledge-external` | Base offline de conhecimento técnico ADVPL/TLPP curado e docs Protheus | | `po-ui` | MCP oficial PO-UI — componentes Angular para fronts Protheus, inputs, outputs e exemplos | diff --git a/fluig/.claude-plugin/plugin.json b/fluig/.claude-plugin/plugin.json index 9fc7a08..a3da2ab 100644 --- a/fluig/.claude-plugin/plugin.json +++ b/fluig/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "fluig", "description": "Plugin Claude Code para desenvolvimento TOTVS Fluig — ciclo completo: brainstorm, planejamento, implementação com teammates (TDD), deploy, QA (E2E Playwright), verificação. Scaffolding de formulários, datasets, widgets Angular 19 + PO-UI, workflows BPM.", - "version": "2.2.0", + "version": "2.3.0", "author": { "name": "TBC - Time de Desenvolvimento" }, diff --git a/fluig/CLAUDE.md b/fluig/CLAUDE.md index fb9e2bd..a847524 100644 --- a/fluig/CLAUDE.md +++ b/fluig/CLAUDE.md @@ -65,7 +65,7 @@ export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 - Custo: ~2K tokens fixos ### Camada 2 — Sob demanda (0 tokens até chamada) -- MCP queries (`searchFluigPatterns`, `searchFluigApi`, etc.) +- MCP queries (`searchKnowledge`, `ragSearchKnowledge`, `ragSearchDocs`) - Consultas ao RAG de documentação ### Camada 3 — Compactável (contexto de trabalho) @@ -87,6 +87,10 @@ export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=50 ## Ciclo de Desenvolvimento ``` +/fluig:base → sandbox descartável na máquina do dev (inner loop E2E) + ↓ (opcional) +/fluig:arqueologia → mapa do ambiente da fatia (quando não houver) + ↓ /fluig:brainstorm → design aprovado ↓ /fluig:plan → plano de implementação diff --git a/fluig/README.md b/fluig/README.md index 1f3e566..c851eb2 100644 --- a/fluig/README.md +++ b/fluig/README.md @@ -80,6 +80,8 @@ source ~/.zshrc ## Ciclo de Desenvolvimento ``` +/fluig:arqueologia → mapa do ambiente da fatia (quando não houver) + ↓ /fluig:brainstorm → entrevista + design aprovado + spec em disco ↓ /fluig:plan → plano de implementação com tasks TDD @@ -102,6 +104,7 @@ Cada skill termina com **"Próximo passo:"** guiando o dev para a etapa seguinte | Comando | Descrição | |---------|-----------| | `/fluig:init-project` | Inicializa projeto — entrevista o dev e gera CLAUDE.md com contexto do cliente (URL servidor, prefixos, integração Protheus) | +| `/fluig:arqueologia` | Mapeia a fatia do ambiente Fluig do cliente (artefatos publicados e regras) antes do design | | `/fluig:brainstorm` | Gate de design — entrevista, mapeia integrações, gera spec aprovada antes de qualquer scaffolding | | `/fluig:plan` | Plano de implementação com tasks TDD e file mapping, salvo em `docs/fluig/plans/` | | `/fluig:implement` | Agent Team executando o plano (fluig-implementer em worktree + fluig-spec-reviewer + fluig-reviewer, comunicação bidirecional) | @@ -123,7 +126,9 @@ Cada skill termina com **"Próximo passo:"** guiando o dev para a etapa seguinte | Comando | Descrição | |---------|-----------| | `/fluig:review` | Atalho: pipeline completo (revisão estática → testes → deploy → qa) — alternativa rápida ao fluxo guiado | -| `/fluig:test` | Gera e executa testes unitários (Jasmine/Karma) + E2E (Playwright) | +| `/fluig:test` | Testes unitários — widget (runner do projeto) e server-side (harness Node para `ds_*`/`wf_*`/eventos) | +| `/fluig:test-web` | Ciclo E2E no servidor real: roteiro aprovado, evidências por passo e spec de regressão com JUnit | +| `/fluig:base` | Sandbox Fluig descartável em Docker NA MÁQUINA DO DEV — inner loop E2E, reset em segundos | | `/fluig:debug` | Debugging sistemático em 4 fases (reproduzir → investigar → hipótese → corrigir) com logs Docker, Playwright traces, Karma | | `/fluig:api-ref` | Referência rápida das APIs Fluig: `DatasetFactory`, `DatasetBuilder`, `CardAPI`, `WCMAPI`, `fluigc`, `WFMovementDTO`, `getValue/setValue` | | `/fluig:feedback` | Registra aprendizado quando Claude erra e o dev corrige — alimenta a base de conhecimento Fluig | diff --git a/fluig/hooks/fluig-lint.sh b/fluig/hooks/fluig-lint.sh index f3e6ce8..90549ea 100755 --- a/fluig/hooks/fluig-lint.sh +++ b/fluig/hooks/fluig-lint.sh @@ -1,7 +1,13 @@ #!/usr/bin/env bash -# fluig-lint.sh — Lint JS/TS/HTML em projetos Angular/PO-UI Fluig +# fluig-lint.sh — Lint JS/TS/HTML para desenvolvimento Fluig # Ativado por PostToolUse (Write|Edit) -# Padrão: espelha quality-check.sh global, adaptado para projetos com @po-ui +# +# Dois mundos, convenções OPOSTAS: +# - SERVER-SIDE (ds_*.js, wf_*.js, events/, Util/): Rhino/ECMA 5 — let/const/arrow/class +# quebram NO SERVIDOR; o config eslint.server.config.mjs do plugin torna isso erro de +# parse aqui. Roda SEMPRE, mesmo sem @po-ui no package.json (repo de datasets puro). +# - WIDGET (.ts/.html em projeto @po-ui): ES moderno; usa o eslint do projeto, com +# fallback para o config do plugin. set -euo pipefail @@ -34,11 +40,28 @@ if [[ "$FILE_PATH" == *.min.js ]] || \ exit 0 fi -# Detectar se é projeto Fluig: procura @po-ui/ng-components no package.json -# Sobe até 4 níveis a partir do diretório do arquivo CWD=$(echo "$INPUT" | jq -r '.cwd // empty') [[ -z "$CWD" ]] && CWD="$(dirname "$FILE_PATH")" +# Assets do plugin (configs ESLint) — CLAUDE_PLUGIN_ROOT quando disponível, +# senão relativo a este script (hooks/ → skills/test/assets/) +PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +SERVER_CONFIG="$PLUGIN_ROOT/skills/test/assets/eslint.server.config.mjs" +WIDGET_CONFIG="$PLUGIN_ROOT/skills/test/assets/eslint.widget.config.mjs" + +BASENAME=$(basename "$FILE_PATH") + +# Roteamento: server-side Fluig se bater no padrão de nome/caminho +is_server_side() { + [[ "$EXT" != "js" ]] && return 1 + [[ "$BASENAME" == *.spec.js || "$BASENAME" == *.test.js ]] && return 1 + [[ "$BASENAME" == ds_* ]] && return 0 + [[ "$BASENAME" == wf_* ]] && return 0 + [[ "$FILE_PATH" == */events/* ]] && return 0 + [[ "$FILE_PATH" == */Util/* ]] && return 0 + return 1 +} + is_fluig_project() { local dir="$1" for i in 1 2 3 4; do @@ -51,18 +74,62 @@ is_fluig_project() { return 1 } -if ! is_fluig_project "$CWD"; then +ERRORS="" + +# ============ ROTA SERVER-SIDE — roda SEMPRE, independe de @po-ui ============ +if is_server_side; then + LINTED=0 + if command -v npx &>/dev/null && [[ -f "$SERVER_CONFIG" ]]; then + ESLINT_EXIT=0 + # cd para o diretório do arquivo: ESLint flat config ignora ("outside of base + # path", exit 0) arquivo fora do cwd — rodar de outro diretório silencia o gate + ESLINT_OUT=$(cd "$(dirname "$FILE_PATH")" && npx --no-install eslint --config "$SERVER_CONFIG" "$(basename "$FILE_PATH")" 2>&1) || ESLINT_EXIT=$? + # exit 1 = achado de lint (bloqueia com a saída); exit >= 2 ou eslint ausente = + # falha da FERRAMENTA (config inválido, versão sem flat config, não instalado) — + # não é culpa do código do dev: cai no fallback de greps + if [[ $ESLINT_EXIT -le 1 ]] && ! echo "$ESLINT_OUT" | grep -qiE "not found|command failed|could not determine"; then + LINTED=1 + if [[ $ESLINT_EXIT -eq 1 ]]; then + ERRORS+="ESLint (server-side Rhino/ES5):\n${ESLINT_OUT}\n" + fi + fi + fi + + # Fallback sem eslint: greps mínimos (melhor que zero gate) + if [[ $LINTED -eq 0 ]]; then + if grep -qE '(^|[^A-Za-z_])(const |let |=>|class )' "$FILE_PATH" 2>/dev/null; then + ERRORS+="Fluig server-side: const/let/arrow/class detectado — o motor do Fluig é Rhino ECMA 5; use var e function\n" + fi + if grep -q 'alert(' "$FILE_PATH" 2>/dev/null; then + ERRORS+="Fluig: uso de alert() proibido\n" + fi + fi + + # Regras estruturais (sempre) + if [[ "$BASENAME" == ds_* ]] || [[ "$BASENAME" == wf_* ]]; then + if ! grep -qE '^[[:space:]]*try[[:space:]]*\{' "$FILE_PATH" 2>/dev/null; then + ERRORS+="Fluig: '${BASENAME}' não tem try/catch — obrigatório em datasets e workflows\n" + fi + fi + + if [[ -n "$ERRORS" ]]; then + jq -n --arg reason "Qualidade Fluig — corrija antes de continuar:\n\n${ERRORS}\nRegra errada para este caso? Registre: /fluig:feedback" \ + '{"decision":"block","reason":$reason}' + fi exit 0 fi -ERRORS="" +# ============ ROTA WIDGET — exige projeto @po-ui ============ +if ! is_fluig_project "$CWD"; then + exit 0 +fi # 1. Prettier — auto-fix silencioso (não bloqueia) if command -v npx &>/dev/null; then npx --no-install prettier --write "$FILE_PATH" &>/dev/null || true fi -# 2. ESLint — bloqueia se erros (só se config existir no projeto) +# 2. ESLint — config do projeto; sem config, fallback para o do plugin if command -v npx &>/dev/null; then ESLINT_CONFIG=$(find "$CWD" -maxdepth 4 -name node_modules -prune -o \( \ -name "eslint.config.js" -o -name "eslint.config.mjs" -o \ @@ -70,10 +137,15 @@ if command -v npx &>/dev/null; then -name ".eslintrc.json" -o -name ".eslintrc.yml" -o \ -name ".eslintrc" \ \) -print 2>/dev/null | head -1) + ESLINT_ARGS=() + if [[ -z "$ESLINT_CONFIG" && "$EXT" == "ts" && -f "$WIDGET_CONFIG" ]]; then + ESLINT_ARGS=(--config "$WIDGET_CONFIG") + ESLINT_CONFIG="$WIDGET_CONFIG" + fi if [[ -n "$ESLINT_CONFIG" ]]; then ESLINT_EXIT=0 - ESLINT_OUT=$(npx --no-install eslint "$FILE_PATH" 2>&1) || ESLINT_EXIT=$? - if [[ $ESLINT_EXIT -ne 0 ]]; then + ESLINT_OUT=$(npx --no-install eslint "${ESLINT_ARGS[@]}" "$FILE_PATH" 2>&1) || ESLINT_EXIT=$? + if [[ $ESLINT_EXIT -ne 0 ]] && ! echo "$ESLINT_OUT" | grep -qiE "not found|command failed|could not determine"; then ERRORS+="ESLint:\n${ESLINT_OUT}\n" fi fi @@ -93,26 +165,17 @@ if [[ "$EXT" == "ts" ]] && [[ "$FILE_PATH" != *.spec.ts ]]; then fi fi -# 4. Regras Fluig-específicas para JS (não aplica a spec/test) +# 4. Regras Fluig para JS de widget (não aplica a spec/test) if [[ "$EXT" == "js" ]] && \ [[ "$FILE_PATH" != *.spec.js ]] && \ [[ "$FILE_PATH" != *.test.js ]]; then - if grep -q 'alert(' "$FILE_PATH" 2>/dev/null; then ERRORS+="Fluig: uso de alert() proibido — use Swal.fire() (SweetAlert2)\n" fi - - # Datasets e workflows devem ter try/catch - BASENAME=$(basename "$FILE_PATH") - if [[ "$BASENAME" == ds_* ]] || [[ "$BASENAME" == wf_* ]]; then - if ! grep -qE '^\s*try\s*\{' "$FILE_PATH" 2>/dev/null; then - ERRORS+="Fluig: '${BASENAME}' não tem try/catch — obrigatório em datasets e workflows\n" - fi - fi fi if [[ -n "$ERRORS" ]]; then - jq -n --arg reason "Qualidade Fluig — corrija antes de continuar:\n\n${ERRORS}" \ + jq -n --arg reason "Qualidade Fluig — corrija antes de continuar:\n\n${ERRORS}\nRegra errada para este caso? Registre: /fluig:feedback" \ '{"decision":"block","reason":$reason}' exit 0 fi diff --git a/fluig/hooks/session-context.cjs b/fluig/hooks/session-context.cjs index cfab586..7ec2110 100755 --- a/fluig/hooks/session-context.cjs +++ b/fluig/hooks/session-context.cjs @@ -12,12 +12,12 @@ emitSessionContext({ profile: 'internal', cleanupTbcDbs: false, groups: [ - { title: 'Ciclo de desenvolvimento (nesta ordem)', names: ['init-project', 'brainstorm', 'plan', 'implement', 'deploy', 'qa', 'verify'] }, + { title: 'Ciclo de desenvolvimento (nesta ordem)', names: ['init-project', 'arqueologia', 'brainstorm', 'plan', 'implement', 'deploy', 'qa', 'verify'] }, { title: 'Scaffolding', names: ['widget', 'dataset', 'form', 'workflow'] }, - { title: 'Qualidade e apoio', names: ['review', 'test', 'debug', 'api-ref', 'feedback'] }, + { title: 'Qualidade e apoio', names: ['review', 'test', 'test-web', 'debug', 'api-ref', 'feedback', 'base'] }, ], notes: [ - 'MCP tools disponiveis: searchFluigPatterns, searchFluigApi, searchKnowledge (plugin fluig tbc-knowledge)', + 'MCP tools disponiveis: searchKnowledge (padroes/convencoes, use platform: "fluig") + ragSearchKnowledge, ragSearchDocs (busca semantica) — plugin fluig tbc-knowledge', 'Regra de modelos: opus para brainstorm/design, sonnet para implementacao e review/QA, haiku somente para deploy/compilacao.', ], }); diff --git a/fluig/skills/api-ref/SKILL.md b/fluig/skills/api-ref/SKILL.md index 940a28d..5dc70de 100644 --- a/fluig/skills/api-ref/SKILL.md +++ b/fluig/skills/api-ref/SKILL.md @@ -11,58 +11,66 @@ description: Referência das APIs TOTVS Fluig — DatasetFactory, DatasetBuilder # Fluig API Reference -Referência completa das APIs TOTVS Fluig via MCP `tbc-knowledge`. +Referência das APIs TOTVS Fluig via MCP `tbc-knowledge`. -## Consulta MCP +## As três tools, e o que cada uma responde -### searchFluigApi — Referência de APIs +| Tool | O que tem dentro | Use para | +|---|---|---| +| `searchKnowledge({ platform: "fluig", keyword })` | conhecimento curado por skill/categoria | convenção, padrão, template, regra de review | +| `ragSearchKnowledge({ query })` | busca semântica sobre a base de conhecimento | pergunta em linguagem natural, quando você não sabe o termo exato | +| `ragSearchDocs({ query })` | busca semântica sobre a documentação processada | assinatura de API, parâmetro, comportamento documentado | -Busca documentação de APIs por categoria: +**Comece pela `searchKnowledge` com o termo técnico exato.** Se não souber o termo, ou +se a busca por palavra-chave vier vazia, caia para `ragSearchKnowledge`/`ragSearchDocs`, +que aceitam a pergunta inteira. -``` -searchFluigApi({ category: "dataset" }) // DatasetFactory, DatasetBuilder, constraints -searchFluigApi({ category: "form" }) // CardAPI, WCMAPI, getValue/setValue, masks -searchFluigApi({ category: "workflow" }) // WFMovementDTO, variáveis WK*, setTaskUser -searchFluigApi({ category: "widget" }) // DatasetFactory client-side, REST Protheus -searchFluigApi({ category: "fluigc" }) // fluigc components (modal, message, etc) -searchFluigApi({ category: "rest" }) // REST endpoints Fluig -``` +> A busca por palavra-chave é sensível a acento: `validação` e `validacao` não são o +> mesmo termo. Na dúvida, tente os dois — ou use a busca semântica. + +## O que buscar, para quê + +### APIs por área -Busca livre por palavra-chave: ``` -searchFluigApi({ query: "DatasetFactory createDataset" }) -searchFluigApi({ query: "WKUser WKNumState" }) -searchFluigApi({ query: "jQuery mask CPF" }) +searchKnowledge({ platform: "fluig", keyword: "DatasetFactory" }) // datasets, constraints +searchKnowledge({ platform: "fluig", keyword: "CardAPI" }) // formulário: getValue/setValue +searchKnowledge({ platform: "fluig", keyword: "WCMAPI" }) // contexto/sessão do WCM +searchKnowledge({ platform: "fluig", keyword: "WFMovementDTO" }) // workflow: movimentação +searchKnowledge({ platform: "fluig", keyword: "fluigc" }) // componentes fluigc (modal, message) ``` -### searchFluigPatterns — Padrões e Templates de Código - -Busca padrões, convenções e templates por categoria e skill: +Assinatura ou comportamento que a busca por palavra-chave não devolve: ``` -searchFluigPatterns({ category: "template", skill: "fluig-dataset" }) -searchFluigPatterns({ category: "template", skill: "fluig-form" }) -searchFluigPatterns({ category: "template", skill: "fluig-workflow" }) -searchFluigPatterns({ category: "template", skill: "fluig-widget" }) -searchFluigPatterns({ category: "naming" }) // convenções de nomenclatura -searchFluigPatterns({ category: "review" }) // regras de code review -searchFluigPatterns({ category: "qa" }) // checklist de QA -searchFluigPatterns({ category: "conventions" }) // boas práticas gerais +ragSearchDocs({ query: "parâmetros do createDataset no Fluig" }) +ragSearchDocs({ query: "quais variáveis WK estão disponíveis no evento de workflow" }) ``` -Busca livre: +### Padrões, convenções e templates + ``` -searchFluigPatterns({ query: "try catch logging dataset" }) -searchFluigPatterns({ query: "SweetAlert2 validação formulário" }) +searchKnowledge({ platform: "fluig", skill: "dataset", keyword: "template" }) +searchKnowledge({ platform: "fluig", skill: "form", keyword: "validação" }) +searchKnowledge({ platform: "fluig", skill: "workflow", keyword: "evento" }) +searchKnowledge({ platform: "fluig", skill: "widget", keyword: "template" }) +searchKnowledge({ platform: "fluig", keyword: "nomenclatura" }) +searchKnowledge({ platform: "fluig", skill: "review", keyword: "review" }) ``` +Vocabulário de `skill` que existe na base: `fluig` · `client-fluig` · `widget` · +`form` · `workflow` · `dataset` · `review` · `debug` · `api-ref`. **Não invente +filtro** — nome de categoria que não existe nos dados devolve vazio, e vazio é lido +como "não existe padrão para isso", que é a conclusão errada. + ## Quando usar cada tool -| Dúvida | Tool | -|--------|------| -| Qual método usar para consultar dataset? | `searchFluigApi({ category: "dataset" })` | -| Como obter valor de campo no formulário? | `searchFluigApi({ category: "form" })` | -| Quais variáveis WK* existem no workflow? | `searchFluigApi({ category: "workflow" })` | -| Como estruturar um dataset corretamente? | `searchFluigPatterns({ category: "template", skill: "fluig-dataset" })` | -| Qual a convenção de nomenclatura? | `searchFluigPatterns({ category: "naming" })` | -| O código passa no review? | `searchFluigPatterns({ category: "review" })` | +| Dúvida | Consulta | +|--------|----------| +| Qual método usar para consultar dataset? | `searchKnowledge({ platform: "fluig", keyword: "DatasetFactory" })` | +| Como obter valor de campo no formulário? | `searchKnowledge({ platform: "fluig", keyword: "getValue" })` | +| Quais variáveis WK* existem no workflow? | `ragSearchDocs({ query: "variáveis WK disponíveis no workflow Fluig" })` | +| Como estruturar um dataset corretamente? | `searchKnowledge({ platform: "fluig", skill: "dataset", keyword: "template" })` | +| Qual a convenção de nomenclatura? | `searchKnowledge({ platform: "fluig", keyword: "nomenclatura" })` | +| O código passa no review? | `searchKnowledge({ platform: "fluig", skill: "review", keyword: "review" })` | +| Não sei nem o nome da API que preciso | `ragSearchKnowledge({ query: "" })` | diff --git a/fluig/skills/arqueologia/SKILL.md b/fluig/skills/arqueologia/SKILL.md new file mode 100644 index 0000000..14c2784 --- /dev/null +++ b/fluig/skills/arqueologia/SKILL.md @@ -0,0 +1,117 @@ +--- +name: arqueologia +description: Mapeia a fatia do ambiente Fluig do cliente que a demanda atual vai tocar — datasets, formulários, workflows e widgets PUBLICADOS, com as regras de negócio que carregam — e grava em docs/legado/ com escala de confiança. Sem código-fonte no repositório, o mapa sai do AMBIENTE (REST do Fluig e exportações do Studio); LACUNA frequente é normal aqui, não falha. Extração parcial e append-only. Use antes do /fluig:brainstorm quando a área da demanda não tiver mapa. +--- + + +Escopo é a fatia que a demanda toca, nunca o ambiente inteiro. Se o mapa que você está +prestes a escrever não cabe em uma sessão, o escopo está errado — volte e reduza. +Esta skill NÃO gera código e NÃO modifica artefato nenhum: escreve apenas em `docs/legado/`. + + +**A diferença para o Protheus, dita de saída:** aqui normalmente **não há código-fonte +no repositório** — o que existe é o que está **publicado no servidor Fluig** do cliente. +O mapa sai do ambiente, e isso muda o teto de confiança: + +- **CONFIRMADO** — você leu o artefato exportado/consultado do ambiente (o JS do + dataset via export ou consulta, o XML/HTML do formulário, o fluxo do processo). +- **INFERIDO** — comportamento deduzido do que a base de conhecimento, a documentação + ou o nome do artefato sugerem. A KB fluig vem de documentação, não de código: + **retorno de MCP sozinho nunca passa de INFERIDO**. +- **LACUNA** — regra dentro de artefato que você **não conseguiu exportar nem + consultar** (dataset builtin sem fonte visível, evento de processo inacessível). + **LACUNA aqui é frequente e normal** — o valor do mapa é justamente dizer onde + ninguém consegue ver. + +## Passo 0 — Escopo e profundidade + +Pergunte ao dev, com `AskUserQuestion`: + +1. **Qual a fatia?** (processo, formulário, conjunto de datasets ou página/widget que + a demanda toca) +2. **Qual profundidade?** + - `1` **Essencial** (padrão) — inventário do publicado + regras de negócio visíveis + - `2` **Completo** — inclui campos de formulário (nome, tipo, obrigatoriedade), + constraints dos datasets e mapa de quem consome quem + - `3` **Detalhado** — inclui eventos de processo/formulário exportados linha a + linha e histórico de versões dos artefatos no servidor + +## Passo 0.5 — Fatia já mapeada: conferir antes de acrescentar + +Se a fatia já tem linha na `docs/legado/COBERTURA.md`, esta rodada é **conferência**, +não extração: revisite cada regra CONFIRMADO na origem (re-exporte/reconsulte o +artefato) e registre em `## Divergências desta rodada` o que mudou — a regra antiga +vai para "Arquivadas" com o motivo. Comece pela seção `## Entregas` (o que o +`/fluig:verify` devolveu ao mapa). Regra CONFIRMADO desatualizada é pior que LACUNA. + +## Passo 1 — Inventário do publicado + +**A via de consulta ao servidor é pré-requisito deste passo** — e o plugin não a +distribui. Em ordem de preferência: + +1. **REST autenticada do Fluig** — `GET /ecm/api/rest/...`, + `/process-management/api/v2/processes`, com credencial do ambiente do cliente +2. **Exportações do Studio/admin** fornecidas pelo dev ou pelo admin do cliente +3. **Nenhuma das duas** → o inventário vem do que o dev/admin declarar, e cada item + entra como INFERIDO com a fonte "declarado por " — diga isso com todas as + letras no cabeçalho do mapa + +> `searchKnowledge`/`ragSearchKnowledge` são a base de padrões do plugin — dizem o que +> o Fluig **faz**, nunca o que **este cliente** tem publicado. Não servem de inventário. + +Levante para a fatia: + +- Datasets: nome, tipo (builtin / customizado), quem o consome +- Formulários: id, versão publicada, processo(s) que o usam +- Workflows: processo, versão, atividades e eventos anexados +- Páginas/widgets: página do portal, instâncias e parâmetros + +Se o repositório TIVER fonte versionado (projeto novo), inventarie também o repo — aí +o teto volta a ser CONFIRMADO pelo código, como no Protheus. + +**Divergência repositório × servidor é achado próprio**: artefato publicado sem fonte +no repo (ou fonte no repo diferente do publicado) é onde mora o comportamento que vai +quebrar. Liste em seção separada. + +## Passo 2 — Regras de negócio visíveis + +Do artefato exportado/consultado, não da documentação: validações de formulário, +condições de fluxo, constraints de dataset, mensagens ao usuário. Numere +`RN--01`, `RN--02`… com rótulo de confiança e a **origem** (artefato + +onde: evento, campo, atividade). + +Regra dentro de artefato não exportável: registre como LACUNA **com o nome do +artefato** — é pergunta para o admin do ambiente, não suposição. + +## Passo 3 — Gravar (append-only) + +`docs/legado/.md` com o cabeçalho: + +> ⚠️ **Extração parcial, baseada no AMBIENTE `` em ``** para a demanda +> ``. Append-only: rodadas futuras acrescentam, nunca reescrevem. Fora desta +> fatia, o mapa não existe. Artefato republicado depois desta data pode divergir. + +E com estas seções fixas (crie vazias na primeira rodada — os outros passos e o +`/fluig:verify` escrevem nelas; seção improvisada quebra o append-only): + +- `## Regras` — as `RN--NN` do Passo 2 +- `## Entregas` — preenchida pelo Passo 5.5 do `/fluig:verify` +- `## Divergências desta rodada` — preenchida pelo Passo 0.5 (conferência) +- `## Arquivadas` — regra que deixou de valer, sempre **com motivo e data** + +E `docs/legado/COBERTURA.md`, **uma linha por fatia mapeada**, neste formato: + +| Fatia | Artefatos cobertos | Demanda | Data | +|---|---|---|---| +| `pedido-compra` | `ds_pedidos`, form 12345, processo `PC_Aprovacao` | TICKET-123 | 2026-08-08 | + +Com a contagem do não-mapeado no rodapé (datasets/forms/processos da instância fora +de qualquer fatia). A coluna `Demanda` acumula tickets — é onde o `/fluig:verify` +registra cada entrega. + +## Passo 4 — Encerrar + +Reporte: artefatos inventariados, regras numeradas, quantas LACUNAs (aqui o número +alto é informação, não vergonha), divergências repo × servidor, cobertura acumulada. +Encaminhe para `/fluig:brainstorm`, que vai ancorar o design neste mapa. **Não** gere +código. diff --git a/fluig/skills/base/SKILL.md b/fluig/skills/base/SKILL.md new file mode 100644 index 0000000..2e49589 --- /dev/null +++ b/fluig/skills/base/SKILL.md @@ -0,0 +1,127 @@ +--- +name: base +description: Sobe um sandbox Fluig DESCARTÁVEL em Docker na MÁQUINA DO DEV (nunca na VPS de trabalho) para o inner loop do agente — implementar, deployar e rodar E2E com log limpo e reset em segundos. Usa o projeto open-source fluig-docker-dev (instalador oficial baixado pelo próprio dev). Use quando o dev pedir "subir um fluig", "sandbox fluig", "ambiente fluig local" ou quando o ciclo precisar de E2E isolado. +--- + +Você vai subir um ambiente Fluig descartável para desenvolvimento e teste E2E isolado. + +## ⚠️ ONDE RODAR — leia antes de qualquer comando + +**Este sandbox sobe na MÁQUINA DO DEV, nunca no servidor de trabalho/homologação da equipe.** + +O servidor Fluig de trabalho da equipe é o ambiente de **entrega** — é onde o `/fluig:deploy` +publica para homologação e onde o cliente valida. Subir instância de sandbox lá: +- rouba RAM do ambiente de entrega da equipe (o Fluig pede no mínimo 8G de máquina); +- polui o log do servidor — e "erro no log durante o teste" é risco **ALTO por + definição** na lista fechada do `/fluig:qa`: tráfego de sandbox alheio gera falso + ALTO para os outros devs. + +Antes de subir, confirme com o dev: + +> "Este sandbox vai rodar **nesta máquina** (precisa de ~8 GB de RAM livres e Docker). +> O servidor de trabalho fica intocado — ele é só o destino de homologação. Confirma?" + +Se a máquina do dev não tem 8 GB livres, a alternativa é UMA instância de sandbox em +servidor próprio de infra **com autorização de quem administra** — nunca por padrão. + +Cheque os recursos antes de prosseguir (comandos Linux; em Docker Desktop +Mac/Windows, confira RAM/disco nas configurações do Docker): + +```bash +docker info >/dev/null 2>&1 && echo "docker ok" || echo "instale o Docker primeiro" +free -g | awk '/Mem:/ {print "RAM livre (GB): " $7}' # precisa de ~8 (recomendado 12) +df -h . | awk 'NR==2 {print "disco livre: " $4}' # instalação ~12 GB + volumes +``` + +## O que é (e o que não é) + +- Baseado no projeto open-source **`fluig-docker-dev`** (MIT, comunidade): Dockerfile + que roda o **instalador oficial do Fluig** dentro do container + MySQL 8 (a opção + leve de banco) + MailDev para inspecionar e-mails. +- **O instalador é baixado pelo PRÓPRIO dev na Central do Cliente TOTVS** — nem o + instalador nem a imagem buildada podem ser redistribuídos. +- **Não é ambiente suportado pela TOTVS e nunca vai para produção** — a TOTVS não + publica imagem Docker de Fluig; isto é automação do instalador oficial, para + desenvolvimento. +- **Licença:** sem License Server configurado o Fluig entra em **modo demonstração — + todos os recursos, 1 usuário ativo** (TDN "Modo demonstração"), que é exatamente o + perfil de um agente rodando E2E. O README do projeto fala em **7 dias** de modo + demonstração (o TDN não documenta prazo) — irrelevante para sandbox descartável: + derrube e suba outro. Para mais de um usuário simultâneo, aponte o License Server + da empresa (porta 5555, slot 4012) — licenças Fluig contam por **identidade**, e o + pool contratado pode ser usado em dev/homologação sem custo adicional (TDN + "Licenciamento da plataforma"). + +## Passo 1 — Obter o projeto e o instalador + +```bash +git clone https://github.com/brunogasparetto/fluig-docker-dev.git +cd fluig-docker-dev +# Baixe o instalador Linux 64 do Fluig na Central do Cliente TOTVS e coloque o +# CONTEÚDO em image/installer/ — a estrutura esperada (INSTALACAO.md do projeto): +# fluig-installer-64.sh, fluig-installer.jar, wildfly-dist.zip, openoffice.zip, +# jdk-64/, packs/ +``` + +O projeto instala **Fluig 2.0 ou 1.8** (`.env` → `FLUIG=2.0|1.8`). Se a homologação +do cliente roda outra release, este sandbox **não** a reproduz — diga isso ao dev em +vez de fingir paridade (o E2E de comportamento dependente de versão vai para a +homologação). + +## Passo 2 — Subir + +```bash +docker compose up -d --build # primeiro build roda o instalador: demora +docker compose logs -f fluig # acompanhe até o boot completar (minutos) +``` + +Acesso padrão do projeto (valores do compose upstream): **Fluig em +`http://localhost:8080`** (WCMAdmin em `/wcmadmin`, login `wcmadmin` / senha `adm`), +MailDev em `http://localhost:1080`, MySQL em `localhost:3306` (root/rootpassword). +Primeiro acesso: o `wcmadmin` cria a empresa de trabalho; cada empresa usa um +subdiretório do volume (`/var/fluig-volume/empresa001`). + +> Se a porta `1080` já estiver em uso na sua máquina (proxies SOCKS costumam usá-la), +> remapeie o MailDev no compose (ex.: `1081:1080`) antes do `up`. + +## Passo 3 — Perfil de recursos (este JÁ é o perfil leve) + +O projeto é a combinação leve viável: **MySQL** como banco (vs SQL Server), e os +componentes opcionais **desligados por padrão** (`INSTALL_NODE=false`, +`INSTALL_SOLR=false` no build). O piso real é o do TDN para homologação: **8G de +RAM de máquina** (recomendado 12G; produção oficial pede 16G por instância) — o +compose upstream **não define limite de container**, então numa máquina justa vale +acrescentar `mem_limit: 8g` ao serviço fluig para o WildFly não engolir o host. +Abaixo de 8G de máquina não insista: é fura-piso do TDN. + +## Passo 4 — Usar no ciclo + +- `/fluig:deploy` pode publicar **no sandbox** durante o inner loop (alvo + `http://localhost:8080`, credenciais locais) — homologação continua sendo o servidor da equipe. + **Os gates do `/fluig:implement` (testes, lint, reviews) valem igual** — o sandbox + muda o ALVO do deploy do inner loop, não pula gate nenhum. +- `/fluig:test-web` e `/fluig:qa` rodam E2E contra o sandbox com **log limpo**: um + erro no log do servidor durante o teste é do SEU código, não de tráfego alheio. +- Sandbox é **servidor Fluig real** — a regra "nunca localhost" do E2E existe para + impedir teste contra mock/porta morta, não contra uma instância descartável + saudável. + +## Reset e descarte + +```bash +docker compose down # para tudo +docker volume rm _fluig-volume # zera documentos/volumes da empresa +docker compose up -d # renasce zerado +``` + +Reset total em segundos — é a razão de este sandbox existir. Estado que precisa +sobreviver (formulário publicado, processo modelado) pertence à homologação, não +ao sandbox. + +## Regras + +- **Nunca no servidor de trabalho da equipe** sem autorização explícita de quem o administra. +- Nunca redistribuir instalador ou imagem buildada. +- Nunca usar o sandbox como ambiente de homologação/entrega — o resultado que o + cliente valida sai do servidor de homologação, via `/fluig:deploy` normal. +- Sandbox velho não se conserta: derrube e suba outro. diff --git a/fluig/skills/brainstorm/SKILL.md b/fluig/skills/brainstorm/SKILL.md index 4762249..c981647 100644 --- a/fluig/skills/brainstorm/SKILL.md +++ b/fluig/skills/brainstorm/SKILL.md @@ -1,6 +1,6 @@ --- name: brainstorm -description: Gate de design para desenvolvimento Fluig. Use ANTES de criar qualquer artefato (widget, form, dataset, workflow). Entrevista o desenvolvedor, mapeia integrações e produz um design aprovado antes de acionar qualquer skill de scaffolding. Invoque ao iniciar qualquer nova funcionalidade Fluig. +description: Gate de design para desenvolvimento Fluig. O dev roda /fluig:brainstorm ANTES de criar qualquer artefato (widget, form, dataset, workflow). Levanta os fatos do projeto, entrevista o desenvolvedor em rodadas com resposta recomendada, mapeia integrações e produz um design aprovado antes de acionar qualquer skill de scaffolding. disable-model-invocation: true --- @@ -8,23 +8,71 @@ Você está iniciando o planejamento de uma funcionalidade Fluig. Nenhum código ## HARD GATE -Não invoque nenhuma skill de scaffolding (`fluig-widget`, `fluig-form`, `fluig-dataset`, `fluig-workflow`) antes de ter o design aprovado pelo usuário nesta skill. +Não gere nenhum código, não crie arquivos de artefato Fluig e não invoque nenhuma skill +de scaffolding ou implementação antes de ter o design aprovado pelo usuário nesta skill. ## HARD GATE — modelo opus -Brainstorm é design, e design roda em **opus**. Antes do Passo 1, verifique qual modelo está -atendendo esta sessão. Se **não** for opus, PARE e responda exatamente: +Brainstorm é design, e design roda em **opus**. Antes do Passo 0, verifique qual +modelo está atendendo esta sessão. Se **não** for opus, PARE e responda exatamente: -> ⚠️ O brainstorm exige o modelo **opus**. +> ⚠️ O brainstorm exige o modelo **opus** (regra de modelos deste plugin). > Sessão atual: ``. Rode `/model opus` e chame `/fluig:brainstorm` de novo. -Não faça as perguntas do Passo 1 e não gere design fora do opus. Só siga em outro modelo se o -desenvolvedor mandar seguir mesmo assim, por escrito, nesta conversa — e registre isso no +Não faça as perguntas do Passo 1 e não gere design fora do opus. Só siga em outro modelo se +o desenvolvedor mandar seguir mesmo assim, por escrito, nesta conversa — e registre isso no design aprovado. -## Passo 1 — Entender o artefato +## Passo 0 — Levantar os fatos (antes de perguntar qualquer coisa) -Use `AskUserQuestion` com as perguntas abaixo em uma única chamada com múltiplas questões: +Antes da primeira pergunta, levante você mesmo: + +```bash +cat CLAUDE.md 2>/dev/null # prefixos e convenções do projeto +find . -name "*.js" -path "*dataset*" # datasets existentes +find . -type d -name "wg_*" # widgets existentes +git log --oneline -15 2>/dev/null # o que foi feito recentemente +``` + +Consulte também o MCP para o tipo de artefato em questão: + +``` +searchKnowledge({ platform: "fluig", skill: "", keyword: "template" }) +ragSearchKnowledge({ query: "" }) +``` + +Leia também, se existirem: +- `docs/legado/.md` e `docs/legado/COBERTURA.md` — mapa da `/fluig:arqueologia` + (o ambiente do cliente, com escala de confiança). Fatia da demanda fora das fatias + mapeadas e ambiente cheio de artefato não documentado? Ofereça rodar + `/fluig:arqueologia` antes de seguir. +- `docs/legado/regressao/*.md` das demandas anteriores na mesma fatia — cada item é + **restrição** do design, não sugestão. + +**Fato é trabalho seu, decisão é do dev.** Se está no repositório, no MCP ou no +ambiente, busque — não pergunte. Trade-off e regra de negócio, pergunte. Busca cara +roda em subagente e não bloqueia a rodada: só as perguntas que dependem dela esperam +o retorno — o resto da fronteira você pergunta agora. + +## Passo 1 — Entender o artefato (rodada 1) + +Use `AskUserQuestion` com as perguntas abaixo em uma única chamada com múltiplas +questões. Em toda pergunta de múltipla escolha, marque qual opção você recomenda e +por quê — com a evidência do Passo 0 ou do MCP. O dev decide, mas nunca no escuro. + +**Teto de confiança:** a KB fluig vem de **documentação processada, não de +código-fonte** — recomendação sustentada só pelo MCP é rotulada `INFERIDO`, nunca +`CONFIRMADO`. `CONFIRMADO` exige fato lido do próprio projeto (Passo 0) ou do +ambiente. O que depender do ambiente do cliente e você não puder ver é `LACUNA` — +vira pergunta, não suposição. + +O rótulo aparece **na própria recomendação**, sempre neste formato: + +> ➡️ **Recomendo:** — +> `[CONFIRMADO | INFERIDO]` · fonte: + +E no design do Passo 2, toda `LACUNA` vai obrigatoriamente para a seção +"Riscos e premissas". **Pergunta 1:** Qual(is) artefato(s) será(ão) criado(s)? - Widget Angular (tela interativa) @@ -41,10 +89,34 @@ Use `AskUserQuestion` com as perguntas abaixo em uma única chamada com múltipl - Workflow existente - Nenhuma integração externa -**Pergunta 4:** Há artefatos Fluig existentes no projeto para reaproveitar ou modificar? -- Sim (descrever quais) +**Pergunta 4** *(pule se o Passo 0 não encontrou artefato nenhum — projeto novo)*: +Encontrei estes artefatos no projeto: ``. Algum deles deve ser +reaproveitado ou modificado? *(não pergunte se existem artefatos — isso você já +levantou no Passo 0)* +- Sim — quais - Não, tudo novo +## Passo 1.5 — Rodadas seguintes (a fronteira) + +As perguntas do Passo 1 são a **raiz** da árvore de decisão, não a entrevista inteira. +Cada resposta abre decisões que dependiam dela — pergunte-as em uma nova rodada, +agrupadas, antes de montar o design: + +| Resposta da raiz | Decisões que ela abre | +|---|---| +| Widget Angular | de onde vêm os dados, paginação ou carga total, comportamento em erro de rede, permissão por papel | +| Dataset | dataset de serviço ou customizado, constraints obrigatórias, volume e timeout, cache | +| Workflow / evento BPM | qual evento exatamente, o que fazer se o evento falhar (trava a atividade?), reprocessamento | +| Integração Protheus REST | autenticação, timeout e retry, o que o usuário vê quando o Protheus está fora | + +**Regra de dependência:** pergunta cuja resposta depende de outra pergunta ainda +aberta **nesta** rodada pertence à rodada **seguinte** — nunca à atual. + +**Quando parar:** a entrevista termina quando a fronteira está vazia **e** não sobrou +premissa sua sem resposta — liste cada decisão que você adotou sem perguntar; se a +lista não está vazia, a fronteira também não está (o que o dev aceitar por escrito +fica registrado na seção "Riscos e premissas" do design). Só então vá para o Passo 2. + ## Passo 2 — Apresentar o design Com base nas respostas, apresente um design estruturado contendo: @@ -61,8 +133,11 @@ Descreva em 3-5 passos o que o usuário final vai fazer e o que cada artefato en ### Dependências entre artefatos Se houver múltiplos artefatos: qual precisa existir antes do outro? Ex: dataset antes do widget. -### Riscos e decisões em aberto -Liste explicitamente qualquer ponto que ainda não está claro e que pode impactar o desenvolvimento. +### Riscos e premissas (esta lista tem que sair vazia) +Liste tudo que ainda não está claro e toda decisão que **você** tomou sem perguntar. +Cada item é uma pergunta da próxima rodada, não um aviso no rodapé do design. Só +permanece na lista o que o dev declarar, por escrito, que aceita assumir — com o +motivo registrado ao lado. Não avance para o Passo 3 com item pendente sem esse aceite. ## Passo 3 — Aprovação @@ -82,18 +157,3 @@ Próximo passo: /fluig:plan → criar plano de implementação com tasks detalha ``` **Se não aprovado:** revise o design com base no feedback e apresente novamente. - -## Regras obrigatórias - -- Nunca pule direto para scaffolding sem design aprovado -- Se o CLAUDE.md do projeto existir, leia-o para usar o prefixo correto dos artefatos -- Prefira perguntar sobre riscos cedo do que descobri-los durante o desenvolvimento - ---- - -## Consulta de Conhecimento - -Se precisar de informação não disponível no MCP, consulte o RAG: -``` -searchKnowledge({ keyword: "" }) -``` diff --git a/fluig/skills/deploy/SKILL.md b/fluig/skills/deploy/SKILL.md index 8446d62..4daf9bd 100644 --- a/fluig/skills/deploy/SKILL.md +++ b/fluig/skills/deploy/SKILL.md @@ -8,11 +8,22 @@ Você vai conduzir o deploy de artefatos Fluig no servidor de teste de forma seg ## HARD GATE -- **Leia `docs/plans/.gates.json`** e confirme: `tests_unit.status=ok`, reviews `ok`. O arquivo é a fonte de - verdade — não confie em afirmação da conversa. +- **Leia `docs/fluig/plans/.gates.json`** e confirme: `tests_unit.widget.status` + ∈ {ok, n/a} **e** `tests_unit.server.status` ∈ {ok, n/a} (nunca os dois `n/a` se a + feature tem artefato), `lint.status=ok`, `spec_review.status=ok`, + `code_review.status=ok`. Não confie em afirmação da conversa — o arquivo é a fonte + de verdade (pipeline retomável). O `` é o basename (sem `.md`) do plano salvo pelo `/fluig:plan` em `docs/fluig/plans/`; havendo mais de um `.gates.json`, pergunte ao dev qual feature. Não inicie o deploy se as tarefas de implementação não estiverem concluídas. Verifique o status das tarefas antes de prosseguir. +## Alvo: sandbox × homologação + +- **Sandbox descartável** (`/fluig:base`, na máquina do dev): alvo do **inner loop** + (implementar → deployar → E2E com log limpo → derrubar). Dispensa a confirmação + formal de ambiente — mas **o HARD GATE acima vale para os dois alvos**. +- **Servidor de homologação** (CLAUDE.md do projeto): alvo da **entrega** — fluxo + completo abaixo, com confirmação. + ## Passo 1 — Ler contexto do projeto Leia o CLAUDE.md do projeto para identificar: diff --git a/fluig/skills/feedback/SKILL.md b/fluig/skills/feedback/SKILL.md index 3926e01..087acfd 100644 --- a/fluig/skills/feedback/SKILL.md +++ b/fluig/skills/feedback/SKILL.md @@ -41,6 +41,26 @@ Analisar o contexto da conversa e propor: --- +## Passo 2.5 — Checar duplicata e contradição (antes de mostrar o rascunho) + +A KB não tem supersessão automática — dois feedbacks contraditórios convivem, e quem +busca pode seguir o errado. Antes do Passo 3, busque o tema: + +``` +searchKnowledge({ keyword: "", limit: 5 }) +``` + +Se vier registro próximo, mostre ao dev junto com o rascunho e pergunte: + +> "Encontrei este(s) aprendizado(s) relacionado(s). O seu **complementa**, **corrige** +> ou **duplica** algum deles?" + +- **Corrige** → abra o rascunho com a linha `⚠️ CORRIGE aprendizado de :` + no campo ERRO, para quem buscar depois achar a versão nova junto da velha. +- **Duplica** → não submeta; avise o dev que já existe. + +--- + ## Passo 3 — Dev revisa e aprova Apresentar o rascunho e perguntar: @@ -51,6 +71,20 @@ Aguardar resposta. Aplicar edições se o dev solicitar. Só avançar após apro --- +## Passo 3.5 — Candidato a promoção? (ANTES de submeter — a tag entra no payload) + +Junto da aprovação do rascunho, pergunte: + +> "Esse erro é provável de se repetir com outros devs? Se sim, qual skill deveria +> **impedir** isso (reviewer, widget, dataset, hook de lint...)?" + +Se o dev apontar destino, acrescente `promote:` (ex.: `promote:reviewer`) às +TAGS do rascunho **antes** do Passo 4 — o `submitFeedback` não tem update: tag que +não entra na submissão não existe. É o filtro que a revisão periódica usa para +decidir o que sai da KB e vira regra de skill. + +--- + ## Passo 4 — Submeter via MCP Chamar a tool MCP `submitFeedback` com os campos aprovados: diff --git a/fluig/skills/implement/SKILL.md b/fluig/skills/implement/SKILL.md index 3781f1e..fc0c0ed 100644 --- a/fluig/skills/implement/SKILL.md +++ b/fluig/skills/implement/SKILL.md @@ -14,6 +14,13 @@ Não proceda se: Se algum desses pontos faltar, peça ao usuário para primeiro executar `/fluig:plan`. +## Passo 0 — Retomar, se houver estado + +Leia `docs/fluig/plans/.gates.json` antes de criar qualquer time. Estágios de +execução na ordem: `tests_unit` (duas metades) → `lint` → `spec_review` → +`code_review` → `deploy` → `qa_e2e`. Entre no primeiro estágio não-`ok` e diga ao dev +o que já passou. Sem `.gates.json` → recomende `/fluig:plan`. + ## Pré-requisitos - **Agent Teams** habilitado: `export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` @@ -29,16 +36,39 @@ Se Agent Teams não estiver habilitado, informe o usuário e caia no fallback (s | fluig-implementer | **sonnet** | Implementação — exige raciocínio sobre regra de negócio | | fluig-spec-reviewer | **sonnet** | Verificação de conformidade com spec | | fluig-reviewer | **sonnet** | Qualidade de código Fluig | -| Opus | **só no brainstorm** | O design já foi feito em opus (`/fluig:brainstorm`). Nenhum teammate escala sozinho | +| Opus | **só no brainstorm** | O design já foi feito em opus (`/fluig:brainstorm`). Nenhum teammate escala para opus por conta própria | | Haiku | **não usar aqui** | Haiku só em deploy (`fluig-deployer`) | ## Artefato de estado dos gates (gates.json) -Todo veredito de gate é GRAVADO em `docs/plans/.gates.json` — a skill seguinte LÊ o -arquivo em vez de confiar na memória da conversa. Atualize a chave do estágio ao concluí-lo: -`{ "tests_unit": {...}, "spec_review": "ok", "code_review": "ok", "lint": "ok", "deploy": {...}, "qa_e2e": {...} }`. -Pipeline retomável: sessão nova lê o arquivo e continua de onde parou. +Todo veredito de gate é GRAVADO em `docs/fluig/plans/.gates.json` (o slug é o basename, sem `.md`, do plano +salvo pelo `/fluig:plan`) — a skill seguinte LÊ o arquivo em vez de confiar na +memória da conversa (doc oficial: "hooks/artefatos são determinísticos; instruções +são consultivas"). Esquema único — toda chave é objeto com `status`: + +```json +{ + "slug": "2026-07-12-aprovacao-desconto", + "plan": { "status": "ok", "file": "docs/fluig/plans/2026-07-12-aprovacao-desconto.md" }, + "tests_unit": { + "widget": { "status": "ok", "coverage_lines": 84.2, "report": "coverage/coverage-summary.json" }, + "server": { "status": "ok", "failures": 0, "junit": "logs/fluig-unit.xml" } + }, + "lint": { "status": "ok" }, + "spec_review": { "status": "ok" }, + "code_review": { "status": "ok" }, + "deploy": { "status": "ok", "servidor": "https://fluig-hml.cliente.com.br" }, + "qa_e2e": { "status": "ok", "cenarios": 5, "junit": "logs/fluig-e2e.xml" } +} +``` + +`tests_unit` tem **duas metades**: `widget` (runner Angular) e `server` (harness Node +para datasets/eventos). Feature sem artefato de um dos lados grava +`{ "status": "n/a" }` naquela metade — `n/a` explícito, nunca chave ausente. + +Atualize a chave correspondente ao concluir cada estágio. Pipeline vira retomável: +sessão nova lê o arquivo e continua de onde parou. ## Estágio 0 — Criar Agent Team @@ -91,14 +121,42 @@ Aguarde resposta. Possíveis status: ### 2a-gate. Testes e cobertura (executado pelo ORQUESTRADOR) -Antes de despachar reviews, **você mesmo** valida (self-report do implementer NÃO aprova): +Antes de despachar reviews, **você mesmo** valida (self-report do implementer NÃO +aprova). São **duas metades** — rode a(s) que a task tocou: + +**Widget** (task mexeu em `.ts`/workspace Angular): ```bash npm test -- --watch=false --browsers=ChromeHeadless --code-coverage ``` -Verde é o critério mecânico (com o karma.conf do template, cobertura < 70% já FALHA). -Vermelho → devolva ao implementer; **máximo 3 ciclos** — no 3º, pare e reporte ao dev. +- Verde é o critério mecânico (com `skills/test/assets/karma.conf.template.js`, + cobertura global < 70% já FALHA aqui — se o projeto não tem o check, copie o + template; projeto no builder novo usa `coverageThresholds` do `angular.json`). +- Leia `coverage/coverage-summary.json`, registre o % real e grave + `tests_unit.widget` no `.gates.json`. + +**Server-side** (task mexeu em `ds_*`, `wf_*`, `events/`): + +```bash +mkdir -p logs +node --test --test-reporter=junit --test-reporter-destination=logs/fluig-unit.xml \ + --experimental-test-coverage --test-coverage-lines=70 \ + --test-coverage-include='**/ds_*.js' --test-coverage-include='**/wf_*.js' \ + --test-coverage-include='**/events/**' 'testes/**/*.test.js' +``` + +- Critério mecânico: exit 0 **e** `fail 0` no `logs/fluig-unit.xml` — **e** todo + `ds_*`/`wf_*`/evento tocado pela task tem arquivo de teste correspondente + (cobertura não enxerga arquivo que nenhum teste carrega: fonte sem teste passa + o threshold em silêncio — confira a existência um a um). Grave + `tests_unit.server` no `.gates.json`. +- Harness e armadilhas (filename absoluto, `deepEqual`, globs desancorados, + `mkdir -p logs`): ver a seção server-side de `/fluig:test`. + +Metade sem artefato na feature: grave `{ "status": "n/a" }` — explícito, nunca omitido. +Vermelho em qualquer metade → devolva ao implementer com a saída; **máximo de 3 +ciclos** de correção — no 3º vermelho, pare e reporte ao dev com o histórico. ### 2b. Dispatch para fluig-spec-reviewer (sonnet) @@ -147,11 +205,27 @@ Se o implementador usou worktree isolado: Quando todas as tasks estiverem ✅: -### 3a. Validação final global +### 3a. Lint gate global (máquina antes do LLM) + +Rode o ESLint sobre **todos** os artefatos da feature, com os configs do plugin +quando o projeto não tiver os seus: + +```bash +# server-side (ds_*, wf_*, events/) +npx eslint --config ${CLAUDE_PLUGIN_ROOT}/skills/test/assets/eslint.server.config.mjs +# widget (se o projeto não tem eslint próprio) +npx eslint --config ${CLAUDE_PLUGIN_ROOT}/skills/test/assets/eslint.widget.config.mjs +``` + +Critério mecânico: exit 0 nos dois. Grave `lint` no `.gates.json`. Erro → devolva ao +implementer (mesmo teto de 3 ciclos). Supressão (`eslint-disable`) só com justificativa +na mesma linha — e o reviewer reporta mesmo suprimido. + +### 3b. Validação final global Dispatch `fluig-reviewer` para validação de todos os artefatos juntos. -### 3b. Anunciar conclusão +### 3c. Anunciar conclusão ``` Implementação concluída diff --git a/fluig/skills/plan/SKILL.md b/fluig/skills/plan/SKILL.md index b5fe95e..e0bbac1 100644 --- a/fluig/skills/plan/SKILL.md +++ b/fluig/skills/plan/SKILL.md @@ -31,7 +31,7 @@ Com base no design, estruture a lista de **arquivos a criar ou modificar**: Consulte o MCP para a estrutura do artefato: ``` -searchFluigPatterns({ skill: "fluig-[tipo]", category: "template" }) +searchKnowledge({ platform: "fluig", skill: "", keyword: "template" }) ``` Use a estrutura retornada para mapear os arquivos a criar/modificar. @@ -44,10 +44,11 @@ Estruture o planejamento em **tasks** usando checkbox syntax. Cada task segue o ``` - [ ] **Task 1: Criar dataset ds_meu_dataset** - - [ ] Escrever teste unitário Jasmine em `ds_meu_dataset/dataset.spec.ts` (falha esperada) - - [ ] Verificar que o teste falha - - [ ] Implementar `ds_meu_dataset/dataset.java` com consulta SQL - - [ ] Executar `npm test` — teste passa + - [ ] Escrever teste unitário em `testes/ds_meu_dataset.test.js` com o harness do plugin + (`fluig-mock.cjs` — ver seção server-side de `/fluig:test`) — falha esperada + - [ ] Verificar que o teste falha (`node --test testes/ds_meu_dataset.test.js`) + - [ ] Implementar `datasets/ds_meu_dataset.js` (ES5/Rhino: var e function, sem let/const) + - [ ] Executar `node --test` — teste passa - [ ] Commit com mensagem: "feat(dataset): implementar ds_meu_dataset com query [resumo]" - [ ] **Task 2: Criar widget Angular wg_meu_widget com componente principal** @@ -79,8 +80,46 @@ Estruture o planejamento em **tasks** usando checkbox syntax. Cada task segue o - Comando exato para executar e validar (ng test, npm test, ng build, etc.) - Mensagem de commit esperada +## Passo 3.5 — Regressão: o que não pode quebrar + +Artefato Fluig vive em terreno compartilhado: o dataset que a demanda altera alimenta +outros formulários e widgets; o evento de processo roda para toda solicitação, não só +para a da demanda; o campo de formulário aparece em relatórios e integrações. + +Antes de salvar o plano, escreva `docs/legado/regressao/.md` — um item por +comportamento que **existia antes** e que a demanda não pode mudar: + +| ID | Origem (artefato + onde, ou `RN--NN`) | O que precisa continuar verdadeiro | Como verificar | Sinal de violação | +|---|---|---|---|---| +| W001 | `ds_clientes` (constraint `ativo`) | Widget de consulta continua listando só clientes ativos | teste harness `testes/ds_clientes.test.js` | cliente inativo aparece na lista | + +Regras: +- **Só entra item CONFIRMADO** (fato lido do projeto ou do ambiente — mapa da + `/fluig:arqueologia`). INFERIDO/LACUNA vai para "Observações", sem peso de regressão. +- Cada item vira teste do harness (`node --test`), cenário de spec E2E (`e2e/`), ou + checagem manual nomeada no `/fluig:verify`. Item sem forma de verificar é + observação. +- **IDs estáveis e append-only.** Item que deixou de valer vai para "Arquivadas" com + o motivo e a data — nunca apagado. +- Esta lista cobre só o que **existia antes**. O comportamento que esta demanda + **criar** entra na mesma lista no writeback do `/fluig:verify`, depois do QA. + +Adicione a linha `**Regressão:** docs/legado/regressao/.md` (ou "não se aplica — +justificativa") ao cabeçalho do plano. + ## Passo 4 — Salvar plano em docs/fluig/plans/ +Ao salvar o plano, grave também `docs/fluig/plans/.gates.json` (slug = basename +do plano, sem `.md`) — o estado dos gates que `/fluig:implement`, `deploy`, `qa` e +`verify` leem: + +```json +{ "slug": "", "plan": { "status": "ok", "file": "docs/fluig/plans/.md" } } +``` + +Se já existir um `.gates.json` com este slug, atualize só a chave `plan` e preserve +as demais (sobrescrever apagaria gates de execução já conquistados). + Crie ou use o diretório `docs/fluig/plans/` no projeto. Salve o plano com nome: ``` YYYY-MM-DD-.md @@ -155,3 +194,10 @@ Se precisar de informação não disponível no MCP, consulte o RAG: ``` searchKnowledge({ keyword: "" }) ``` + +## Rastreabilidade task → teste → evidência + +- Numere as tasks com ID estável (`T1`, `T2`, …) no plano. +- O teste de cada task referencia o ID: `describe('T1 — …')` e commit `feat(T1): …`. +- O spec-reviewer verifica "cada task tem teste com o ID dela"; evidências E2E em + `evidencias//`. diff --git a/fluig/skills/qa/SKILL.md b/fluig/skills/qa/SKILL.md index 8b1e47a..fbfcd0d 100644 --- a/fluig/skills/qa/SKILL.md +++ b/fluig/skills/qa/SKILL.md @@ -1,6 +1,6 @@ --- name: qa -description: Testes de integração e E2E (Playwright) contra servidor Fluig deployado. Aciona fluig:test e fluig-qa teammate. Se riscos ALTOS encontrados, retorna para /fluig:implement. Acione após /fluig:deploy. Próximo passo: /fluig:verify. +description: Testes de integração e E2E (Playwright) contra servidor Fluig deployado. Roda as specs E2E (JUnit) ou aciona fluig:test-web, e o fluig-qa teammate. Se riscos ALTOS encontrados, retorna para /fluig:implement. Acione após /fluig:deploy. Próximo passo: /fluig:verify. disable-model-invocation: true --- @@ -8,8 +8,9 @@ Você vai conduzir testes de integração e QA contra os artefatos deployados no ## HARD GATE -- **Leia `docs/plans/.gates.json`** e confirme: `deploy.status=ok` e `tests_unit.status=ok`. O arquivo é a fonte de - verdade — não confie em afirmação da conversa. +- **Leia `docs/fluig/plans/.gates.json`** e confirme: `deploy.status=ok`, + `tests_unit.widget.status` e `tests_unit.server.status` ∈ {ok, n/a}, `lint.status=ok`. + Não confie em afirmação da conversa — o arquivo é a fonte de verdade (pipeline retomável). O `` é o basename (sem `.md`) do plano salvo pelo `/fluig:plan` em `docs/fluig/plans/`; havendo mais de um `.gates.json`, pergunte ao dev qual feature. Não inicie testes de QA se: - O deploy não foi realizado com sucesso (não há servidor com os artefatos) @@ -19,15 +20,27 @@ Verifique que o deploy do Passo 3 anterior foi concluído antes de prosseguir. ## Passo 1 — Executar testes E2E (Playwright) -Acione a skill `/fluig:test` solicitando execução dos testes E2E contra o servidor deployado: +Primeiro, **liste os artefatos da task** (do plano) e confira, um a um, qual spec em +`e2e/` cobre cada um. Spec de demanda anterior não conta como cobertura da task atual. +**1a. Existe spec cobrindo CADA artefato da task** — rode o gate mecânico: + +```bash +mkdir -p logs +PLAYWRIGHT_JUNIT_OUTPUT_NAME=logs/fluig-e2e.xml npx playwright test --reporter=junit ``` -Execute os testes Playwright E2E dos artefatos [nomes] contra o servidor [URL do deploy anterior]. -Se NÃO existirem testes E2E para os artefatos da task: **PARE e gere-os via -`/fluig:test`** (com aprovação do roteiro) antes de prosseguir — QA sem E2E é -reprovação automática. Se existirem, execute os existentes (não duplique). -Use FLUIG_BASE_URL apontando para o servidor (nunca localhost). -``` + +Critério: exit 0. Leia o XML (não aceite afirmação da conversa), grave `qa_e2e` no +`.gates.json` com o caminho do relatório. Falha → screenshot + log do servidor, e +volta ao `/fluig:implement`. + +**1b. Algum artefato da task SEM spec** — **PARE e acione `/fluig:test-web`** para +esses artefatos (roteiro aprovado → execução com evidências → spec de regressão +gerado da sessão validada), e só então rode o 1a. QA sem E2E **dos artefatos da +task** é reprovação automática, não aprovação vazia — suíte antiga verde não aprova +artefato novo. + +Use FLUIG_BASE_URL apontando para o servidor real (homologação ou sandbox do `/fluig:base` — nunca mock/porta morta). Os testes E2E devem validar: - Carregamento dos formulários e datasets no servidor real @@ -55,7 +68,21 @@ O agente `fluig-qa` acessa o servidor real para validar comportamento e identifi ## Passo 3 — Avaliar resultados -Revise a análise do fluig-qa: +Revise a análise do fluig-qa. + +### Lista FECHADA de risco ALTO + +Qualquer item abaixo é **ALTO por definição** — nada desta lista pode ser rebaixado +por "parece ok", "cenário improvável" ou impressão visual: + +1. Caso de teste E2E cujo critério verificável **não foi encontrado** na tela/snapshot +2. Erro no log do servidor Fluig durante a execução dos testes (stacktrace, HTTP 5xx) +3. Dataset devolvendo linha de erro estruturada (ou exceção) em cenário **feliz** +4. SQL/constraint montado por concatenação de entrada do usuário em código novo +5. Campo obrigatório do formulário gravando vazio sem validação +6. Qualquer gate anterior vermelho ou ausente no `.gates.json` + +Fora da lista, classifique MÉDIO/BAIXO por julgamento — mas a lista acima não se julga. ### Se houver riscos ALTO: @@ -91,7 +118,7 @@ Próximo passo: /fluig:verify ## Regras obrigatórias -- Testes E2E sempre contra servidor real (nunca localhost) +- Testes E2E sempre contra servidor real (homologação ou sandbox do `/fluig:base` — nunca mock/porta morta) - Fluig-qa sempre executado após E2E (análise no servidor live) - Riscos ALTOS obrigam retorno para `/fluig:implement` — não pule - Riscos MÉDIO/BAIXO são documentados mas não bloqueiam diff --git a/fluig/skills/review/SKILL.md b/fluig/skills/review/SKILL.md index 6d4f543..7198f27 100644 --- a/fluig/skills/review/SKILL.md +++ b/fluig/skills/review/SKILL.md @@ -71,7 +71,7 @@ Acione a skill `/fluig:test` solicitando execução dos testes E2E: Se não houver testes E2E ainda: > "Gere e execute os testes Playwright E2E para o artefato [nome] contra o servidor [URL]." -Os testes E2E usam `FLUIG_BASE_URL` apontando para o servidor do Passo 3 — nunca localhost. +Os testes E2E usam `FLUIG_BASE_URL` apontando para o servidor do Passo 3 — servidor real: homologação ou sandbox do /fluig:base. **Se os testes E2E falharem:** corrija o artefato, redeploy (Passo 3) e execute novamente. @@ -115,7 +115,7 @@ Próximo passo: /fluig:verify → deploy final - Os 5 passos são sequenciais — nunca em paralelo - Testes unitários antes do deploy (Jasmine/Karma rodam localmente) - Deploy obrigatório antes dos testes E2E (Playwright exige servidor real) -- E2E nunca usa localhost — sempre o servidor do Passo 3 +- E2E usa servidor real: homologação ou o sandbox do /fluig:base — sempre o servidor do Passo 3 - Sempre ler CLAUDE.md para saber servidor e prefixo antes de acionar qualquer agente - Sempre confirmar com o usuário antes de fazer qualquer deploy diff --git a/fluig/skills/test-web/SKILL.md b/fluig/skills/test-web/SKILL.md new file mode 100644 index 0000000..8c93b46 --- /dev/null +++ b/fluig/skills/test-web/SKILL.md @@ -0,0 +1,156 @@ +--- +name: test-web +description: Ciclo completo de testes E2E do TOTVS Fluig via Playwright com evidências e documentação. Cobre roteiro → aprovação → execução com screenshots → validação → error handling (log do servidor) → specs de regressão com JUnit. Use quando precisar testar widget, formulário, dataset publicado ou processo BPM no servidor Fluig real, coletar evidências ou gerar documentação de testes. +--- + +# Fluig Test Web — Testes E2E com Evidências e Gate Mecânico + +Ciclo completo de testes E2E contra o servidor Fluig real, em **dois modos**: + +- **Modo exploratório (evidência):** MCP Playwright interativo — roteiro aprovado, + screenshot por passo, validação com critério verificável. É onde um cenário novo + nasce e é validado. +- **Modo gate (regressão):** specs `@playwright/test` versionadas, geradas a partir da + sessão exploratória **já validada**, executadas com `--reporter=junit`. Exit code é o + critério — sem LLM no loop. É o que o `/fluig:qa` consome como gate. + +O Fluig é aplicação web comum (ao contrário do webapp Protheus): autenticação via +`storageState`, seletores DOM normais. Prefira seletores estáveis; screenshots são a +evidência, não o mecanismo de localização. + +## Pré-requisitos + +- Plugin MCP Playwright instalado (`/plugin` → instalar `playwright`) +- URL do servidor Fluig real (a proibição de `localhost` barra mock/porta morta — o sandbox real do `/fluig:base` vale) — do CLAUDE.md do projeto ou do dev +- Credenciais via `FLUIG_USER` / `FLUIG_PASSWORD` (nunca hardcoded) +- Artefato **publicado** no servidor (`/fluig:deploy` concluído — confira o + `.gates.json`) +- Página onde o widget/form está publicado (ex.: `/portal/p/home`) + +## Etapa 1 — Roteiro de testes (obrigatório, ANTES de executar) + +Consulte a base antes de elaborar: + +``` +searchKnowledge({ platform: "fluig", skill: "fluig-test", keyword: "" }) +ragSearchKnowledge({ query: "cenários de teste para no Fluig" }) +``` + +Formato do roteiro a apresentar ao team leader: + +```markdown +# Roteiro de Teste: + +**Artefato(s):** +**Servidor:** +**Página:** +**Data:** + +## Objetivo + + +## Pré-condições + + +## Cenários + +### TC01 — +- **Ação:** +- **Dados:** +- **Resultado esperado:** +- **Critério VERIFICÁVEL:** + +## Cleanup + +``` + +**REGRA:** só executar após aprovação explícita do team leader. + +## Etapa 2 — Evidências + +``` +evidencias// +└── YYYY-MM-DD__/ + ├── 01_login.png + ├── 02_pagina_widget.png + ├── ... + └── relatorio.md +``` + +## Etapa 3 — Execução (modo exploratório) + +Screenshot via `browser_take_screenshot` em **cada passo** — sem evidência não há +teste válido. + +1. `browser_navigate` → URL do servidor; login com `FLUIG_USER`/`FLUIG_PASSWORD` + (se SSO/Identity, siga o fluxo real e registre qual é) — **Screenshot:** `01_login.png` +2. Navegue até a página do artefato — **Screenshot:** `02_pagina_widget.png` +3. **Formulário dentro de processo:** abra a tarefa pela Central de Tarefas. Atenção: + o form pode renderizar em **iframe** — se o snapshot não mostrar os campos, procure + o frame antes de concluir que a tela não carregou. +4. Execute a ação do TC (preencher, enviar, movimentar atividade) — screenshot a cada + preenchimento relevante. +5. **Validação:** procure o **critério verificável literal** no snapshot — + **Screenshot:** `0N_resultado.png`. Registre PASSOU / FALHOU / BLOQUEADO. +6. **Cleanup:** reverta dados criados — screenshot final. + +## Etapa 4 — Tratamento de erros (log do servidor) + +Erro na tela (mensagem genérica, tela branca, HTTP 5xx) durante o teste: + +1. **Screenshot imediato** do estado da tela +2. Colete o log do servidor Fluig no período do teste (Docker: + `docker logs --since `; ver `/fluig:debug` para o setup + do projeto) e recorte o stacktrace +3. Classifique: erro de ambiente (config/permissão), erro de artefato (dev), erro de dados +4. **Devolva ao desenvolvedor** com screenshot + stacktrace + análise + sugestão +5. **NÃO prossiga** até o erro ser resolvido; registre a lição via `/fluig:feedback` + +Erro no log do servidor durante o teste é risco **ALTO por definição** na lista +fechada do `/fluig:qa` — não rebaixe. + +## Etapa 5 — Review das evidências + +Apresente ao team leader: lista de screenshots, resultado por cenário +(PASSOU/FALHOU com o critério encontrado/não encontrado), erros com stacktrace. + +## Etapa 6 — Modo gate: spec de regressão + +Cenário validado no exploratório vira spec versionada — a sessão valida com +evidência; o spec vira regressão determinística: + +```bash +mkdir -p logs +PLAYWRIGHT_JUNIT_OUTPUT_NAME=logs/fluig-e2e.xml npx playwright test e2e/.spec.ts --reporter=junit +``` + +- Autenticação via `storageState` (setup project) — nunca login por spec. +- Cada `expect` do spec é o critério verificável do TC correspondente — mesma + fonte, dois consumidores. +- Critério mecânico: exit 0. Grave `qa_e2e` no `.gates.json` com o caminho do XML. +- Specs vivem no repo do projeto (`e2e/`), rodam em CI e no `/fluig:qa` das + próximas demandas. + +**A spec versionada É a persistência do cenário.** O roteiro aprovado e as evidências +ficam em `evidencias//`, junto do repositório do projeto — é o que a próxima +demanda lê. + +## Etapa 7 — Documentação e lições + +- Falha, armadilha ou lição aprendida durante o teste → registre via `/fluig:feedback` + (que submete à base de conhecimento com sua aprovação). +- Documentação formal (quando o cliente exigir): relatório em Markdown/DOCX com + screenshots inline, resultado esperado × obtido por cenário e conclusão + APROVADO/REPROVADO. + +## Regras críticas + +- **Aprovação do roteiro antes de executar** — sem exceção. +- **Screenshot em cada passo** — sem evidência não há teste válido. +- **Critério verificável literal** — "parece certo" não é critério. +- **Nunca credencial hardcoded. `localhost` só se for o sandbox real do `/fluig:base`.** +- Snapshot sem os campos esperados: verifique **iframe** e tempo de carregamento + antes de reportar falha. +- Dialogs/toasts (Swal): verifique antes de interagir com a tela. diff --git a/fluig/skills/test/SKILL.md b/fluig/skills/test/SKILL.md index e2a8296..65f66cf 100644 --- a/fluig/skills/test/SKILL.md +++ b/fluig/skills/test/SKILL.md @@ -1,84 +1,122 @@ --- name: test -description: Gera e orienta execução de testes para widgets TOTVS Fluig. Unit tests com Jasmine + Karma e E2E com Playwright. Sempre questiona a URL do servidor antes de gerar configurações E2E. O E2E requer build + deploy antes de executar. +description: Gera e orienta execução de testes UNITÁRIOS para artefatos TOTVS Fluig — widget Angular (runner do projeto) e dataset/evento server-side (harness Node + fluig-mock, sem servidor). Para E2E no servidor real (roteiro, evidências, specs de regressão), use /fluig:test-web. --- -Você vai ajudar a criar e executar testes para widgets Fluig. +Você vai ajudar a criar e executar testes para artefatos Fluig. -Existem dois tipos de teste: -- **Unit (Jasmine + Karma):** testa componentes, services e pipes isoladamente, sem servidor -- **E2E (Playwright):** testa o widget publicado no servidor Fluig real +Esta skill cobre os testes **unitários** (sem servidor): +- **Unit de widget** (runner do projeto Angular): componentes, services e pipes +- **Unit server-side** (harness Node do plugin): datasets `ds_*`, eventos `wf_*` e eventos de + form — o fonte roda inalterado no Node (Rhino é ECMA 5, subconjunto do Node) ---- - -## HARD GATE — Antes de qualquer E2E - -Antes de gerar qualquer configuração E2E ou executar testes E2E, você DEVE: - -1. Perguntar ao usuário a URL do servidor Fluig (ex: `https://fluig.empresa.com.br`) -2. Confirmar que o widget já foi publicado **ou** executar o fluxo de build + deploy agora -3. Nunca usar `localhost` como base URL — widgets Fluig só rodam no servidor Fluig - -Use `AskUserQuestion` para coletar: -- URL do servidor Fluig -- Usuário e senha (ou confirmar que `FLUIG_USER`/`FLUIG_PASSWORD` estão definidos) -- Página Fluig onde o widget está publicado (ex: `/portal/p/home`) +**E2E no servidor real é da `/fluig:test-web`** (roteiro aprovado, evidências, +specs de regressão com JUnit) — não gere configuração E2E por aqui. --- -## Busca de Testes Existentes (Context Enrichment) +## Unit server-side — dataset e eventos (harness Node, sem servidor) + +O motor server-side do Fluig é **Rhino, ECMA 5** — e ES5 é subconjunto do que o Node +executa. O harness do plugin carrega o fonte `ds_*.js`/`wf_*.js` **sem alterar nada** +num contexto com os globais Fluig mockados, e o `node --test` (nativo, zero dependência, +Node 22+) dá o gate mecânico. + +Ciclo: identifique o alvo (função de regra pura primeiro — a `/fluig:clean-architecture` +já exige regra de negócio separada de `hAPI`/`DatasetFactory`) → casos feliz, borda e +erro de infra → escreva o teste com o harness: + +```js +const test = require('node:test'); +const assert = require('node:assert'); +const { carregar } = require('${CLAUDE_PLUGIN_ROOT}/skills/test/assets/fluig-mock.cjs'); + +test('createDataset devolve linha estruturada', () => { + const ctx = carregar('./datasets/ds_meu.js'); + const ds = ctx.createDataset(null, [], null); + assert.deepEqual(ds.rows, [['001', true]]); // deepEqual, NUNCA deepStrictEqual +}); + +test('falha de infra não vaza exceção', () => { + const ctx = carregar('./datasets/ds_meu.js', { + DatasetFactory: { getDataset: () => { throw new Error('offline'); } } + }); + // conforme o contrato do fonte: linha de erro estruturada, ou throw esperado +}); +``` -Após confirmar o widget/módulo, consulte testes similares antes de gerar: +Gate mecânico (executado pelo orquestrador — self-report não aprova): -``` -listTests({ platform: "fluig", module: "", limit: 5 }) +```bash +mkdir -p logs +node --test --test-reporter=junit --test-reporter-destination=logs/fluig-unit.xml \ + --experimental-test-coverage --test-coverage-lines=70 \ + --test-coverage-include='**/ds_*.js' --test-coverage-include='**/wf_*.js' \ + --test-coverage-include='**/events/**' 'testes/**/*.test.js' ``` -- Se retornar resultados: mostre os cenários existentes e pergunte se quer algo diferente ou complementar -- Se retornar vazio: prossiga normalmente -- Use os exemplos para manter consistência de page objects e seletores +Critério: exit code 0 **e** `fail 0` no XML — **e** a checagem de existência abaixo. + +**Checagem de existência (a cobertura NÃO faz isso):** arquivo que nenhum teste +carrega é **invisível** para o relatório — dataset novo sem um único teste passa o +threshold verde. Antes de aprovar, confira um a um: **todo `ds_*`/`wf_*`/evento tocado +pela task tem arquivo de teste correspondente em `testes/`**. Fonte sem teste = gate +vermelho, independente do percentual. + +**Quatro armadilhas (não ignore):** +1. O harness passa `filename` **absoluto** ao `vm` — com caminho relativo o fonte some + do relatório de cobertura em silêncio. +2. Compare linhas com `assert.deepEqual`, nunca `deepStrictEqual` — os arrays nascem + dentro do `vm`, em outro realm, e o strict falha com conteúdo idêntico. +3. O threshold **só morde com `--test-coverage-include`** apontando para os fontes — + sem o include, mock e teste (100%) diluem a média. Use padrões **desancorados** + (`'**/ds_*.js'`, `'**/events/**'`): evento de form mora em + `formularios/
/events/`, e `'events/**'` ancorado na raiz não o alcança. +4. `mkdir -p logs` antes: com o diretório inexistente o Node aborta no reporter antes + de rodar teste algum; e o argumento posicional precisa ser glob de arquivos + (`'testes/**/*.test.js'`), não diretório. + +**Limite de fidelidade, dito em voz alta:** o Node valida a **lógica**, não o Rhino. +Interop Java (`java.*`, `java.lang.String` retornado por `hAPI`/`getValue`) pode divergir — +isso só o teste de integração pós-deploy no `/fluig:qa` pega. O harness não substitui o QA; +o QA não substitui o harness. --- -## Unit Tests — Jasmine + Karma - -Estrutura esperada, configuração karma.conf.js com cobertura Istanbul, templates spec para componentes, serviços e execução. +## Unit de widget — runner do PROJETO (nunca fixe) -Consulte a referência completa via MCP: -``` -searchKnowledge({ skill: "fluig-test", keyword: "unit tests" }) -``` - ---- - -## Configuração de cobertura (obrigatória) +O runner vem do projeto, não do plugin: leia `angular.json` e `package.json` antes de +gerar qualquer spec ou comando. -Se o projeto **não tem** `karma.conf.js` com `coverageReporter.check`, copie o template do -plugin — cobertura vira **gate mecânico** (`npm test` falha sozinho abaixo de 70%): +- **Builder atual** (`@angular/build:unit-test`): o default é **vitest**, com `karma` + suportado via `--runner`. Gate: `ng test` com `--reporters junit` + `coverageThresholds` + no `angular.json` (o comando falha sozinho abaixo do limite). +- **Projeto legado com `karma.conf.js`:** mantenha Karma. Se não houver + `coverageReporter.check`, copie o template do plugin — ele transforma a cobertura em + gate mecânico (`npm test` falha sozinho abaixo de 70%): ```bash -cp /skills/test/assets/karma.conf.template.js ./karma.conf.js +cp ${CLAUDE_PLUGIN_ROOT}/skills/test/assets/karma.conf.template.js ./karma.conf.js npm test -- --watch=false --browsers=ChromeHeadless --code-coverage +node -e "const c=require('./coverage/coverage-summary.json').total; console.log(JSON.stringify(c.lines))" ``` -## E2E Tests — Playwright - -> **Playwright é a engine oficial de E2E do plugin** — visão computacional real da tela -> (screenshots + snapshots), não automação cega de seletores. - -HARD GATE: build + deploy obrigatório antes dos testes. +Leia o resumo de cobertura do arquivo (não aceite auto-relato). Templates de spec: +consulte a referência via MCP: +``` +searchKnowledge({ platform: "fluig", skill: "fluig-test", keyword: "unit tests" }) +``` -**Restrições Fluig obrigatórias:** -- `BASE_URL` = URL do servidor Fluig real (ex: `https://fluig.empresa.com.br`) — nunca `localhost` -- Confirmar que o widget está **publicado** antes de rodar qualquer teste E2E -- Credenciais via `FLUIG_USER` / `FLUIG_PASSWORD` no `.env.test` — nunca hardcoded -- A página de teste deve ser a página Fluig onde o widget está publicado (ex: `/portal/p/home`) +--- -Para setup completo, `playwright.config.ts`, CLI commands, autenticação via `storageState` e templates, use o plugin playwright: +## E2E — vá para `/fluig:test-web` -``` -/playwright:playwright-cli -``` +E2E contra o servidor real saiu desta skill: o protocolo completo (roteiro aprovado +pelo team leader, screenshots por passo em `evidencias/`, critério verificável +literal, tratamento de erro com log do servidor, spec de regressão com +`--reporter=junit`) vive na **`/fluig:test-web`**. As restrições permanecem lá: +servidor real (nunca `localhost`), artefato publicado, credenciais por variável de +ambiente. --- @@ -95,41 +133,5 @@ Para setup completo, `playwright.config.ts`, CLI commands, autenticação via `s Se precisar de informação não disponível no MCP, consulte o RAG: ``` -searchKnowledge({ keyword: "" }) +ragSearchKnowledge({ query: "" }) ``` - ---- - -## Gravar Teste no MCP (após validação) - -Após confirmação que o teste E2E passou no servidor Fluig real: - -> "Teste validado. Quer salvar na base de conhecimento para outros devs reutilizarem?" - -Apresente o rascunho para revisão antes de persistir: - -``` -platform: fluig -module: -title: -scenario: -script: -tags: -``` - -Somente após confirmação explícita do dev, chame: - -``` -saveTest({ - platform: "fluig", - module: "", - title: "", - scenario: "", - script: "