From 8bca9d8e82fc61436c4a24ee5457e98eb0b5b8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sun, 2 Aug 2026 14:01:57 -0300 Subject: [PATCH 1/3] Separate intention plans from engineering execution in agent skills. plan-issue now shapes product intent only; agent-work-issue (pool) and work-issue (human gate) deliberate the implementation plan before coding. --- .cursor/skills/agent-pool/SKILL.md | 2 +- .cursor/skills/agent-work-issue/SKILL.md | 107 +++++++++ .cursor/skills/capture-review-debts/SKILL.md | 17 +- .cursor/skills/engineering-audit/SKILL.md | 2 +- .cursor/skills/local-database/SKILL.md | 2 +- .cursor/skills/model-selection/SKILL.md | 14 +- .cursor/skills/plan-issue/SKILL.md | 146 ++++++------ .../skills/plan-issue/data-presentation.md | 115 ++-------- .cursor/skills/plan-issue/decision-quality.md | 147 ------------ .../skills/plan-issue/intention-template.md | 89 ++++++++ .cursor/skills/plan-issue/plan-template.md | 209 ------------------ .cursor/skills/plan-issue/shaping.md | 56 +++++ .cursor/skills/rebase-on-main/SKILL.md | 5 +- .cursor/skills/work-issue/SKILL.md | 175 ++++----------- .cursor/skills/work-issue/decision-quality.md | 51 +++++ .../skills/work-issue/engineering-brief.md | 36 +++ .../work-issue/implementation-template.md | 65 ++++++ docs/AGENT-OPS.md | 2 +- docs/CHANGELOG-AGENTS.md | 2 + scripts/agent-pool.mjs | 2 +- scripts/lib/agent-pool-prompt.mjs | 23 +- tests/unit/agentPoolPrompt.unit.spec.ts | 6 + 22 files changed, 592 insertions(+), 681 deletions(-) create mode 100644 .cursor/skills/agent-work-issue/SKILL.md delete mode 100644 .cursor/skills/plan-issue/decision-quality.md create mode 100644 .cursor/skills/plan-issue/intention-template.md delete mode 100644 .cursor/skills/plan-issue/plan-template.md create mode 100644 .cursor/skills/plan-issue/shaping.md create mode 100644 .cursor/skills/work-issue/decision-quality.md create mode 100644 .cursor/skills/work-issue/engineering-brief.md create mode 100644 .cursor/skills/work-issue/implementation-template.md diff --git a/.cursor/skills/agent-pool/SKILL.md b/.cursor/skills/agent-pool/SKILL.md index 381e27b2..e96ced86 100644 --- a/.cursor/skills/agent-pool/SKILL.md +++ b/.cursor/skills/agent-pool/SKILL.md @@ -11,7 +11,7 @@ description: >- # Agent pool — operação do supervisor remoto -O pool mantém até **5 Cursor Cloud Agents** (configurável, máx 12) rodando `work-issue` sobre Issues `ready` elegíveis para autonomia. O supervisor é **determinístico** (não é um agente): workflow **`.github/workflows/agent-pool.yml`** (tick stateless a cada 10 min + a cada merge em `main` + sob dispatch) rodando `scripts/agent-pool.mjs`. Arquitetura e elegibilidade: `docs/plans/agent-pool-orchestrator.md`. +O pool mantém até **5 Cursor Cloud Agents** (configurável, máx 12) rodando `agent-work-issue` sobre Issues `ready` elegíveis para autonomia. O supervisor é **determinístico** (não é um agente): workflow **`.github/workflows/agent-pool.yml`** (tick stateless a cada 10 min + a cada merge em `main` + sob dispatch) rodando `scripts/agent-pool.mjs`. Arquitetura e elegibilidade: `docs/plans/agent-pool-orchestrator.md`. **O pool NUNCA deploya** — deploy gated fica em `ci.yml` após merge em `main`. diff --git a/.cursor/skills/agent-work-issue/SKILL.md b/.cursor/skills/agent-work-issue/SKILL.md new file mode 100644 index 00000000..7684d0a9 --- /dev/null +++ b/.cursor/skills/agent-work-issue/SKILL.md @@ -0,0 +1,107 @@ +--- +name: agent-work-issue +description: >- + Executa uma Issue já claimada de ponta a ponta para workers do agent pool: + Plan mode → plano de implementação (engenharia deliberada a partir da + intenção) → execução → /simplify → capture-review-debts → PR Ready em main + com auto-merge. Use quando o pool spawna um worker, quando a Issue já está + in-progress, ou quando o usuário pedir /agent-work-issue. +disable-model-invocation: true +--- + +# Agent work-issue (autônomo) + +Conduz **uma** Issue já claimada do plano de implementação ao merge em `main`. É o contrato dos workers do **agent pool** e a base de execução de `work-issue` (versão humana só adiciona claim + pausa). + +**Proibido:** DB de prod / `ALLOW_REMOTE_DB`; merge sem CI green; editar outras Issues `in-progress`; pular `/simplify`; entregar Draft ou sem auto-merge. + +**Fonte de produto:** plano de intenção (`docs/plans/.md`) + body da Issue. +**Fonte de engenharia:** o `*-impl.md` que **você** cria — não o plano de intenção. + +## Relação com outras skills + +| Skill | Papel | +| ----- | ----- | +| `plan-issue` | Intenção (já feita) | +| **`agent-work-issue` (esta)** | Impl plan → execução → simplify → débitos → PR | +| `work-issue` | Igual, mas claim + **confirmação humana** do impl plan | + +## Checklist + +``` +- [ ] 0. Prep: `pnpm i` se preciso; Cloud → §Prep Cloud (sem Docker) +- [ ] 1. Sessão: rename_chat + open_resource na intenção +- [ ] 2. Modelo (best effort) vs `model:` da Issue +- [ ] 3. Plan mode → plano de implementação (`*-impl.md`) +- [ ] 4. Executar o impl plan (schema/server → UI → gates) +- [ ] 5. /simplify → fixes; capture-review-debts (modo autônomo) +- [ ] 6. PR Ready `--base main` + auto-merge + checks --required +``` + +## Passo 0 — Prep + +```bash +pnpm i # se node_modules ausente/stale +``` + +### Cloud (sem Docker) + +Não use `pnpm db:start`. Postgres via `.cursor/environment.json`. `gate:fast` / `gate:push` não precisam de DB. **Proibido** `git push --no-verify` no fechamento. + +## Passo 1 — Sessão (Issue já claimada) + +**Não** rode `pnpm agent:claim` — o pool (ou o humano via `work-issue`) já claimou. + +1. `rename_chat` → `# — ` (padrão canônico; truncar só o título). +2. `open_resource` no plano de intenção (`docs/plans/.md`). + +## Passo 2 — Modelo + +Compare `model:` da Issue com a sessão (`model-selection`): + +- Sessão mais fraca → informa e segue +- Sessão mais forte → informa; em autonomia do pool, **segue** (o spawn já fixou o modelo) +- Ausente → trate como `composer-2.5` + +## Passo 3 — Plano de implementação (Plan mode) + +1. `SwitchMode` → `plan` (explique: deliberar engenharia a partir da intenção). +2. Leia [engineering-brief.md](../work-issue/engineering-brief.md) e aplique skills/princípios sob demanda. +3. Explore o código o bastante para **reavaliar** a “Direção no codebase” da intenção — você **deve** escolher a melhor abordagem maintainable, mesmo que difira da hipótese do plano de intenção, desde que o **aceite de produto** se mantenha. +4. Escreva `docs/plans/-impl.md` via [implementation-template.md](../work-issue/implementation-template.md). Qualidade: [decision-quality.md](../work-issue/decision-quality.md) ≥4/5. +5. Em autonomia: marque Status `aprovado` e **siga sem pausa**. (Em `work-issue`, o humano confirma antes.) + +**Divergência material de produto** (aceite/persona/lockdown da intenção não cabem na abordagem): pare, comente na Issue, flip para `blocked` se pool — não invente produto novo. + +## Passo 4 — Executar + +`SwitchMode` → `agent` se ainda estiver em plan. Ordem: + +1. **Schema/server** — migrations (`payload-migrations`), utilities, actions, testes de domínio. Invariantes do engineering-brief. +2. **UI** — se Impeccable B/C/D: shape → craft → critique → polish (harden/optimize só sob gatilho). Tokens `data-theme='campaign'`; shells existentes. +3. **Gates** — `pnpm gate:fast` na iteração; entrega com `pnpm push` (não `git push` nu). + +Tracer bullet cedo se o item for grande. Inclua o `*-impl.md` no commit da entrega. + +## Passo 5 — `/simplify` + débitos + +1. Rode o comando `/simplify` completo (3 reviewers paralelos via Task, read-only) no diff da sessão. +2. Aplique fixes pontuais que preservem comportamento. +3. Rode `capture-review-debts` em **modo autônomo**: + - Colha e pontue como a skill manda + - **Sem** gate humano: registre só `expensive_lock` com score ≥4 (Issues novas com `depends: []` se necessário) + - Score ≤3 / cheap_polish / defer_trigger → defer com gatilho no `*-impl.md` ou descarte — **não** polua a fila + - Nunca edite a Issue `in-progress` atual para absorver débitos + +## Passo 6 — Fechar em main + +1. Branch `agent/-` (worktrees Cursor podem já ter criado). +2. **`pnpm push -u origin HEAD`** +3. PR **Ready** (nunca draft): `gh pr create --base main` com `Closes #` — ou Cloud `ManagePullRequest` com `draft: false` +4. `gh pr merge --auto --merge` imediatamente +5. `gh pr checks --watch --required` (`checks` + `migration-lock`; ignore Vercel Git) +6. CI flipa `done`/`in-prod` no merge. Comente na Issue o desfecho em uma linha. + +## Resumo final + +Issue · impl plan (abordagem escolhida + rejeitadas) · o que divergiu da hipótese de direção · simplify + débitos registrados/deferidos · PR + estado do merge. diff --git a/.cursor/skills/capture-review-debts/SKILL.md b/.cursor/skills/capture-review-debts/SKILL.md index c834c85c..e17fb194 100644 --- a/.cursor/skills/capture-review-debts/SKILL.md +++ b/.cursor/skills/capture-review-debts/SKILL.md @@ -9,7 +9,7 @@ Após um ciclo de entrega, `/simplify` e `/impeccable` deixam achados **maiores **Regra inviolável — nunca editar Issue `in-progress`.** Nem para absorver um débito do mesmo pai: registra como **Issue nova** com `depends: []` no frontmatter — assim ela destrava sozinha na fila quando o pai flipar `done` — ou defer com gatilho. Editar a Issue que um agente está executando é esquecimento/conflito garantido. -**Qualidade de decisão:** [decision-quality.md](../plan-issue/decision-quality.md) — caro vs barato, defer+gatilho, depth/YAGNI. Score sozinho não basta: classifique o **tipo de decisão** na triage. Sem tour de fases. +**Qualidade de decisão:** [decision-quality.md](../work-issue/decision-quality.md) — caro vs barato, defer+gatilho, depth/YAGNI. Score sozinho não basta: classifique o **tipo de decisão** na triage. Sem tour de fases. ## Checklist @@ -124,7 +124,7 @@ Mostre ao usuário **antes** de editar docs: Inclua baldes **já_resolvido**, **descartar** e **defer** com uma linha de racional/gatilho cada (transparência > silêncio). -**Pare e confirme.** Só avance ao Passo 6 com aprovação explícita (ou ajuste pedido). Sem confirmação = não cria Issue nem toca `docs/plans/`. +**Pare e confirme** — exceto em **modo autônomo** (`agent-work-issue` / pool): aí aplique a seção Modo autônomo abaixo e pule o AskQuestion. Só avance ao Passo 6 com aprovação explícita (humano) ou com as regras do modo autônomo. Sem isso = não cria Issue nem toca `docs/plans/`. ## Passo 6 — Registrar via `agent:register` / `agent:file-miss` @@ -139,9 +139,18 @@ Para cada lote com destino **registrar** ou **absorver**: - **Explicitamente fora** (skips dos revisores + descartes + defers com gatilho deste triage) 6. Self-score de decisão ≥4/5 antes de gravar; classe Impeccable A–D conforme o lote (só-backend = A; UX pós-critique = B/C). -Não implemente as fases aqui. Execução é via `work-issue` (claim → merge em stage), só se o usuário pedir em seguida. +Não implemente as fases aqui. Execução é via `work-issue` / `agent-work-issue`, só se o usuário (ou o pipeline autônomo) seguir. -**Próximo no fluxo de entrega:** após a triage confirmada (ou no-op sem débitos), o fechamento da sessão segue o Passo 6 da skill `work-issue` (fast gate → PR `--base stage` com `Closes #N` → acompanhar CI até o merge). +**Próximo no fluxo de entrega:** após a triage (confirmada ou modo autônomo), o fechamento segue o Passo 6 de `agent-work-issue` / `work-issue` (`pnpm push` → PR `--base main` com `Closes #N` → auto-merge → CI). + +## Modo autônomo (`agent-work-issue` / pool) + +Quando não há humano no gate (Passo 5): + +1. Faça colheita, dedup, score e tipo como de costume. +2. **Registre** só itens `expensive_lock` com score ≥4 (Issue nova + `depends` no pai se preciso; plano curto se score ≥3 eng). +3. Todo o resto → **defer** (gatilho no `*-impl.md` / Explicitamente fora) ou **descartar** — não abra AskQuestion. +4. Resuma no comentário/fechamento o que registrou vs deferiu. ## Anti-padrões (baseline) diff --git a/.cursor/skills/engineering-audit/SKILL.md b/.cursor/skills/engineering-audit/SKILL.md index 6ca83e22..a2ab9ef0 100644 --- a/.cursor/skills/engineering-audit/SKILL.md +++ b/.cursor/skills/engineering-audit/SKILL.md @@ -50,7 +50,7 @@ Read, in this order: 2. `.cursor/rules/codebase-map.mdc` — dependency direction, where things live, the list system, invariants. 3. `docs/ARCHITECTURE.md` — layers, bounded contexts, decision log. 4. `AGENTS.md` — operational rules + "Recently resolved" history (Pass 1, Pass 2, every post-Pass-2 delivery's /simplify findings). -5. `docs/AGENT-OPS.md` — paradigma de agentes paralelos vigente (claim→PR→stage→promote humano, skills plan-issue/work-issue/project-status). O audit avalia o repo **contra** esse fluxo, não contra o fluxo legacy de roadmap.md. +5. `docs/AGENT-OPS.md` — paradigma de agentes paralelos vigente (claim→PR→main, skills plan-issue / work-issue / agent-work-issue / project-status). O audit avalia o repo **contra** esse fluxo, não contra o fluxo legacy de roadmap.md. 6. `docs/IMPROVE-CODE-QUALITY-PLAN.md` — what the earlier passes already swept. Don't re-register what they fixed; verify fixes held. 7. `docs/TECH-DEBT.md` — open ledger. New findings de-dup against it; verify each open row still exists, close stale ones. 8. `docs/TESTING.md` — safety-net map: what is pinned, where the gaps are. diff --git a/.cursor/skills/local-database/SKILL.md b/.cursor/skills/local-database/SKILL.md index 89fe9fb1..fd58b5aa 100644 --- a/.cursor/skills/local-database/SKILL.md +++ b/.cursor/skills/local-database/SKILL.md @@ -15,7 +15,7 @@ Production is a live **Neon Postgres** with real citizens' PII. Local developmen ## Cursor Cloud (sem Docker) -VMs Cloud **não rodam** `docker compose` / `pnpm db:start`. Postgres nativo: `.cursor/environment.json` (`cloud-setup.sh` + `ensure-postgres.sh`). Mesmos URLs `localhost:5432/teqo` e `teqo_test`. Agentes em `work-issue`: não tente Docker; `gate:fast`/`gate:push` não precisam de banco. +VMs Cloud **não rodam** `docker compose` / `pnpm db:start`. Postgres nativo: `.cursor/environment.json` (`cloud-setup.sh` + `ensure-postgres.sh`). Mesmos URLs `localhost:5432/teqo` e `teqo_test`. Agentes em `work-issue` / `agent-work-issue`: não tente Docker; `gate:fast`/`gate:push` não precisam de banco. ## Local connection values diff --git a/.cursor/skills/model-selection/SKILL.md b/.cursor/skills/model-selection/SKILL.md index 6a67dbd0..2364f898 100644 --- a/.cursor/skills/model-selection/SKILL.md +++ b/.cursor/skills/model-selection/SKILL.md @@ -58,14 +58,11 @@ Depois de escolher Grok, **obrigatório** pickar um effort. Escreva o slug compl | `kimi-k3-low` | `kimi-k3` | `reasoning=low` | | `composer-2.5` | `composer-2.5` | `fast=false` | -## Issues muito complexas (bipartir) +## Issues muito complexas -Quando o blast radius é alto (refactor amplo, simplify repo-wide, migration+RBAC), `plan-issue` **registra duas Issues encadeadas**: +O plano de **implementação** nasce em `work-issue` / `agent-work-issue` (`*-impl.md`), não em `plan-issue`. Preferir **uma** Issue de intenção + Grok no `model:` quando a deliberação de engenharia for cara. -1. `{id}-plan` — `model: cursor-grok-4.5-high` — entregável = plano em `docs/plans/…` + critérios de aceite da execução -2. `{id}` ou `{id}-exec` — `model: kimi-k3-low`, `depends: [{id}-plan]` — executa o plano - -Não despachar Kimi K3 Low sem plano fechado na Issue de plan. +Bipartir (`{id}-plan` Grok High → `{id}-exec` `kimi-k3-low`) só sob pedido explícito ou blast radius extremo (refactor repo-wide / simplify amplo). Não despachar Kimi K3 Low sem plano de implementação fechado. ## Regras @@ -86,6 +83,7 @@ Não despachar Kimi K3 Low sem plano fechado na Issue de plan. ## Gates por fluxo - **`/simplify`:** herda o modelo da sessão. Preferir sessão em Kimi K3 Low **só** quando houver Issue de exec bipartida; caso contrário Composer ou Grok (effort pela tabela) conforme complexidade. -- **`work-issue`:** verifica `model:` da Issue (assimétrico: sessão mais fraca → informa e segue; mais forte → informa e pausa). Ausente → aplica esta tabela **incluindo** o effort Grok. -- **`plan-issue`:** sugere via esta tabela (slug completo com effort); em issues muito complexas, registra o par plan/exec. +- **`work-issue`:** verifica `model:` (assimétrico: mais fraca → informa e segue; mais forte → informa e **pausa**). Ausente → aplica esta tabela. +- **`agent-work-issue`:** pool já fixou o modelo no spawn; sessão mais forte → informa e segue. +- **`plan-issue`:** sugere via esta tabela (slug completo com effort); bipartição só sob blast radius extremo / pedido explícito. - **`agent-pool` smoke:** Composer default; Grok com effort explícito / Kimi K3 Low em Issues de teste bipartidas. diff --git a/.cursor/skills/plan-issue/SKILL.md b/.cursor/skills/plan-issue/SKILL.md index eab5eebb..e4822e6d 100644 --- a/.cursor/skills/plan-issue/SKILL.md +++ b/.cursor/skills/plan-issue/SKILL.md @@ -1,127 +1,125 @@ --- name: plan-issue -description: Registra uma ou VÁRIAS ideias/features/débitos do Teqo como GitHub Issues rastreáveis — classifica, explora o código, produz plano completo em docs/plans/.md por item (premissas, critérios de aceite, fases verticais, tracer bullet), sugere modelo × effort (model-selection) e passa por um GATE de confirmação (overview + wireframes ASCII + premissas) antes de criar qualquer Issue via `pnpm agent:register`. Usar quando o usuário pedir para adicionar/registrar algo ("adiciona ao roadmap", "registra essa ideia", "cria um plano para", "planeja a issue", "registra tudo isso"), em lote ou avulso. +description: >- + Transforma ideias humanas em Issues GitHub + planos de intenção em + docs/plans/ (persona, fluxo, objetivo, direção suave no código — sem + decisões duras de engenharia). Divide o pedido nas menores tarefas que + ainda fazem sentido. Use quando o usuário pedir /plan-issue, planejar + features, fatiar um pedido em Issues, ou registrar trabalho novo na fila. +disable-model-invocation: true --- -# Planejar e registrar Issues (1..N ideias) +# Planejar Issues (intenção, não engenharia) -Esta skill transforma ideias soltas em: (1) um **plano completo** em `docs/plans/.md` por item, e (2) uma **GitHub Issue rastreável** por item (`pnpm agent:register`, frontmatter `id/depends/serializes/priority/model`). **GitHub Issues são a fonte canônica** de spec/status/deps/prio/modelo — `docs/roadmap.md` é legado congelado e **nunca** é editado aqui. +Esta skill transforma ideias soltas em: (1) um **plano de intenção** em `docs/plans/.md` por item, e (2) uma **GitHub Issue rastreável** (`pnpm agent:register`, frontmatter `id/depends/serializes/priority/model`). GitHub Issues são a fonte canônica de spec/status/deps/prio/modelo — `docs/roadmap.md` é legado congelado e **nunca** é editado aqui. **Gate de confirmação (obrigatório):** nenhuma Issue é criada no GitHub antes do usuário aprovar o overview do lote (Passo 5). Planos locais podem ser rascunhados antes; Issues, não. -**Divisão com `work-issue`:** aqui se classifica a superfície UI (A–D), semeia âncoras de design, escreve o plano (spec + fatias). **Não** se implementa código nem se roda Impeccable craft/critique/polish — isso pertence a `work-issue` (incremental + TDD + gates). +## Divisão com as skills de execução -**Mapa das skills de planejamento:** o que absorver vs. rotear — [skills-map.md](skills-map.md). Princípios em silêncio; sem tour. +| Skill | Papel | +| ----- | ----- | +| **`plan-issue` (esta)** | Intenção humana: o quê / para quem / por quê / outcome. Direção suave no codebase. **Proibido** travar schema, signatures, migrations, abstrações ou “Abordagem” de engenharia. | +| **`work-issue`** | Humano supervisiona: claim → plano de **implementação** (Plan mode) → **pausa** para confirmação → executa. | +| **`agent-work-issue`** | Pool / autonomia: Issue já claimada → plano de implementação → executa sem pausa → `/simplify` → `capture-review-debts` → PR Ready + auto-merge. | -**Qualidade de decisão (não é tour):** aplique [decision-quality.md](decision-quality.md) em silêncio — filtro caro vs barato, Opções+Recomendação+rejeitadas, appetite, rabbit holes, depth check, fatias verticais, doubt em decisão cara, self-score ≥4 antes de gravar. +Aqui **não** se implementa código, **não** se escreve plano de implementação, **não** se roda Impeccable craft/critique/polish. -**Dados → decisão → apresentação:** se o item produz, agrega ou exibe números/séries/mapas/KPIs, aplique [data-presentation.md](data-presentation.md) em silêncio antes de travar a Abordagem. Se não há superfície de dados, `Dados: N/A` no plano. +**Shaping (não tour):** aplique [shaping.md](shaping.md) em silêncio — appetite, fatia mínima útil, rabbit holes de produto, self-score ≥4 antes de gravar. -## Checklist do fluxo +**Dados (intenção):** se o item envolve números/KPIs/mapas, aplique [data-presentation.md](data-presentation.md) só até “quem decide o quê com este dado”. A **forma** (chart/mapa/KPI) fica para o plano de implementação. + +## Checklist ``` - [ ] 1. Parse do lote + dedup (intra-lote, Issues existentes, docs/plans, roadmap legacy) -- [ ] 1b. Clareza: ideia vaga → idea-refine leve / perguntar; senão premissas explícitas - [ ] 2. Reserva de IDs de uma vez por trilha (roadmap legacy + issuesById()) -- [ ] 3. Por item (ordem topológica): classificar → explorar código → Impeccable A–D → dados - → decisões (doubt se caro) → fases verticais + tracer → posicionamento → plano completo +- [ ] 3. Por item (ordem topológica): classificar → fatiar → explorar só o suficiente → intenção completa - [ ] 4. Sugestão de modelo × effort via model-selection (uma linha por item) -- [ ] 5. GATE: overview do lote + premissas + wireframes ASCII (B/C/D) → confirmar/iterar -- [ ] 6. Registro: pnpm agent:register (com --model) por item + commit dos planos +- [ ] 5. GATE: overview do lote + esboços de fluxo (B/C/D) → confirmar/iterar +- [ ] 6. Registro: pnpm agent:register (com --model) por item + commit dos planos de intenção ``` -## Passo 1 — Parse do lote e dedup +## Passo 1 — Parse e dedup -1. **Separe os itens.** A entrada pode vir como 1 ideia ou N (lista, parágrafo, notas). Se a separação for ambígua, assuma a leitura mais provável e liste como você entendeu — a confirmação vai junto do gate. -2. **Deduplique dentro do lote.** Desfechos: **mesclar** (um item, plano único), **absorver** (vira fase de plano existente, sem ID novo), ou **manter separados** com dependência explícita. -3. **Deduplique contra o que já existe:** `gh issue list --state all` (frontmatter `id` via `issuesById()` em `scripts/lib/agent-github.mjs`), grep em `docs/plans/*.md` e em `docs/roadmap.md` (legado). Três desfechos por item: **já coberto** → aponte a Issue/plano existente e não crie nada; **é fase de plano existente** → adicione ao plano existente; **realmente novo** → siga. - -## Passo 1b — Clareza e premissas (antes de reservar ID / escrever plano) - -Inspirado em `idea-refine` + `spec-driven-development` + `using-agent-skills` (“Surface Assumptions”) — **leve**, sem sessão divergente completa. - -1. **Ideia ainda vaga?** (não dá para escrever Objetivos testáveis, persona, ou 1 fatia vertical). Desfechos: - - **Roteiar** para `idea-refine` / `interview-me` se o usuário quiser explorar; ou - - **Uma rodada** de 3–5 sharpening questions (quem, sucesso, constraints, o que já tentou, por quê agora) + recomendação; **não** avance ao Passo 2 até haver um HMW / problema em uma frase e um MVP óbvio. -2. **Premissas explícitas.** Antes de redigir o plano, liste o que está assumindo (produto, schema, papel, escopo). Formato no plano e no gate: - - ```text - PREMISSAS: - 1. … - 2. … - → Corrija agora ou sigo com estas. - ``` - - Não preencha ambiguidade em silêncio. Assunções que forem default de produto ficam marcadas _(assumido — validar com produto)_ nas Questões em aberto. -3. **Pedido vago → critérios de sucesso.** Reframe (“deixar o dashboard mais rápido”) em condições testáveis antes da Abordagem. Se não der para medir/verificar → ainda está vago (voltar ao item 1). +1. **Separe os itens.** Entrada pode ser 1 ideia ou N. Se ambíguo, assuma a leitura mais provável e liste — a confirmação vai no gate. +2. **Fatia mínima útil.** Prefira várias Issues pequenas a um epic. Cada item deve caber num appetite curto e entregar um outcome verificável sozinho. Mesclar só quando separar criaria trabalho inútil (mesmo fluxo, mesma persona, mesma superfície sem valor incremental). +3. **Dedup intra-lote:** mesclar | absorver (fase de plano existente, sem ID novo) | manter separados com `depends`. +4. **Dedup contra o existente:** `gh issue list --state all` + `issuesById()` + grep em `docs/plans/*.md` e `docs/roadmap.md` (legado). Já coberto → apontar e não criar; fase de plano existente → editar aquele plano; novo → seguir. ## Passo 2 — Reserva de IDs -Levante o último ID usado por trilha (A/B/C/D/E) olhando o roadmap legacy + `issuesById()` e distribua sequencialmente para os itens do lote **antes de escrever qualquer plano** (ex.: trilha B em `B78` → os dois itens de B viram `B79`, `B80`). IDs fora de trilha (chore/ops) seguem o prefixo do seu domínio conforme precedentes (`O0+`, `FD+`, `RS+`) ou a trilha temática mais próxima. +Último ID por trilha (A/B/C/D/E) via roadmap legacy + `issuesById()`; distribua **antes** de escrever planos. Fora de trilha: prefixos `O0+`, `FD+`, `RS+` ou trilha temática mais próxima. -## Passo 3 — Por item: do tipo ao plano completo +## Passo 3 — Por item: intenção completa -Ordem topológica intra-lote (o plano do dependente cita o ID do dependido, que já existe). Por item: +Ordem topológica (dependente cita ID do dependido). Por item: -1. **Classificar o tipo:** +1. **Tipo** | Tipo | Destino | | ---- | ------- | -| Feature de `/campanha` ou site público com escopo próprio | Issue `kind:feature` com plano | -| Tarefa pequena / chore / débito de engenharia | Issue `kind:chore` + plano curto (ou body direto se trivial) | -| Défice comportamental do fluxo de agentes | `pnpm agent:file-miss` (`kind:agent-miss`), não aqui | -| Bloqueio externo (jurídico/LGPD) | Issue `blocked` + label `needs:consent`/`needs:migration`; texto novo entra no lote jurídico existente, nunca rodada separada | -| Evidência de usuário ainda inexistente (discovery) | `blocked` / defer+gatilho — não inventar feature; ver `continuous-discovery` só se o usuário pedir discovery | +| Feature `/campanha` ou site com escopo próprio | `kind:feature` + plano de intenção | +| Chore / débito pequeno | `kind:chore` + plano curto (ou body se trivial) | +| Défice do fluxo de agentes | `pnpm agent:file-miss` (`kind:agent-miss`) — não aqui | +| Bloqueio externo (jurídico/LGPD) | `blocked` + `needs:consent`/`needs:migration`; texto no lote jurídico existente | | Decisão de NÃO fazer | Comentário/doc, não Issue | -2. **Explorar o código** (depth check): onde a feature se pluga, o que reusar de `src/utilities/*`, `src/components/campaign/*`, `src/lib/*`; schema novo → migration (caro de reverter → Decisão travada com rejeitadas); pessoa → join com `Contact`; opt-in/PII → `Consent` por chave estável falhando fechado. Precedente análogo em `docs/plans/` dita o nível de detalhe. Cite **caminhos reais** (context-engineering: plano legível sem dump do repo). -3. **Classificar superfície UI (A–D)** e semear âncoras: `PRODUCT.md`/`DESIGN.md`, design-ref em `docs/design-refs/latest/` se houver, shells existentes. Classe C exige **brief compacto** (persona, job, estratégia de cor, anti-goals) no plano. Proibido: rodar craft/critique/polish aqui. -4. **Dados → decisão → apresentação** ([data-presentation.md](data-presentation.md)) ou `Dados: N/A`. -5. **Decisões caras + doubt (quando couber):** para cada decisão caro-de-reverter (schema, access, Consent, URL imutável, multi-collection), use a forma Opções+Recomendação+rejeitadas. Se a decisão for **não trivial** no sentido de [decision-quality.md](decision-quality.md) § Doubt, faça um ciclo curto CLAIM → adversarial self-check (ou `Task` fresh-context) **antes** de gravar como Decisão travada. Não doubt em rename/copy. -6. **Posicionamento:** prioridade `P0..P3` (jurídico/caminho crítico > base de dados > operação de campo > inteligência > plataforma), `depends` (IDs duros), appetite, janela do calendário eleitoral quando relevante, `serializes` quando toca recurso compartilhado (ex.: migrations). -7. **Fatiar em fases verticais** ([decision-quality.md](decision-quality.md) § Fatias): não “toda a DB → toda a API → toda a UI”. Cada fase = caminho fino ponta a ponta (ou o pedaço mínimo que deixa o sistema verde), com **aceite**, **verify** (`pnpm gate:fast` / pin unit|int / check manual), arquivos prováveis, tamanho S/M. **Fase 1 = tracer bullet** (schema mínimo → uma action → uma superfície, ou o menor path que prova a aposta). High-risk / incerteza primeiro. Se a fatia seria L/XL (≫5 arquivos ou >1 sessão focada) → mais fases **ou** Issue bipartida `{id}-plan` / `{id}-exec`. -8. **Boundaries Teqo** no plano (sempre, compacto): o que esta entrega **Always** / **Ask first** / **Never** — ver template. Defaults do repo (overrideAccess, transação, Consent fail-closed, sem Neon) entram em Always/Never sem inventário genérico. -9. **Source-driven (flag, não fetch):** se a Abordagem depende de API de framework (Payload Local API, Next App Router, WebAuthn), anote na fase: “na implementação, verificar docs oficiais da versão em `package.json`” — o fetch é de `work-issue` / `source-driven-development`. -10. **Plano completo** em `docs/plans/.md` seguindo [plan-template.md](plan-template.md) à risca — cabeçalho, Premissas, Objetivos como aceite, Decisões, Fases verificáveis (quota do appetite), Dados, wireframe ASCII se B/C/D com layout, guardrails, rabbit holes, adiados. Self-score ≥4/5 antes de gravar. +2. **Explorar o código o mínimo** — só para apontar **direção** (rotas/pastas/domínios prováveis) e evitar duplicar algo já entregue. Não inventar signatures, collections novas como decisão travada, nem diagramas de componentes. +3. **Superfície UI (A–D)** como dica para quem for executar — não semear brief Impeccable completo nem wireframe de implementação. Classe C/D: esboço de fluxo persona (ASCII curto) no gate, não especificação de layout. +4. **Dados (intenção)** ou `Dados: N/A`. +5. **Posicionamento:** `P0..P3`, `depends`, appetite, janela eleitoral se relevante, `serializes` se tocar recurso compartilhado (ex. migrations) — sem detalhar a migration. +6. **Plano de intenção** em `docs/plans/.md` via [intention-template.md](intention-template.md). Self-score ≥4/5 ([shaping.md](shaping.md)). -## Passo 4 — Sugestão de modelo × effort +### O que é proibido no plano de intenção -Aplique a skill `model-selection` (tabela canônica) por item e registre a sugestão no cabeçalho do plano (`Model:`) e no resumo do gate. Caminho feliz único: +- Decisões de schema / collection / unicidade / Consent key concreta como “travadas” +- Assinaturas de funções, nomes de arquivos novos obrigatórios, mermaid de arquitetura de solução +- “Abordagem proposta” com componentes e migration nomeada +- Fases de implementação verificáveis com quota de engenharia +- Forçar o executor a uma única forma técnica -- `composer-2.5` — default (features, chores simples, fixes localizados, docs leves) -- `cursor-grok-4.5-low` | `cursor-grok-4.5-medium` | `cursor-grok-4.5-high` — Grok com **effort explícito** (ver model-selection: Low = mecânico com julgamento; Medium = discovery/análise; High = multi-domínio / falha cara) -- `kimi-k3-low` — **só** fase de execução de issues bipartidas (refactor / simplify / migration+RBAC de blast radius alto) +### O que é obrigatório -**Issues muito complexas:** registre **duas** Issues encadeadas — `{id}-plan` (`model: cursor-grok-4.5-high`, entregável = plano + critérios de aceite) e `{id}`/`{id}-exec` (`model: kimi-k3-low`, `depends: [{id}-plan]`). O valor vai ao frontmatter `model:` — propriedade da Issue, verificada (não recalculada) por `work-issue`. +- Intenção do humano (problema/oportunidade) +- Persona(s) e fluxo desejado (job / outcome) +- Critérios de aceite em linguagem de produto +- Appetite e fora de escopo (produto) +- **Direção provável no codebase** (pastas/rotas/domínios — hipotética, revisável) +- Questões em aberto com **Opções + Recomendação de produto** (não de engenharia) -## Passo 5 — GATE de confirmação (obrigatório) +## Passo 4 — Modelo × effort -Apresente no chat, **antes de criar qualquer Issue**: +Skill `model-selection`. Registre no cabeçalho (`Model:`) e no gate. -- **Overview do lote** — uma linha por plano: ID, título, prio, depends, appetite, modelo sugerido, link do plano local. -- **Premissas** por item (ou bloco compartilhado do lote) — “corrija agora ou sigo”. -- **Wireframes ASCII** das superfícies B/C/D (copiados dos planos). -- **Fases em uma linha** (F1 tracer → …) quando o item tiver >1 fase. -- Perguntas acumuladas (se houver) em uma única rodada, recomendação primeiro. +- Default: `composer-2.5` +- Grok com effort explícito quando discovery/multi-domínio / falha cara de **produto** +- Não crie par `{id}-plan` / `{id}-exec` por default — o plano de implementação nasce em `work-issue` / `agent-work-issue`. Reserve bipartição só para blast radius extremo (refactor repo-wide) se o humano pedir. -O usuário confirma ou pede modificações; itere até confirmar. **Só depois** avance ao Passo 6. Planos em `docs/plans/` podem ser rascunhados antes do gate (arquivos locais isolados); ajuste-os conforme a resposta. +## Passo 5 — GATE + +Antes de criar Issues: + +- Overview: ID, título, prio, depends, appetite, modelo, link do plano local +- Esboços de fluxo persona para superfícies B/C/D (se houver) +- Perguntas acumuladas numa rodada, recomendação de produto primeiro + +Itere até confirmação. Só então Passo 6. ## Passo 6 — Registro -Por item confirmado: - ```bash pnpm agent:register -- --id --title "" --prio \ --depends --kind --plan docs/plans/.md \ --model [--blocked] [--labels extra] ``` -A Issue nasce `ready` (ou `blocked`) com frontmatter `id/depends/serializes/priority/model` + spec + link do plano. Atualize o cabeçalho do plano (`Issue: #N`). Commit dos planos (commits lógicos por item ou por lote). +Atualize `Issue: #N` no plano. Commit dos planos de intenção. -**PR só de plano (`docs/plans/` exclusivamente):** nunca use `Closes #N` / `Fixes` / `Resolves` no body — use `Related #N`. O CI (`plans-only-closes`) bloqueia keywords de fechamento; a implementação fecha a Issue em outro PR (`work-issue`). +**PR só de plano:** nunca `Closes #N` — use `Related #N` (`plans-only-closes`). -**NÃO faz:** editar `docs/roadmap.md` (legado congelado), implementar código, claim (`pnpm agent:claim` é de `work-issue`), abrir jornada `design-code-architecture`, gravar `tasks/plan.md` / `tasks/todo.md` (artefato Teqo = `docs/plans/.md`). +**NÃO faz:** editar `docs/roadmap.md`, implementar, claim, escrever `*-impl.md`. ## Resumo final -Tabela do lote (ID, título, prio, depends, appetite, modelo, Issue #N, plano) + em prosa: mesclados/absorvidos/descartados por duplicidade, premissas que o gate confirmou ou corrigiu, decisões assumidas _(validar com produto)_, e o que o gate decidiu. +Tabela do lote + mesclados/absorvidos/descartados + decisões de produto assumidas _(validar)_ + o que o gate decidiu. diff --git a/.cursor/skills/plan-issue/data-presentation.md b/.cursor/skills/plan-issue/data-presentation.md index e75c66a6..70ff9b76 100644 --- a/.cursor/skills/plan-issue/data-presentation.md +++ b/.cursor/skills/plan-issue/data-presentation.md @@ -1,112 +1,39 @@ -# Dados → decisão → apresentação +# Dados → decisão (intenção) → forma (implementação) -Filtro obrigatório quando um item do roadmap **produz, agrega ou exibe** números, séries, rankings, mapas ou KPIs. Inspirado em `visualization-choice-reporting` (pergunta → tipo de chart → narrativa acionável) e no kernel Teqo (`PRODUCT.md` §5 + `docs/research/`). **Não** é um tour de dashboard design — são três perguntas com resposta registrada no plano. Decisão silenciosa (“só coloca um gráfico”) é defeito. +Quando um item **produz, agrega ou exibe** números, séries, rankings, mapas ou KPIs. Kernel Teqo: `PRODUCT.md` §5 + `docs/research/` — disputa de DF é relativa/local, não % estadual absoluto. -Aplique em silêncio em `plan-issue` (ao semear o plano) e em `work-issue` (ao auditar e fatiar UI). Se o item **não** apresenta dados (ex.: migration de Consent, invite WhatsApp, form CRUD sem métrica), escreva `Dados: N/A` no plano e pule o resto. +| Fase | Skill | O que decide | +| ---- | ----- | ------------ | +| Intenção | `plan-issue` | Vou apresentar dados? Quais **decisões de ator** isso desbloqueia? Restrições de produto. | +| Implementação | `work-issue` / `agent-work-issue` | Forma concreta (número+contexto / lista / série / mapa / chart) + rejeitadas técnicas. | -## As três perguntas (ordem fixa) +Se o item **não** apresenta dados: `Dados: N/A` no plano de intenção e pule o resto. -Responda nesta ordem. Pular para “qual chart?” antes de fechar (1)–(2) é o erro clássico. +## Na intenção (`plan-issue`) + +Ordem fixa: ```text 1. Vou apresentar dados? 2. Quais decisões serão tomadas a partir destes dados? -3. Como apresentar estes dados para auxiliar essa decisão de forma efetiva? ``` ### 1. Vou apresentar dados? -| Resposta | Critério | Ação no plano | -| ------------------------------ | ---------------------------------------------------------------------- | ------------------------------------------------- | -| **Não** | Só schema/action/access; nenhum número lido por humano nesta entrega | `Dados: N/A` — fim | -| **Sim, derivado / API só** | Utility/aggregate que **outra** superfície já consome (ou item futuro) | Registrar payload + quem consome; UI fica no dono | -| **Sim, superfície neste item** | Lista, detalhe, dashboard, mapa, CSV export legível, card de insight | Preencher seção completa abaixo | +| Resposta | Ação | +| -------- | ---- | +| **Não** | `Dados: N/A` | +| **Sim, derivado / API só** | Registrar quem consome; UI no dono | +| **Sim, superfície neste item** | Preencher decisões desbloqueadas + restrições | -Se “sim”: profile rápido do dado — tipo (categórico / numérico / temporal / geo), granularidade (praça / município / TI / estado), tamanho típico (1 KPI vs 436 linhas vs série 3 anos), e se é **absoluto ou relativo** (no `/campanha` electoral, preferir relativo/local — ver anti-goals). +Profile rápido (opcional): tipo / granularidade / absoluto vs relativo. -### 2. Quais decisões serão tomadas a partir destes dados? +### 2. Decisões desbloqueadas -Uma frase por decisão, no formato **ator + escolha**. Sem decisão nomeável → o dado é vaidade; corte ou rebaixe a “contexto secundário”. +Formato **ator + escolha**. Sem decisão nomeável → vaidade; corte. -Exemplos Teqo bons: +**Não** escolha chart/mapa/KPI aqui. Registre só anti-goals de produto (ex.: sem gauge SaaS, sem % estadual absoluto). -- Coordenador: “nesta semana, atacar déficit descoberto em quais Praças?” -- Assessor: “minha meta fecha? se não, renegociar pledge ou abrir demanda?” -- Staff: “comparar candidato X vs Solla neste município — manter ou pivotar discurso?” +## Na implementação (`*-impl.md`) -Exemplos ruins (rejeitar): - -- “Ter visibilidade do % estadual” (anti-goal; não decide alocação) -- “Ver quantos apoiadores cadastrados” sem vínculo a cobertura/meta -- “Dashboard bonito para impressionar” - -Se o item toca inteligência de campanha / mapa / metas / fila: consulte **antes** o playbook em `docs/research/relatorio-entrevista-persona-campanha.md` (§6 padrões dado→decisão) e o anti-catálogo do plano-mestre `docs/plans/inteligencia-campanha.md`. Não invente métrica que o research vetou. - -### 3. Como apresentar para auxiliar essa decisão? - -Escolha a **forma mais pobre que ainda responde a decisão** (depth: reusar shell existente > chart novo > lib de charts). Registre no plano: forma + racional + o que foi rejeitado. - -**Escada de pobreza (preferir o degrau mais baixo que resolve):** - -| Degrau | Quando usar | Reuso Teqo típico | -| ------------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------- | -| **Número + contexto** | 1–2 valores, “fecha / não fecha”, delta vs meta/semana | `CampaignMetricStrip`, linha no overview | -| **Tabela / lista ranqueada** | Comparar muitas entidades (Praças), ordenar por déficit/cobertura | `PlazaList` + colunas; fila de alocação | -| **Série temporal simples** | Evolução curta (3–5 pontos: 2014/2018/2022 ou delta semanal) | sparkline / tabela ano×valor; não multi-eixo | -| **Mapa (cor / símbolo)** | Decisão é **onde** agir; geo é central à pergunta | `BahiaMap` + escala relativa (quantil/LQ), não % est. | -| **Chart (barra / linha)** | Comparação de poucas categorias ou tendência com >~5 pontos | shadcn/`src/components/ui`; tokens `campaign` | -| **Chart composto / dashboard** | Só se appetite e research pedirem; senão rabbit hole | Adiar com gatilho | - -**Família de pergunta → forma** (resumo de visualization-choice-reporting, calibrado ao Field Desk): - -| Pergunta que o dado responde | Preferir | Evitar neste produto | -| ---------------------------------- | -------------------------------- | ----------------------------------- | -| Quanto falta / cobre a meta? | Número + % cobertura + delta | Gauge/velocímetro SaaS, hero-metric | -| Onde está o buraco / oportunidade? | Mapa relativo ou lista ranqueada | Choropleth de % estadual absoluto | -| Como evoluiu no tempo? | Linha/sparkline ou tabela anos | Pie; dual Y-axis | -| Como A se compara a B (poucos)? | Barra horizontal ou tabela | Pie com muitas fatias; 3D | -| Qual a composição do todo? | Stacked bar ou tabela | Donut decorativo | -| Correlação / nuvem de pontos? | Quase nunca em v1 `/campanha` | Scatter exploratório sem decisão | - -**Narrativa mínima na UI** (mesmo sem relatório formal): o bloco de dados deve permitir ler, nesta ordem — **insight → padrão → contexto (vs meta/histórico) → implicação → próxima ação possível**. Título descritivo (“Votos por mês”) sem insight é falha. Em `/campanha`, a “ação” é menu para julgamento humano (nunca auto-decisão) — alinhado ao research. - -## Anti-goals Teqo (não negociar sem fonte nova) - -- KPI de **% estadual absoluto** como escala principal (achatamento; DF ≠ majoritária). -- Contagem bruta de cadastros / “vanity metrics” sem vínculo a cobertura/meta/fila. -- Cartograma, gauge, rainbow choropleth, dual-axis enganoso. -- Mapa bonito com pergunta errada (visualização serve à análise; análise serve à decisão — research § ordem Brewer/Pickle). -- Segunda lib de charts / design system paralelo; inventar paleta fora de `data-theme='campaign'`. -- Sugestão automática sem registro de decisão humana (C12 / motor de sugestões). - -## Quando NÃO usar gráfico - -- Um único número decisivo → número tipográfico + unidade + vs quê. -- Dois números → lado a lado com delta %. -- Poucas linhas densas de auditoria → tabela. -- Export/CSV para operação → tabela; chart é opcional depois. - -## Caro vs barato nesta superfície - -| Caro (Decisão travada + rejeitadas) | Barato (Não escopo / Adiado) | -| ------------------------------------------------ | ----------------------------------------- | -| Métrica canônica (fórmula, denominador, quem vê) | Cor da barra, ordem de colunas | -| Escala do mapa (absoluta vs % vs quantil/LQ) | Tooltip copy | -| Staff-only vs leader-visible | Sparkline vs número só | -| Persistência de derivado vs compute on read | Biblioteca de chart (se já há shadcn/SVG) | - -## Self-check (30s, só se Dados ≠ N/A) - -Antes de gravar o plano ou declarar a fase de UI pronta — 1 ponto cada; **<3 → corrigir**: - -1. As três perguntas estão respondidas por escrito? -2. Há pelo menos uma **decisão nomeável** (ator + escolha)? -3. A forma escolhida é o degrau mais pobre que resolve — com alternativas rejeitadas? -4. Anti-goals do research/`PRODUCT.md` §5 não foram violados? -5. Depth: reusa `CampaignMetricStrip` / lista / `BahiaMap` / tabela existente quando bastam? - -## Onde registrar - -- **`plan-issue`:** seção **Dados → decisão → apresentação** no `docs/plans/.md` (template). Se N/A, uma linha basta. -- **`work-issue`:** auditar essa seção no Passo 4; se ausente e o item claramente apresenta dados → defasado (completar no Passo 7). Nas fases de UI, a escolha de forma é input do craft/critique — não reinventar no meio do polish. -- Skills externas (opcional, leitura sob demanda): `visualization-choice-reporting` (famílias de chart + narrativa); `.agents/skills/refactoring-ui/references/data-visualization.md` (quando NÃO chartar); Mapbox patterns só se o item for mapa Mapbox (hoje o mapa Teqo é Leaflet/`BahiaMap`). +Responda a pergunta 3 com Opções + Recomendação + rejeitadas, alinhado a [decision-quality.md](../work-issue/decision-quality.md) e ao research Teqo. Preferir a forma mais pobre que ainda desbloqueia a decisão. diff --git a/.cursor/skills/plan-issue/decision-quality.md b/.cursor/skills/plan-issue/decision-quality.md deleted file mode 100644 index a23771e1..00000000 --- a/.cursor/skills/plan-issue/decision-quality.md +++ /dev/null @@ -1,147 +0,0 @@ -# Qualidade de decisão (roadmap / implementação / débitos) - -Inspirado em `design-code-architecture` e nas skills que ela compõe (37signals, software-design-philosophy, clean-architecture, DDD), mais destilados de `spec-driven-development`, `planning-and-task-breakdown`, `incremental-implementation` e `doubt-driven-development` — **sem** jornada guiada por fases. Use estes filtros automaticamente ao redigir planos, implementar ou triar débitos. Decisão silenciosa é defeito. - -Mapa completo do que absorver vs. rotear: [skills-map.md](skills-map.md). - -## Filtro caro vs barato - -Delibere e registre só o **caro de reverter**. O barato vira Não escopo, fill-in, ou “mais simples que funciona” com **gatilho de revisitação** (não “depois”). - -| Caro (decidir agora + alternativas rejeitadas) | Barato (adiar / mais simples) | -| ------------------------------------------------------------------------ | ----------------------------------- | -| Collection nova, unicidade, modelo de access | Polish cosmético, 2º estilo de card | -| Nova chave `Consent` / superfície PII | Copy, motion, a11y P3 | -| Semântica de escrita multi-collection / locks | Prefetch, apresentação de KPI | -| URL pública / slug imutável | Ordem de colunas na lista | -| Separar item de trilha vs absorver em plano | Rename de pureza, naming cosmético | -| Fronteira de módulo em ponto de volatilidade real (DB, vendor, delivery) | Adapter/interface com 1 call site | - -Converter caro em barato: colocar um boundary na frente (fail-closed por chave, join em `Contact`, chips opt-in em vez de igualdade forçada) — o conjunto irredutível de decisões caras deve caber nas Decisões travadas. - -## Forma obrigatória de decisão - -Toda decisão não trivial (posicionamento, classe Impeccable B↔C, absorver vs registrar, default de produto, schema vs não): - -```text -Opções: A | B | C -Recomendação: B — porque … -Alternativas rejeitadas: A porque …; C porque … -``` - -No plano: Decisões travadas = **decisão + por quê + fonte/data + alternativas rejeitadas**. Em Questões em aberto: **Opções + Recomendação** (nunca pergunta sem posição). Assuma o default e marque _(assumido — validar com produto)_ — não abra tour de confirmações. - -## Premissas (antes da Abordagem) - -Não preencha ambiguidade em silêncio. Liste premissas no plano e no gate: - -```text -PREMISSAS: -1. … -→ Corrija agora ou sigo com estas. -``` - -Pedido vago → **reframe em critérios de aceite testáveis** antes de desenhar componentes. Se não dá para marcar um checkbox de Objetivo ao fim da Issue, a ideia ainda não está shaped (Passo 1b / `idea-refine`). - -## Appetite (tempo fixo, escopo flexível) - -Além da **janela** eleitoral, declare um **appetite** (quanto o slice vale), não uma estimativa aberta: - -- Exemplos: `~0,5 dia eng, só utility` · `~1–2 dias, migration + 1 action + encaixe em lista` · `~1 ciclo curto, UI nova 1 rota`. -- Se a Abordagem proposta estourar o appetite → cortar rabbit holes / Não escopo, não inflar o item. -- Em `work-issue`, cada fase declara quanto do appetite consome; preferir um **tracer bullet** cedo (schema → uma action → uma superfície UI) antes de polish paralelo. - -## Fatias verticais (fases do plano) - -De `planning-and-task-breakdown` + `incremental-implementation` — no artefato Teqo (`docs/plans/`), não em `tasks/todo.md`. - -**Ruim (horizontal):** schema completo → todas as actions → toda a UI → “ligar”. -**Bom (vertical):** Fase 1 prova um path fino ponta a ponta; Fase 2 alarga. - -Regras: - -1. **Ordem = grafo de dependência**, não “o que parece importante”. Fundações (migration, access) só o mínimo para o tracer. -2. **Cada fase** tem aceite (≤3 bullets), verify (comando/pin), files, tamanho **S** (1–2 files) ou **M** (3–5). **L/XL** → partir a fase ou bipartir a Issue. -3. **Fase 1 = tracer bullet** (ou risk-first se a aposta arriscada for outra — ex. provar WebAuthn antes do chrome). -4. Após cada fase o sistema deve permanecer **compilável / testes verdes** (disciplina de `work-issue`). -5. Checkpoint humano só quando o plano tiver >2 fases ou uma decisão cara ainda aberta — não cerimônia entre S chores. - -## Doubt em decisão cara (ciclo curto) - -De `doubt-driven-development` — **só** quando a decisão é não trivial **e** cara de reverter (branching de access, migration irreversível, invariante que o type system não pega, URL pública). - -```text -CLAIM: <1–2 linhas> -WHY IT MATTERS: -→ Adversarial self-check (ou Task fresh-context): achar furos no ARTIFACT+CONTRACT, - não validar. Classificar: actionable | trade-off documentado | noise. -→ Parar em 1 ciclo no planning (3 ciclos é para implementação). -``` - -Não doubt: rename, copy, chore mecânico, “seguimos o shell de lista existente”. -Não invocar cross-model CLI daqui — se o usuário quiser segunda opinião, ofereça no gate. - -## Rabbit holes - -Riscos que explodem o escopo se tocados “de passagem”. Listar ao lado de Não escopo (não são a mesma coisa): - -| Não escopo | Rabbit hole | -| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Exclusão consciente com destino (outro plano/ID) | Armadilha se alguém “só completar” | -| Ex.: mapa Leaflet → B3 | Ex.: layers Clean Architecture só para um feature; 2ª collection de pessoa; igualdade forçada TSE; event sourcing; helper shared antes de 3 call sites | - -Rabbit holes Teqo recorrentes: layers/cerimônia sem volatilidade, cadastro paralelo a `Contact`, Consent hardcoded por ID, abstração DRY prematura, redesign de paleta fora de `campaign`. - -## Depth check (módulos profundos) - -Antes de propor arquivo/utility/componente novo na Abordagem: - -1. Já existe módulo profundo que esconde isso? (`campaignAccess`, shells de lista, `withPayloadTransaction`, `campaignConsent`, …) → **reusar**. -2. O novo símbolo esconde complexidade real ou é pass-through raso (classitis)? → **consolidar ou não criar**. -3. Uma decisão de design vazaria em N módulos? → **encapsular uma vez**. - -Não invocar a skill `software-design-philosophy` inteira — aplicar só este check no momento da decisão. - -## Defer com gatilho - -Adiar sem gatilho vira backlog morto. Formato: - -`Adiar X até ` — ex.: “shared `drizzleBulk` quando existir o 3º caminho bulk”; “réplicas/cache quando QPS médio > N” (raro neste produto). - -Em `capture-review-debts`: DRY com <3 call sites → preferir **descartar** ou **defer+gatilho** a **registrar**, salvo access/LGPD/hot path. - -## Boundaries Always / Ask / Never (por entrega) - -Compacto no plano — reforça o que **esta** Issue carrega; não reescrever AGENTS.md. - -| Tier | Exemplos Teqo típicos | -| ----------- | ------------------------------------------------------------------------------------- | -| **Always** | `overrideAccess: false` com `user`; pin de teste do parser/access; fail-closed Consent | -| **Ask first** | Collection/migration nova; dependency nova; mudar contrato de URL pública | -| **Never** | Neon em dev/test; Consent por document ID; pessoa fora de `Contact`; `as never` | - -## Princípios sob demanda (método, não jornada) - -Quando o tipo de decisão bater, aplicar o princípio — **não** abrir fases de `design-code-architecture`: - -| Tipo de decisão | Princípio | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -| Dependências de código / testabilidade do domínio | Dependency Rule: framework/ORM no anel externo; core testável sem DB/web | -| Vocabulário / aggregate / “Customer” em dois sentidos | DDD: não unificar; invariants no aggregate; refs por ID | -| Escopo de v1 do item | 37signals: build less, YAGNI, cortar abstração especulativa; **nunca** cortar o caro de reverter | -| Depth / leakage na abordagem | software-design-philosophy: deep modules, um lugar por conhecimento | -| Outbound / lista / import | Timeouts, paginação, fail-closed (já em AGENTS.md) — nomear como decisão se o item toca | -| KPI / mapa / série / ranking na UI | [data-presentation.md](data-presentation.md): dado → decisão nomeável → forma mais pobre | -| API de framework na Abordagem | Flag source-driven para `work-issue` (versão em package.json); não inventar da memória no plano | - -## Self-score (gate de 30s) - -Antes de gravar docs ou declarar o plano de implementação pronto, pontue 0–5 (1 ponto cada). **<4 → corrigir antes de escrever/seguir**: - -1. Decisões caras têm alternativas rejeitadas (e doubt curto se migration/access/Consent/URL)? -2. Appetite declarado; fases verticais cabem nele; Fase 1 = tracer (ou risk-first justificado)? -3. Premissas listadas + Objetivos como aceite testável + Boundaries Always/Ask/Never? -4. Rabbit holes nomeados (além de Não escopo) + depth check (reusa shells, sem classitis)? -5. Consistência aplicável: tripla grafo=tabela=Dependências **ou** triage score/destino/tipo alinhados? - -Opcional no resumo ao usuário: `Qualidade de decisão: N/5` + o item que falhou. diff --git a/.cursor/skills/plan-issue/intention-template.md b/.cursor/skills/plan-issue/intention-template.md new file mode 100644 index 00000000..3099db88 --- /dev/null +++ b/.cursor/skills/plan-issue/intention-template.md @@ -0,0 +1,89 @@ +# Template de plano de intenção (`docs/plans/.md`) + +Plano de **produto/intenção** — o que o humano quer, para quem, e como saber que deu certo. **Não** é plano de engenharia: quem executa (`work-issue` / `agent-work-issue`) cria o `docs/plans/-impl.md` depois, com liberdade de reavaliar a abordagem técnica. + +Shaping: [shaping.md](shaping.md). Dados (só decisão de produto): [data-presentation.md](data-presentation.md). + +````markdown +# + +Status: rascunho +Atualizado em: +Issue: # (após `pnpm agent:register`; senão "—") +Priority: +Model: +Impeccable: — +Appetite: +Responsável: — + +## Intenção + + + +## Persona e fluxo + +- **Persona / contexto:** +- **Job principal:** +- **Fluxo desejado:** +- **Anti-goals de produto:** + +### Esboço de fluxo (B/C/D) + + + +\```text +[início] → … → [outcome] +\``` + +## Objetivo e aceite + +- +- + +## Dados (intenção) + + + +- **Vou apresentar dados?** +- **Decisões desbloqueadas:** +- **Forma:** *adiada ao plano de implementação* — aqui só restrições de produto (ex.: "sem % estadual absoluto", "leitura relativa/local"). + +## Direção no codebase (hipótese) + + + +- **Áreas prováveis:** /`, `src/utilities//`, `src/lib/…`> +- **Precedente a olhar:** +- **Risco de acoplamento:** + +## Dependências + +- + +## Fora de escopo + +- + +## Rabbit holes de produto + +- **.** Se alguém “só completar”: . **Corte neste item:** <…>. + +## Questões em aberto (produto) + + + +- **?** **Opções:** A | B | C. **Recomendação:** <…>. _(assumido — validar com produto)_ quando couber. + +## Referências + +- GitHub Issue # +- +- `AGENTS.md` / `docs/ARCHITECTURE.md` — só se o item toca convenção já travada no repo +```` + +Notas: + +- Alvo: ~60–100 linhas. Mais que isso costuma significar engenharia vazando para a intenção. +- Classe **A:** `Impeccable: A — N/A`; omita esboço de fluxo. +- Não rode `/impeccable` aqui. +- Self-score shaping ≥4/5 antes de gravar. diff --git a/.cursor/skills/plan-issue/plan-template.md b/.cursor/skills/plan-issue/plan-template.md deleted file mode 100644 index 6f667391..00000000 --- a/.cursor/skills/plan-issue/plan-template.md +++ /dev/null @@ -1,209 +0,0 @@ -# Template de plano (`docs/plans/.md`) - -Estrutura extraída dos planos existentes (`overview-lista-nucleos.md`, `visitados-recentemente.md`, etc.). Manter as seções nesta ordem; omitir "Design (Impeccable)" apenas na classe **A** (só backend); **sempre** incluir "Dados → decisão → apresentação" (`N/A` se não houver superfície de dados); omitir a subseção "Referência visual (UX Pilot)" quando não houver design-ref; omitir "Wireframe (texto)" quando não houver layout a posicionar (ver skill `work-issue`). - -Filtros de decisão (caro vs barato, appetite, rabbit holes, depth, fatias, doubt): [decision-quality.md](decision-quality.md). -Filtro de dados (apresentar? decisão? forma?): [data-presentation.md](data-presentation.md) — obrigatório se houver KPI/mapa/série/ranking; senão `Dados: N/A`. -Mapa skills de planejamento: [skills-map.md](skills-map.md). - -````markdown -# - -Status: rascunho -Atualizado em: -Issue: # (preencher após `pnpm agent:register`; enquanto não registrado: "—") -Priority: -Model: -Impeccable: — -Appetite: -Responsável: — - -## Premissas - - - -1. -2. <…> -→ Corrija no gate ou o implementador segue com estas. - -## Design (Impeccable) - - - -Âncoras: `PRODUCT.md` / `DESIGN.md` (register ) · tema `data-theme='campaign'` (ou tokens do site público). - -Na implementação (`work-issue`): . Declarar `harden`/`optimize` só se o Passo 8 do implement acionar gatilho (não pipeline fixo). - -Brief compacto (obrigatório em C; se B ambíguo): - -- **Persona / contexto:** -- **Job principal:** -- **Estratégia de cor:** Restrained (default) | -- **Edit where you see** (só `/campanha` staff, UI B/C/D): — princípio 3 de `PRODUCT.md`; rabbit hole: spreadsheet mode -- **Anti-goals:** - -### Wireframe (texto) - - - -\```text -┌─ ───────────────────────────────┐ -│ │ -│ <…> │ -└──────────────────────────────────────────────────────┘ - Fora do frame: . -\``` - -### Referência visual (UX Pilot) - - - -Design: [`.png`](../design-refs/latest/.png) · [`.html`](../design-refs/latest/.html) - -![](../design-refs/latest/.png) - -Como usar: - -- **Adotar a estrutura:** . -- **Fora deste plano:** . -- **Ajustar cores e código:** o HTML/PNG usa a paleta antiga (vermelho escuro `#8E0E23`, navy `#1B2B4B`, dourado `#C8874B`) e Tailwind via CDN. Implementar com os componentes shadcn existentes (`src/components/ui`) e os tokens do tema `data-theme='campaign'` (`src/app/(frontend)/styles.css`): fundo branco, primário `#C51414`, superfícies neutras claras. - - - -## Dados → decisão → apresentação - - - -- **Vou apresentar dados?** -- **Decisões desbloqueadas:** -- **Forma escolhida:** — **por quê.** **Rejeitado:** . -- **Profile (se Sim):** tipo / granularidade / tamanho típico / absoluto vs relativo. -- **Anti-goals de dado:** . - -## Contexto - - - -## Objetivos (critérios de aceite) - - - -- [ ] -- [ ] <…> -- Guardrails: - -## Boundaries (desta entrega) - - - -- **Always:** -- **Ask first:** -- **Never:** - -## Decisões travadas - - - -- **.** **Rejeitado:** . -- **i18n e naming** seguem o AGENTS.md: identificadores em inglês (), strings visíveis em pt-BR. - -## Questões em aberto - - - -- **?** **Opções:** A | B | C. **Recomendação:** . <"Definir com - produto" quando aplicável — mas nunca pergunta sem recomendação.> - -## Abordagem proposta - -\```mermaid -flowchart LR - -\``` - -Componentes: - - - -- **``** (em `src/`): -- -- ****: `, o que adiciona, - se tem backfill. Se não houver: "Sem migration, sem collection, sem server action."> -- **Docs de framework (se couber):** - -## Fases verificáveis - - - -### Fase 1 — Tracer: - -- **Quota:** -- **Entrega:** -- **Aceite:** - - [ ] -- **Verify:** -- **Files:** `src/…`, `tests/…` -- **Tamanho:** S | M - -### Fase 2 — - -- **Quota:** … -- **Entrega:** … -- **Aceite:** … -- **Verify:** … -- **Files:** … -- **Tamanho:** S | M - -### Checkpoint (se >2 fases) - -- [ ] Aceites das fases 1–N verdes; sistema compilável; seguir ou cortar rabbit hole X. - -## Dependências - -- - -## Não escopo - -- - -## Rabbit holes - - - -- **.** Se alguém “só completar”: . **Mitigação neste item:** . - -## Adiado com gatilho - - - -- **.** Revisitar quando: . - -## Referências - -- GitHub Issue # (spec + frontmatter `id/depends/serializes/priority/model`) -- -- AGENTS.md — -- `PRODUCT.md` / `DESIGN.md` — -```` - -Notas: - -- No bloco mermaid acima, remover as barras invertidas dos fences internos (`\``` → ` ``` `). -- Nível de detalhe alvo: ~100–160 linhas (Premissas + Fases acrescentam estrutura, não prosa). Menos que ~80 costuma significar que o Passo 3 (exploração do código) foi pulado. -- Classe **A**: omita a seção "Design (Impeccable)" e use no cabeçalho `Impeccable: A — N/A (sem superfície UI)`. -- Seção **Dados → decisão → apresentação** é sempre presente (preenchida ou `Dados: N/A`); não omita. -- Seções **Premissas**, **Objetivos (critérios de aceite)**, **Boundaries**, **Fases verificáveis** são sempre presentes. Chore trivial: Premissas 1 linha, Boundaries defaults, Fases = 1 entrada. -- Não rode `/impeccable craft|critique|polish` ao criar o plano — só classifique e semeie; a skill `work-issue` executa o ciclo. -- Self-score de qualidade de decisão (0–5) antes de gravar: ver [decision-quality.md](decision-quality.md). Se Dados ≠ N/A, self-check em [data-presentation.md](data-presentation.md) (≥3/5). Se Rabbit holes ou Adiado com gatilho estiverem vazios de propósito, escreva `Nenhum neste item.` — não omita a seção. diff --git a/.cursor/skills/plan-issue/shaping.md b/.cursor/skills/plan-issue/shaping.md new file mode 100644 index 00000000..2c553e43 --- /dev/null +++ b/.cursor/skills/plan-issue/shaping.md @@ -0,0 +1,56 @@ +# Shaping de intenção (plan-issue) + +Filtros para fatiar e descrever o que o humano quer — **sem** travar engenharia. Inspirado em Shape Up / 37signals (appetite, build less) e no caro-vs-barato de produto. Decisão silenciosa de produto é defeito; decisão silenciosa de schema aqui é **erro** (isso pertence ao plano de implementação). + +## Fatia mínima útil + +Cada Issue deve: + +1. Caber num **appetite** declarado (tempo fixo, escopo flexível). +2. Entregar **um** outcome verificável sozinho. +3. Ser compreensível sem ler o epic-pai. + +Se o pedido humano misturar jobs (ex.: “mapa + CSV + invite”), **separe** — a menos que um sem o outro não tenha valor. + +| Preferir separar | Preferir manter junto | +| ---------------- | --------------------- | +| Outcomes distintos para personas distintas | Mesmo fluxo, mesma tela, mesmo aceite | +| Um pedaço desbloqueia o outro depois | Separar criaria handoff inútil | +| Um pedaço é jurídico/LGPD e o outro não | — | + +## Appetite + +Declare quanto o slice vale, não estimativa aberta: + +- Exemplos: `~0,5 dia, só copy/fluxo` · `~1 dia, um encaixe em lista existente` · `~1–2 dias, fluxo novo numa rota` +- Se a intenção não cabe → cortar rabbit holes / Fora de escopo, **não** inflar o item nem detalhar engenharia para “caber”. + +## Caro de reverter (produto) vs barato + +No plano de intenção, “travar” só o que é caro **de produto** e já é política do repo ou decisão humana explícita: + +| Pode afirmar na intenção | Deixar para o plano de implementação | +| ------------------------ | ------------------------------------ | +| Leader lockdown / assimetria de votos | Collection nova, unicidade, shape do schema | +| Fail-closed de consentimento (conceito) | Chave `Consent` concreta, textos versionados | +| “Sem % estadual absoluto” | Chart vs tabela vs mapa | +| Appetite e fora de escopo | Nomes de utilities, signatures, migration | + +## Rabbit holes de produto + +Armadilhas se alguém “só completar” o desejo humano: + +- Segundo cadastro de pessoa paralelo a `Contact` +- Spreadsheet mode / edição em massa sem pedido +- Dashboard de vaidade sem decisão nomeável +- Escopo que puxa redesign de paleta ou shell inteiro + +## Self-score (0–5, gate ≥4) + +1. Fatia = um outcome verificável? +2. Appetite declarado e a intenção cabe nele? +3. Persona + job + aceite claros (sem jargão de stack)? +4. Direção no codebase é hipótese (não contrato técnico)? +5. Zero decisões duras de engenharia no plano? + +<4 → corrigir antes de gravar / registrar Issue. diff --git a/.cursor/skills/rebase-on-main/SKILL.md b/.cursor/skills/rebase-on-main/SKILL.md index 76d38414..81a431ec 100644 --- a/.cursor/skills/rebase-on-main/SKILL.md +++ b/.cursor/skills/rebase-on-main/SKILL.md @@ -203,6 +203,7 @@ Opcional e leve (se o rebase tocou schema/types): `pnpm exec tsc --noEmit` nos a ## Posição no fluxo ``` -plan-issue → work-issue (claim → execução → /simplify+/impeccable - → rebase-on-main → capture-review-debts → PR --base stage → merge) +plan-issue → work-issue | agent-work-issue + (impl plan → execução → /simplify → rebase-on-main? + → capture-review-debts → PR --base main → merge) ``` diff --git a/.cursor/skills/work-issue/SKILL.md b/.cursor/skills/work-issue/SKILL.md index 5c9beace..e6792ef1 100644 --- a/.cursor/skills/work-issue/SKILL.md +++ b/.cursor/skills/work-issue/SKILL.md @@ -1,159 +1,80 @@ --- name: work-issue -description: Conduz uma GitHub Issue rastreável do Teqo do claim ao merge em main — prep (pnpm i), claim (pnpm agent:claim), renomear a sessão + abrir o plano no editor, verificação do modelo declarado na Issue, freshness audit do plano, execução das fases (schema/server → UI via /impeccable → gates), /simplify (3 reviewers paralelos via Task) + capture-review-debts, PR --base main com Closes #N, acompanhamento do CI até o merge. Usar quando o usuário pedir para implementar/trabalhar uma Issue ("trabalha a issue #N", "implementa o B79", "pega a próxima issue", "vamos fazer o X", "continua a implementação"). +description: >- + Versão humana de agent-work-issue: claim de uma Issue, Plan mode com plano + de implementação deliberado a partir da intenção, pausa para confirmação + humana, depois execução → /simplify → capture-review-debts → PR Ready em + main com auto-merge. Use quando o usuário pedir /work-issue, quiser + supervisionar a abordagem de engenharia, ou trabalhar uma Issue com gate + humano antes do código. +disable-model-invocation: true --- -# Trabalhar uma Issue (claim → merge em main) +# Work-issue (humano supervisiona) -Esta skill conduz UMA Issue rastreável do claim ao merge em `main`. Substitui o antigo fluxo implement-roadmap-item + close-delivery + ship-to-main. **GitHub Issues são a fonte canônica** de spec/status/deps/prio/modelo; `docs/roadmap.md` é legado congelado e nunca é editado aqui (registro de entrega vai na Issue + `docs/plans/.md` + notebook do projeto). +Mesmo pipeline de [`agent-work-issue`](../agent-work-issue/SKILL.md), com duas diferenças: -**Proibido neste fluxo:** qualquer acesso a DB de prod, merge sem CI green, editar outras Issues `in-progress`. +1. **Você claima** a Issue (`pnpm agent:claim`). +2. **Pausa após o plano de implementação** até o humano confirmar — só então executa. -**Qualidade de decisão e dados:** aplique [decision-quality.md](../plan-issue/decision-quality.md) ao auditar e fatiar (caro vs barato, Opções+Recomendação+rejeitadas, appetite, rabbit holes, tracer bullet cedo) e [data-presentation.md](../plan-issue/data-presentation.md) ao auditar/fatiar superfícies com KPI/mapa/série/ranking. +**Proibido:** DB de prod; merge sem CI green; editar outras Issues `in-progress`; pular a pausa do impl plan; Draft / sem auto-merge. -## Checklist do fluxo +## Checklist ``` -- [ ] 0. Prep: `pnpm i` antes do `agent:claim` (Cloud Agents já rodam via `.cursor/environment.json` → `install`; worktrees locais: obrigatório, ou deixe para o `ensure-repo-deps` do `pnpm push`). Cloud → §Prep Cloud (sem Docker; não use `db:start`) -- [ ] 1. Claim: pnpm agent:claim (ou -- --issue ) — o brief do stdout é o contrato -- [ ] 1b. Renomear a sessão (cursor-app-control rename_chat) — padrão `# — ` (linha `rename_chat:` do brief) -- [ ] 1c. Abrir o plano no editor (cursor-app-control open_resource) para o humano acompanhar -- [ ] 2. Verificação de modelo (best effort): comparar model: da Issue com o modelo da sessão -- [ ] 3. Freshness audit (enxuto) do plano contra o repositório -- [ ] 4. Executar as fases (schema/server → UI via /impeccable → gates de engenharia) -- [ ] 5. /simplify no diff da sessão → capture-review-debts -- [ ] 6. Fechar em main: branch → commit → **`pnpm push -u origin HEAD`** (não `git push` nu) → gh pr create --base main (Closes #N, sem --draft) - → gh pr merge --auto --merge → gh pr checks --watch --required até o merge → consolidar pontas soltas +- [ ] 0. Prep: `pnpm i` se preciso +- [ ] 1. Claim: pnpm agent:claim (ou -- --issue ) +- [ ] 1b. rename_chat + open_resource (intenção) +- [ ] 2. Verificação de modelo (assimétrica — pausa se sessão mais forte) +- [ ] 3. Plan mode → escrever `*-impl.md` → **PARAR e confirmar com o humano** +- [ ] 4. Após “ok”: executar como agent-work-issue Passos 4–6 ``` -## Passo 0 — Prep (deps) - -`pnpm agent:claim` depende de `node_modules` (scripts em `scripts/` importam pacotes do projeto). Em worktree/clone fresco, ou quando `node_modules` não existir, rode **antes** do claim: +## Passo 0–1 — Prep e claim ```bash pnpm i +pnpm agent:claim # topo da fila +pnpm agent:claim -- --issue ``` -Não pule este passo — sem deps instaladas o claim falha antes de imprimir o brief. +O brief do stdout é o contrato de **produto** (id, prio, model, link da intenção). Claim = `ready → in-progress`. -### Prep Cursor Cloud (sem Docker) +### Rename + abrir intenção -VMs Cloud **não têm Docker** — `pnpm db:start` (docker compose) **não funciona** aqui. O ambiente já sobe Postgres nativo via `.cursor/environment.json` (`cloud-setup.sh` no install + `ensure-postgres.sh` no start): bancos `teqo` e `teqo_test` migrados e com `db:seed:minimal`. +Padrão: `# — ` via `cursor-app-control` `rename_chat`. +Abra o plano de intenção com `open_resource` (`file:///…/docs/plans/.md`). -- **`pnpm gate:fast` e `pnpm gate:push` não precisam de Postgres** — `test:unit` usa `DATABASE_URL` inválida de propósito (sem conexão real). -- Só `pnpm test:int` / `pnpm migrate` tocam o banco — já preparado no install; se conexão falhar: `bash ./.cursor/ensure-postgres.sh`. -- **Proibido `git push --no-verify`** no fechamento da Issue. O pre-push roda `pnpm gate:push` (~1–2 min) — espere passar; CI não substitui o hook local. +## Passo 2 — Modelo -## Passo 1 — Claim +Regra assimétrica (`model-selection`): -```bash -pnpm agent:claim # pega o topo da fila (ready, desbloqueadas, por prio) -pnpm agent:claim -- --issue # quando o usuário especifica -``` +- Sessão **mais fraca** que `model:` → informa e segue +- Sessão **mais forte** → **informa e pausa** (“pedir X, está em Y — seguir?”) +- Ausente → aplique model-selection e registre na Issue -O brief impresso no stdout é o contrato: id, priority, **model**, spec, link do plano. O claim faz o swap de label `ready → in-progress` com lock otimista. Se falhar ("claimed by someone else"), re-rodar — nunca forçar. +## Passo 3 — Plano de implementação + GATE humano -### Renomear a sessão +Siga o **Passo 3** de `agent-work-issue` (Plan mode, engineering-brief, decision-quality, `docs/plans/-impl.md`). -Logo após o claim bem-sucedido, **renomeie a aba desta conversa** para o humano identificar o trabalho em curso — não espere ele pedir. Política do repo: no fluxo `work-issue` isso **não** é opcional — a descrição padrão da ferramenta MCP ("só quando o usuário pedir") não se aplica aqui. +Depois de gravar o impl plan: -**Padrão canônico** (decisão travada — use sempre este formato, sem variações): +1. Apresente no chat: abordagem recomendada, opções rejeitadas, fases, riscos, o que diverge da hipótese de direção da intenção. +2. **Pare.** Não escreva código de feature até confirmação explícita (“ok”, “pode executar”, “aprovado”, …). +3. Se o humano pedir mudança de abordagem → revise o `*-impl.md` e reapresente. +4. Confirmação → marque Status `aprovado` no impl plan → `SwitchMode` `agent` → continue. -| Campo | Fonte no brief | Regra | -| ----- | --------------- | ----- | -| `#` | linha `Claimed # — …` | número GitHub da Issue; sempre presente | -| `` | linha `id: …` | id de roadmap (`B79`, `C11`, `FD2+`…); omita o segmento se `(none)` | -| `` | título da Issue (após `Claimed # — `) | se começar com ` — `, remova esse prefixo para não duplicar | +## Passos 4–6 — Execução -**Montagem:** +Idênticos a `agent-work-issue`: -- Com `id`: `# — ` — ex.: `#456 B79 — Mapa LQ na legenda` -- Sem `id`: `# — ` — ex.: `#789 — Hotfix cookie path` +- Executar fases (schema/server → UI → gates) +- `/simplify` completo + fixes +- `capture-review-debts` **com** gate humano (Passo 5 daquela skill) — não use o modo autônomo do pool +- `pnpm push` → PR Ready `--base main` + `Closes #N` → auto-merge → `gh pr checks --watch --required` -**Truncagem:** limite da ferramenta = 200 caracteres. Preserve intactos `#` e ``; encurte só o `` no final (reticências opcionais). +Detalhes, Prep Cloud, invariantes e templates: leia [`agent-work-issue/SKILL.md`](../agent-work-issue/SKILL.md) e os arquivos em `.cursor/skills/work-issue/` (`engineering-brief.md`, `implementation-template.md`, `decision-quality.md`). -**Passos:** +## Resumo final -1. Monte o título pelo padrão acima a partir do stdout do claim. -2. Chame `cursor-app-control` → `rename_chat` com `title`. -3. Se `rename_chat` não estiver disponível (fora da Agents Window), informe em uma linha o título exato sugerido para o humano renomear manualmente — mas tente o MCP primeiro. - -Se a sessão continuar uma Issue já claimada (sem re-rodar claim), derive os mesmos campos do brief anterior na conversa ou de `gh issue view --json number,title` + frontmatter `id:`. - -### Abrir o plano para o humano - -Em seguida, **abra o arquivo do plano no editor** para quem acompanha a sessão — não basta `Read` no chat. - -1. Extraia o caminho do plano do brief (`docs/plans/.md`) ou do link `Plano:` no body da Issue. -2. Chame `cursor-app-control` → `open_resource` com URI absoluta, ex.: `file:///…/docs/plans/foo.md` (caminho relativo resolvido a partir da raiz do workspace). -3. Informe em uma linha qual plano foi aberto (`B79 — docs/plans/…`). - -Se `open_resource` não estiver disponível (fora da Agents Window), diga o caminho absoluto do plano para o humano abrir manualmente — mas tente o MCP primeiro. - -## Passo 2 — Verificação de modelo (best effort, não programática) - -Leia a propriedade `model:` do brief e compare com o modelo da sessão atual pela tabela de capacidade de `model-selection` (Composer 2.5 ↔ Grok 4.5 ↔ Kimi K3 Low). Regra **assimétrica** (decisão travada 2026-07-30): - -- Sessão **mais fraca** que o especificado → assume-se escolha consciente do humano: **informa em uma linha e continua, sem pausar**. -- Sessão **mais forte** que o especificado → possível erro do humano: **informa e pausa** ("a Issue pede X, você está em Y — seguir mesmo assim?"). -- Propriedade **ausente** → aplique `model-selection` uma vez e registre a escolha na Issue (`gh issue edit ` no body, frontmatter `model:`). -- Issue `{id}-exec` / `kimi-k3-low` sem a dep de plan `done` → **pare** (não execute sem plano fechado). - -Subagentes despachados via `Task` saem **no modelo da propriedade** quando couber (`Task.model` ∈ `composer-2.5` | `cursor-grok-4.5-high` | `kimi-k3-low`; se a Issue for `-low`/`-medium` e o enum não listar, `inherit` — ver model-selection). - -## Passo 3 — Freshness audit (enxuto) - -O plano foi escrito no passado e o repositório andou. Cheque, afirmação por afirmação: - -- Arquivos citados no plano existem? Utilities "a reusar" têm a assinatura que o plano assume? -- Premissas de schema batem com `src/payload-types.ts` e `src/migrations/`? -- Deps do frontmatter estão `done`/`in-prod` (ou fechadas)? Dependência dura não entregue → pare e proponha (fazer a dep primeiro ou corte explícito). -- Questões em aberto já respondidas pelo código/notebook? - -Desfechos: **divergência factual** (caminho renomeado, assinatura) → corrija o plano na mesma sessão e siga; **divergência material de produto** → pare e pergunte. Não reescreva o plano inteiro: freshness audit é enxuto, não uma re-planificação. - -## Passo 4 — Executar as fases - -Ordem fixa, fases pequenas e verificáveis, respeitando o appetite do plano: - -1. **Schema e server** — migrations (`pnpm migrate:create`, seguir `payload-migrations`), collections, utilities, server actions, testes de domínio. Guardrails do repo: Local API com `user` → `overrideAccess: false`; escrita multi-collection → transação com `req: { transactionID }`; pessoa → join com `Contact`; opt-in/PII → `Consent` por chave estável falhando fechado. -2. **UI via /impeccable** (classes B/C/D — a classe está no plano; se A, declare "Impeccable: N/A" e siga só engenharia): shape conforme a classe → craft → critique → (harden/optimize **só sob gatilho**) → polish. Paleta = tokens `data-theme='campaign'`; reusar `src/components/ui` e shells existentes; shape obrigatório em C **para** para confirmação do brief antes do craft. -3. **Gates de engenharia** — iteração: `pnpm gate:fast`. Push: **`pnpm push -u origin HEAD`** (ensure-deps + `gate:push` no script — não depende do hook Husky). `pnpm gate:push` manual só para debug sem push. Scan Aikido dos arquivos editados. Comandos bare, nunca piped. **Nunca** `git push --no-verify` cru (pula o gate) — em Cloud não há Docker, mas `gate:fast`/`gate:push` não precisam de banco (ver §Prep Cloud). - -Tracer bullet: se a Issue for grande, a primeira fatia vertical real (schema mínimo → uma action → uma superfície UI) vem cedo. - -## Passo 5 — /simplify + débitos - -Rode `/simplify` sobre o diff da sessão **antes** do Passo 6. Não pule os reviewers — o simplify deste fluxo é o comando completo, não uma leitura manual do diff. - -### Escopo - -1. `git diff --no-color` + `git diff --cached --no-color` (diff combinado da sessão). -2. Se vazio, arquivos/símbolos citados na conversa; se ainda vazio, `git show --stat --patch --no-color HEAD`. - -### Reviewers paralelos (obrigatório) - -Lance **três** subagentes via `Task` **em paralelo** na mesma mensagem — read-only, mesmo modelo da sessão, **sem editar arquivos**. Passe o diff combinado (ou lista de arquivos + hunks relevantes se o diff for grande): - -1. **Code quality** — comentários vazios, helpers de uso único, nullable desnecessário, try/catch amplos, abstração prematura, casts fracos, estado derivado duplicado, código morto. -2. **Performance** — I/O bloqueante em hot path, recomputação sem cache, busy-wait, concatenação em loop, N+1, logging em loop. -3. **Reuse** — padrões/helpers existentes no repo ou já presentes no diff que o escopo deveria reutilizar. - -### Fixes + débitos - -1. Agregue os achados dos três reviewers e aplique **fixes pontuais** no diff (comportamento preservado). -2. Achados maiores que o cleanup da sessão → skill `capture-review-debts` (registra via `agent:register`/`agent:file-miss`; **nunca** edita Issue `in-progress` — nem esta; débito do mesmo pai vira Issue nova com `depends: []`). -3. Resuma o que corrigiu e o que ficou como recomendação/débito. - -## Passo 6 — Fechar em main - -1. Branch `agent/-` (worktrees do Cursor são donos da criação; commits lógicos). -2. **`pnpm push -u origin HEAD`** — canonical; roda `ensure-repo-deps` + `gate:push` + push (não depende do Husky). Não use `git push` nu. -3. Abrir PR **Ready** (nunca draft) com `Closes #` — `.cursor/rules/agent-pr-workflow.mdc` (alwaysApply). Canal: `gh pr create --base main` **ou**, em Cloud, `ManagePullRequest` com **`draft: false`**. -4. `gh pr merge --auto --merge ` **imediatamente** após o PR existir Ready (`strict=false` na proteção de `main`). Não encerre o turn com PR Draft ou sem auto-merge armado. -5. **Acompanhe só os checks obrigatórios** (`gh pr checks --watch --required`): gate = `checks` + `migration-lock`; Vercel Git não bloqueia. Falha no ci-pr → corrige na mesma branch. **Conflito de merge** → rebase em `main` e reempurre. -6. O flip `in-progress → done` + `in-prod` é **determinístico no CI** (`issue-done-on-main-merge.yml`). Deploy gated fica em `ci.yml` — não é passo do agente. Consolide pontas soltas após o merge. - -## Resumo final ao usuário - -Issue trabalhada + sessão renomeada + plano aberto no editor + verificação de modelo (declarado vs sessão, ou registro do ausente), veredito do freshness audit, o que entrou por fase, resultado do critique/polish (se UI), simplify (3 reviewers paralelos) + débitos registrados, gates (`pnpm push`, sem `--no-verify` cru), link do PR e estado do merge em main, pontas soltas consolidadas. `done`+`in-prod` no merge; deploy gated em `ci.yml`. +Issue claimada · sessão renomeada · intenção aberta · modelo · **impl plan confirmado pelo humano** · execução · simplify/débitos · PR + merge. diff --git a/.cursor/skills/work-issue/decision-quality.md b/.cursor/skills/work-issue/decision-quality.md new file mode 100644 index 00000000..de486972 --- /dev/null +++ b/.cursor/skills/work-issue/decision-quality.md @@ -0,0 +1,51 @@ +# Qualidade de decisão (plano de implementação) + +Aplique ao escrever `docs/plans/-impl.md` em `work-issue` / `agent-work-issue`. O plano de **intenção** já fixou o outcome — aqui se escolhe a engenharia. Decisão silenciosa é defeito. + +## Caro vs barato + +Delibere e registre só o **caro de reverter**. O barato vira Não escopo, fill-in, ou “mais simples que funciona” com **gatilho de revisitação**. + +| Caro (decidir agora + rejeitadas) | Barato (adiar / mais simples) | +| --------------------------------- | ----------------------------- | +| Collection nova, unicidade, access | Polish cosmético, 2º estilo de card | +| Nova chave `Consent` / PII | Copy, motion, a11y P3 | +| Semântica multi-collection / locks | Prefetch, ordem de colunas | +| URL pública / slug imutável | Rename de pureza | +| Fronteira de módulo em volatilidade real | Adapter com 1 call site | + +## Forma obrigatória + +Toda decisão não trivial de engenharia: + +```text +Opções: A | B | C +Recomendação: B — porque … +Alternativas rejeitadas: A porque …; C porque … +``` + +No `*-impl.md`: **Decisões de engenharia** = decisão + por quê + rejeitadas. + +## Appetite e tracer bullet + +Respeite o appetite do plano de intenção. Se a abordagem estoura → cortar rabbit holes, não inflar. Preferir **tracer bullet** cedo (schema mínimo → uma action → uma superfície) antes de polish paralelo. + +## Depth check + +Antes de propor arquivo/utility/componente novo: + +1. Já existe módulo profundo? (`campaignAccess`, shells de lista, `withPayloadTransaction`, …) → **reusar** +2. Pass-through raso? → **não criar** +3. Conhecimento vazaria em N módulos? → **encapsular uma vez** + +## Rabbit holes Teqo + +Layers/cerimônia sem volatilidade; cadastro paralelo a `Contact`; Consent por ID hardcoded; DRY <3 call sites; redesign de paleta fora de `campaign`. + +## Self-score (0–5, gate ≥4) + +1. Decisões caras têm rejeitadas? +2. Abordagem cabe no appetite da intenção? +3. Rabbit holes nomeados? +4. Depth check: reusa shells/helpers existentes? +5. Intenção (aceite de produto) permanece satisfeita — engenharia não reescreveu o outcome? diff --git a/.cursor/skills/work-issue/engineering-brief.md b/.cursor/skills/work-issue/engineering-brief.md new file mode 100644 index 00000000..eccb746a --- /dev/null +++ b/.cursor/skills/work-issue/engineering-brief.md @@ -0,0 +1,36 @@ +# Brief de engenharia (carregar antes do plano de implementação) + +Leia o que for relevante **antes** de travar a abordagem no `*-impl.md`. Não invoque jornadas inteiras de skills — aplique o princípio no momento da decisão. + +## Pinos do repo (sempre) + +- `.cursor/rules/codebase-map.mdc` — direção `lib/` → `utilities/` → `components/` → `app/`; dono da feature +- `.cursor/rules/engineering-standards.mdc` — gates, type honesty, client boundary, caching ladder, access +- `.cursor/rules/security-critical.mdc` — quando tocar access/Local API/overrideAccess +- `AGENTS.md` — auth campanha, Consent fail-closed, transactions, naming, DB local +- `docs/ARCHITECTURE.md` — se a mudança cruza verticais ou modelo de domínio + +## Skills sob demanda (não tour) + +| Situação | Skill / princípio | +| -------- | ----------------- | +| Onde colocar lógica / dependência | `clean-architecture` — Dependency Rule; core testável | +| Complexidade de módulo / API | `software-design-philosophy` — deep modules, anti-classitis | +| Escopo v1 / cortar especulação | `37signals-way` / `pragmatic-programmer` — build less, tracer bullet, DRY de conhecimento | +| Schema Payload | skill `payload-migrations` | +| DB local / Cloud sem Docker | skill `local-database` | +| KPI/mapa/série na UI | [data-presentation.md](../plan-issue/data-presentation.md) pergunta 3 no impl | +| Domínio `/campanha` específico | rules `projects/*` se o item tocar aquele projeto | + +## Invariantes que o impl plan não pode violar + +- Local API com `user` → `overrideAccess: false` +- Escrita multi-collection → transação + `req: { transactionID }` +- Pessoa → join com `Contact` (não collection paralela) +- Opt-in/PII → `Consent` por chave estável, fail-closed +- `leader` lockdown; sem `estimatedVotes` para liderança +- Copy pt-BR / identificadores em inglês + +## Sistema de listas + +Se a superfície for lista/tabela: reusar shells (`CampaignTable`, URL canônica, column visibility, pending boundary) — ver codebase-map. Não reinventar. diff --git a/.cursor/skills/work-issue/implementation-template.md b/.cursor/skills/work-issue/implementation-template.md new file mode 100644 index 00000000..586a22a1 --- /dev/null +++ b/.cursor/skills/work-issue/implementation-template.md @@ -0,0 +1,65 @@ +# Template de plano de implementação (`docs/plans/-impl.md`) + +Criado em Plan mode por `work-issue` / `agent-work-issue` a partir do plano de **intenção**. Aqui a engenharia é deliberada — Opções + Recomendação + rejeitadas ([decision-quality.md](decision-quality.md)). + +O outcome de produto do plano de intenção é **inviolável**. A forma técnica é **livre** desde que mantenha aceite, appetite e invariantes do repo ([engineering-brief.md](engineering-brief.md)). + +````markdown +# Impl: + +Status: rascunho | aprovado | em execução +Atualizado em: +Issue: # +Intenção: docs/plans/.md +Appetite restante: + +## Leitura da intenção + +- **Outcome:** <1–2 frases, nas palavras do aceite de produto> +- **O que NÃO negociar:** +- **O que reavaliar:** + +## Abordagem recomendada + +\```mermaid +flowchart LR + … +\``` + +**Opções consideradas:** A | B | C +**Recomendação:** <…> — porque … +**Rejeitadas:** … + +### Componentes / mudanças + +- **``** (`src/…`): +- **Migration:** +- **Access / Consent:** +- **UI:** + +### Dados → forma (se aplicável) + +- Forma escolhida + por quê + rejeitadas (pergunta 3 de data-presentation) + +## Fases verificáveis + +1. **Tracer / schema+server** — +2. **UI** — <…> +3. **Gates** — `pnpm gate:fast`; push via `pnpm push` + +## Rabbit holes / Não escopo (engenharia) + +- … + +## Riscos e mitigação + +- … + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção ainda coberto +- [ ] Invariantes AGENTS/engineering-standards +- [ ] Testes de domínio previstos (unit/int) onde access/write paths mudam +```` + +Self-score decision-quality ≥4 antes de marcar `aprovado` ou executar. diff --git a/docs/AGENT-OPS.md b/docs/AGENT-OPS.md index 4071f557..863c5c72 100644 --- a/docs/AGENT-OPS.md +++ b/docs/AGENT-OPS.md @@ -18,7 +18,7 @@ claim → feature branch → PR --base main → CI PR green (cascade + skips) main → ci.yml full suite → vercel deploy --prod (se verde) → requeue se HEAD andou ``` -**Skills:** `plan-issue` → `work-issue` (claim → modelo → freshness → execução → /simplify → PR `--base main` → CI até o merge) → `project-status`. `docs/roadmap.md` = legado congelado; fonte canônica = GitHub Issues. +**Skills:** `plan-issue` (intenção + Issues) → `work-issue` (humano: claim → impl plan → confirmação → execução) ou `agent-work-issue` (pool: já claimada → impl plan → execução sem pausa) → `/simplify` → `capture-review-debts` → PR `--base main` → `project-status`. `docs/roadmap.md` = legado congelado; fonte canônica = GitHub Issues. - **Agente faz sozinho:** `pnpm agent:claim` → implementa → **`pnpm push`** → PR **Ready** (nunca draft) com `Closes #N` → `gh pr merge --auto --merge` → `gh pr checks --watch --required`. Regra always-on: `.cursor/rules/agent-pr-workflow.mdc`. Em Cursor Cloud: `ManagePullRequest` com `draft: false`, depois armar auto-merge via `gh pr merge --auto --merge` (o default draft da tool **não** vale neste repo). - **Só humano:** secrets Vercel (`VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID`), `POOL_GITHUB_TOKEN`, `pnpm configure:branch-protection`, editar envs Neon/Vercel. diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 223925b1..8380f331 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-02):** **Skills plan/work reescritas** — `plan-issue` agora só gera planos de **intenção** (persona/fluxo/aceite + direção suave no código; sem decisões duras de engenharia). Execução: `agent-work-issue` (pool — Plan mode → `*-impl.md` → executa → `/simplify` → `capture-review-debts` autônomo → PR Ready + auto-merge) e `work-issue` (humano — claim + **pausa** após o impl plan). Prompt do pool aponta para `agent-work-issue`. + **Recently resolved (2026-08-02):** **WhatsApp interno D3–D5 descartado** — Issues #30–#32 `wontfix` (bridge Baileys/Evolution/WAHA / linked device): risco de ban do número pessoal do staff. Canal no produto = só `wa.me`. Planos `whatsapp-interno-campanha.md`, `whatsapp-canal-fundacao.md`, `whatsapp-envio-liderancas.md`, `whatsapp-sugestao-atualizacoes.md` removidos; refs em `notifications.md` / convite·email listas / fluxos-ação / regra `nucleos-eleitorais` atualizadas. **Recently resolved (2026-08-02):** **Agent pool hard max → 12** — `POOL_HARD_MAX_SLOTS` de 8 → 12 (`scripts/lib/agent-pool-state.mjs`); default continua 5. Skill `agent-pool` alinhada. diff --git a/scripts/agent-pool.mjs b/scripts/agent-pool.mjs index baaa43bd..7aa52c37 100644 --- a/scripts/agent-pool.mjs +++ b/scripts/agent-pool.mjs @@ -3,7 +3,7 @@ * (docs/plans/agent-pool-orchestrator.md). * * The pool keeps up to POOL_MAX_SLOTS (default 5) Cursor Cloud Agents running - * work-issue over `ready` autonomous-eligible issues. The supervisor is + * agent-work-issue over `ready` autonomous-eligible issues. The supervisor is * REMOTE: a stateless GitHub Actions tick (`.github/workflows/agent-pool.yml`) * runs `workflow --action …` / `tick --live`; humans drive it via * `gh workflow run` (canonical channel). Local usage is inspection + diff --git a/scripts/lib/agent-pool-prompt.mjs b/scripts/lib/agent-pool-prompt.mjs index 32d9a1d4..224f277b 100644 --- a/scripts/lib/agent-pool-prompt.mjs +++ b/scripts/lib/agent-pool-prompt.mjs @@ -3,10 +3,9 @@ * (docs/plans/agent-pool-orchestrator.md §4/§5). * * The supervisor claims the issue BEFORE spawning (label flip + pool-worker - * marker comment — the coordinated-claim anti-race), so the worker SKIPS the - * claim step of work-issue and starts at step 1b. Everything else — gates, PR - * base main with Closes #N, auto-merge + CI watch — is the standard skill - * contract restated so the worker cannot miss it. + * marker comment — the coordinated-claim anti-race), so the worker SKIPS + * claim and runs `agent-work-issue` (autonomous: impl plan → execute → + * simplify → capture-review-debts → PR). Human-supervised path is `work-issue`. */ /** First docs/plans/*.md path in the issue body (the linked plan), if any. */ @@ -31,22 +30,24 @@ export const buildPoolWorkerPrompt = ({ issueNumber, issueTitle, issueId, planPa '', '## Fluxo', '', - 'Siga a skill `.cursor/skills/work-issue/SKILL.md` a partir do passo 1b, PULANDO o passo 1 (claim):', + 'Siga a skill `.cursor/skills/agent-work-issue/SKILL.md` por completo (Issue já claimada):', '1. Rode `pnpm i` se `node_modules` não existir.', planPath - ? `2. Plano: \`${planPath}\` — freshness audit enxuto (passo 3 da skill).` - : '2. O body da Issue é a spec (não há plano linkado — avalie se ele basta para trabalhar).', - '3. Execute as fases com os gates do repo: `pnpm gate:fast` antes do push (o pre-push roda `pnpm gate:push`). Comandos bare, nunca piped.', - `4. PR obrigatoriamente com \`gh pr create --base main\` e "Closes #${issueNumber}" no body.`, - '5. `gh pr merge --auto --merge ` e acompanhe `gh pr checks --watch --required` até o merge. Falha de CI no seu PR é sua (docs/AGENT-OPS.md — "Dono do PR, dono do CI"): corrija na mesma branch.', + ? `2. Plano de intenção: \`${planPath}\` — abra-o; depois Plan mode e escreva \`docs/plans/-impl.md\` (engenharia deliberada; pode divergir da hipótese de direção se o aceite de produto se mantiver).` + : '2. O body da Issue é a spec de intenção (não há plano linkado). Em Plan mode, escreva mesmo assim um `docs/plans/-impl.md` a partir do body.', + '3. Execute o impl plan. Gates: `pnpm gate:fast` na iteração; entrega com `pnpm push` (não `git push` nu). Comandos bare, nunca piped.', + '4. Rode `/simplify` completo; `capture-review-debts` em modo autônomo (só expensive_lock ≥4).', + `5. PR obrigatoriamente com \`gh pr create --base main\` e "Closes #${issueNumber}" no body (Ready, nunca draft).`, + '6. `gh pr merge --auto --merge ` e acompanhe `gh pr checks --watch --required` até o merge. Falha de CI no seu PR é sua (docs/AGENT-OPS.md — "Dono do PR, dono do CI"): corrija na mesma branch.', '', '## Proibido', '', '- `DATABASE_URL` de prod ou `ALLOW_REMOTE_DB` — o setup local (`.cursor/cloud-setup.sh` + seed mínimo) cobre tudo.', `- Editar outras Issues \`in-progress\` ou trabalhar fora da Issue #${issueNumber}.`, + '- Tratar o plano de intenção como contrato de engenharia — ele é intenção; o `*-impl.md` é a engenharia.', '', modelSlug - ? `Modelo declarado da Issue: \`${modelSlug}\` — você foi spawnado nele (ou no fallback documentado pelo pool); a verificação assimétrica do passo 2 não se aplica.` + ? `Modelo declarado da Issue: \`${modelSlug}\` — você foi spawnado nele (ou no fallback documentado pelo pool); a verificação assimétrica de work-issue não se aplica.` : 'A Issue não declara `model:` — você foi spawnado no default do pool (composer-2.5).', '', `Ao terminar — merge concluído ou falha terminal — comente na Issue #${issueNumber} o desfecho em uma linha e encerre.`, diff --git a/tests/unit/agentPoolPrompt.unit.spec.ts b/tests/unit/agentPoolPrompt.unit.spec.ts index 7fec64ef..4a6031a9 100644 --- a/tests/unit/agentPoolPrompt.unit.spec.ts +++ b/tests/unit/agentPoolPrompt.unit.spec.ts @@ -19,6 +19,12 @@ describe('buildPoolWorkerPrompt', () => { expect(prompt).toContain('in-progress') }) + it('points workers at agent-work-issue and forbids treating intention as engineering contract', () => { + expect(prompt).toContain('agent-work-issue/SKILL.md') + expect(prompt).toContain('*-impl.md') + expect(prompt).toContain('plano de intenção como contrato de engenharia') + }) + it('pins the PR contract: base main, Closes #N, auto-merge, CI watch', () => { expect(prompt).toContain('gh pr create --base main') expect(prompt).toContain('Closes #42') From 3001b9da0045e593075f3d936152fa6ea8dc1a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sun, 2 Aug 2026 14:05:53 -0300 Subject: [PATCH 2/3] Pin access memo int test to the actor's campaign_user read. Depth-1 populate of leftover leadership.user rows was counted as a second fresh-actor select and falsely failed the per-request memo guard. --- .../int/campaignAccessRequestMemo.int.spec.ts | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/tests/int/campaignAccessRequestMemo.int.spec.ts b/tests/int/campaignAccessRequestMemo.int.spec.ts index 4d4c867b..6bdd4720 100644 --- a/tests/int/campaignAccessRequestMemo.int.spec.ts +++ b/tests/int/campaignAccessRequestMemo.int.spec.ts @@ -16,10 +16,11 @@ const campaignFixtures = installCampaignFixtures({ }, }) -/** Counts pool statements matching `fragment` while `run` executes. */ +/** Counts pool statements matching `fragment` (or `match`) while `run` executes. */ const countStatements = async ( fragment: string, run: () => Promise, + match?: (text: string, params: unknown) => boolean, ): Promise<{ result: T; count: number }> => { const pool = (payload.db as unknown as { pool: { query: (...args: unknown[]) => unknown } }).pool const originalQuery = pool.query.bind(pool) @@ -33,7 +34,12 @@ const countStatements = async ( : typeof (first as { text?: unknown })?.text === 'string' ? ((first as { text: string }).text as string) : '' - if (text.includes(fragment)) count += 1 + const params = + typeof first === 'string' + ? args[1] + : ((first as { values?: unknown })?.values ?? args[1]) + const hits = match ? match(text, params) : text.includes(fragment) + if (hits) count += 1 return originalQuery(...args) } @@ -67,29 +73,41 @@ describe('access-control per-request memo', () => { const fixtures = campaignFixtures() const coordinator = await fixtures.createCampaignUser('coordinator') const municipality = await fixtures.getMunicipality() + const leadershipIds: number[] = [] for (let index = 0; index < 3; index += 1) { const contact = await fixtures.createContact() - await fixtures.createLeadership({ + const leadership = await fixtures.createLeadership({ contact: contact.id, municipalities: [municipality.id], supportStatus: 'engajado', }) + leadershipIds.push(leadership.id) } - const { result, count } = await countStatements('from "campaign_user"', () => - payload.find({ - collection: 'leadership', - // depth 1 is what the list loaders use; it is also what multiplies the - // field-level access checks across every populated relation. - depth: 1, - limit: 25, - user: coordinator, - overrideAccess: false, - }), + // Count only SELECT … WHERE id = . Other `campaign_user` rows can + // appear at depth 1 when leftover docs in the same municipality have + // `user` set — that is populate, not a broken memo. + const { result, count } = await countStatements( + `from "campaign_user"`, + () => + payload.find({ + collection: 'leadership', + // depth 1 is what the list loaders use; it is also what multiplies the + // field-level access checks across every populated relation. + depth: 1, + limit: 25, + user: coordinator, + overrideAccess: false, + where: { id: { in: leadershipIds } }, + }), + (text, params) => + text.includes('from "campaign_user"') && + Array.isArray(params) && + params.includes(coordinator.id), ) - expect(result.docs.length).toBeGreaterThan(0) + expect(result.docs).toHaveLength(3) // One read for the whole operation. Before the fix this was in the hundreds // and grew with rows × populated fields. expect(count).toBe(1) From 9e4c537f8f901deb12cfe4559b764de43c2fe3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sun, 2 Aug 2026 14:06:20 -0300 Subject: [PATCH 3/3] Format campaignAccessRequestMemo int spec with Prettier. --- tests/int/campaignAccessRequestMemo.int.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/int/campaignAccessRequestMemo.int.spec.ts b/tests/int/campaignAccessRequestMemo.int.spec.ts index 6bdd4720..d8b8e6a9 100644 --- a/tests/int/campaignAccessRequestMemo.int.spec.ts +++ b/tests/int/campaignAccessRequestMemo.int.spec.ts @@ -35,9 +35,7 @@ const countStatements = async ( ? ((first as { text: string }).text as string) : '' const params = - typeof first === 'string' - ? args[1] - : ((first as { values?: unknown })?.values ?? args[1]) + typeof first === 'string' ? args[1] : ((first as { values?: unknown })?.values ?? args[1]) const hits = match ? match(text, params) : text.includes(fragment) if (hits) count += 1 return originalQuery(...args)