diff --git a/.cursor/skills/work-issue/SKILL.md b/.cursor/skills/work-issue/SKILL.md new file mode 100644 index 00000000..43647710 --- /dev/null +++ b/.cursor/skills/work-issue/SKILL.md @@ -0,0 +1,81 @@ +--- +name: work-issue +description: Conduz uma GitHub Issue rastreável do Teqo do claim ao merge em stage — claim (pnpm agent:claim), verificação do modelo declarado na Issue, freshness audit do plano, execução das fases (schema/server → UI via /impeccable → gates), /simplify + capture-review-debts, PR --base stage 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"). +--- + +# Trabalhar uma Issue (claim → merge em stage) + +Esta skill conduz UMA Issue rastreável do claim ao merge em `stage`. 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). + +**Proibido neste fluxo:** `pnpm agent:promote` (promote stage→main é humano), qualquer acesso a DB de stage/prod, merge sem CI green, editar outras Issues `in-progress`. + +**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. + +## Checklist do fluxo + +``` +- [ ] 1. Claim: pnpm agent:claim (ou -- --issue ) — o brief do stdout é o contrato +- [ ] 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 stage: fast gate → branch → commit → gh pr create --base stage (Closes #N) + → gh pr merge --auto --merge → gh pr checks --watch até o merge → consolidar pontas soltas +``` + +## Passo 1 — Claim + +```bash +pnpm agent:claim # pega o topo da fila (ready, desbloqueadas, por prio) +pnpm agent:claim -- --issue # quando o usuário especifica +``` + +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 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`. 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:`). + +Subagentes despachados via `Task` saem **no modelo da propriedade** quando couber (`Task.model`). + +## 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** — fast gate local: `pnpm lint`, `pnpm exec tsc --noEmit`, `pnpm test:unit`, `pnpm format:check`, `pnpm check:cycles` (+ `pnpm migrate && pnpm db:seed:minimal && pnpm test:int` se tocou schema). Scan Aikido dos arquivos editados. Comandos bare, nunca piped. + +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. Follow-ups maiores que o cleanup → `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: []`). + +## Passo 6 — Fechar em stage + +1. Fast gate verde (Passo 4.3). +2. Branch `agent/-` (worktrees do Cursor são donos da criação; commits lógicos). +3. `gh pr create --base stage` com `Closes #` no body. +4. `gh pr merge --auto --merge `. +5. **Acompanhe os checks até o merge** (`gh pr checks --watch`): falha no ci-pr → corrige na mesma branch e reempurra (o auto-merge dispara de novo quando verde). +6. O flip `in-progress → done` é **determinístico no CI** (workflow `issue-done-on-stage-merge.yml` lê o `Closes #N` no merge em stage). O agente espera o merge apenas para verificar e **consolidar pontas soltas**: débitos não registrados, doc da sessão pendente (notebook do projeto + `docs/plans/.md` com Status/Atualizado em), comentário de fechamento na Issue com o que entrou. Se o agente falhar depois do merge, o status já está correto. + +## Resumo final ao usuário + +Issue trabalhada + 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 + débitos registrados, gates, link do PR e estado do merge em stage, pontas soltas consolidadas. Nunca anuncie promote — `in-prod` é humano (`pnpm agent:promote --i-am-human`). diff --git a/.github/workflows/issue-done-on-stage-merge.yml b/.github/workflows/issue-done-on-stage-merge.yml new file mode 100644 index 00000000..c03b877e --- /dev/null +++ b/.github/workflows/issue-done-on-stage-merge.yml @@ -0,0 +1,28 @@ +name: Stage merge flips claimed issues to done + +on: + pull_request: + types: [closed] + +permissions: + issues: write + pull-requests: read + contents: read + +jobs: + issue-done: + if: github.event.pull_request.base.ref == 'stage' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + steps: + - name: Flip Closes/Fixes #N issues from in-progress to done + run: | + body="$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json body --jq .body)" + numbers="$(printf '%s' "$body" | grep -oiE '(closes|fixes) #[0-9]+' | grep -oE '[0-9]+' | sort -u)" + for n in $numbers; do + gh issue edit "$n" --repo "$GITHUB_REPOSITORY" --remove-label in-progress --add-label done || true + gh issue comment "$n" --repo "$GITHUB_REPOSITORY" \ + --body "Merged em stage via PR #${PR_NUMBER} — done; \`in-prod\` entra no promote humano." || true + done diff --git a/scripts/agent-claim.mjs b/scripts/agent-claim.mjs index 790eec7a..2146a787 100644 --- a/scripts/agent-claim.mjs +++ b/scripts/agent-claim.mjs @@ -79,7 +79,8 @@ const queue = openReady if (flags['dry-run'] || flags['dryrun']) { console.log(`[agent:claim] ${queue.length} unblocked ready issue(s):`) for (const entry of queue) { - console.log(` #${entry.issue.number} [${entry.priority}] ${entry.issue.title}`) + const model = entry.meta.model ? ` model:${entry.meta.model}` : '' + console.log(` #${entry.issue.number} [${entry.priority}]${model} ${entry.issue.title}`) } process.exit(0) } @@ -119,6 +120,15 @@ gh([ const { rest } = parseFrontmatter(pick.issue.body) console.log(`\n[agent:claim] Claimed #${pick.issue.number} — ${pick.issue.title}`) console.log(` id: ${pick.meta.id ?? '(none)'} priority: ${pick.priority}`) +if (pick.meta.model) { + console.log( + ` model: ${pick.meta.model} (propriedade da Issue — verificar, não recalcular; ver skill model-selection)`, + ) +} else { + console.log( + ' model: ausente — aplicar model-selection uma vez e registrar na Issue (gh issue edit)', + ) +} if (pick.satisfiedWithoutIssue.length > 0) { console.log( ` deps sem issue (roadmap entregue, satisfeitas): ${pick.satisfiedWithoutIssue.join(', ')}`,