feat: work-issue skill

This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-30 12:22:05 -03:00
parent 1c1a379897
commit 111308f00e
3 changed files with 120 additions and 1 deletions

View file

@ -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/<slug>.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 <N>) — 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 <N> # 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 <N>` 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: [<id-do-pai>]`).
## Passo 6 — Fechar em stage
1. Fast gate verde (Passo 4.3).
2. Branch `agent/<id>-<slug>` (worktrees do Cursor são donos da criação; commits lógicos).
3. `gh pr create --base stage` com `Closes #<N>` no body.
4. `gh pr merge --auto --merge <PR>`.
5. **Acompanhe os checks até o merge** (`gh pr checks <PR> --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/<slug>.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`).

View file

@ -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

View file

@ -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(', ')}`,