diff --git a/.cursor/skills/rebase-on-main/SKILL.md b/.cursor/skills/rebase-on-main/SKILL.md index 0e5bc7ac..2f15ab6d 100644 --- a/.cursor/skills/rebase-on-main/SKILL.md +++ b/.cursor/skills/rebase-on-main/SKILL.md @@ -2,10 +2,11 @@ name: rebase-on-main description: >- Sync the current feature branch with latest origin/main via fetch + rebase, - then resolve conflicts intelligently. Use after /simplify & /impeccable and - before capture-review-debts when the user says "pull main, rebase on main, - resolve conflicts", "rebase on main", "atualiza com main", "sincroniza com - main", or asks to bring the worktree branch up to date with main. + then resolve conflicts intelligently. Auto-handles a dirty tree (commit WIP + or stash+pop — no asking). Use after /simplify & /impeccable and before + capture-review-debts when the user says "pull main, rebase on main, resolve + conflicts", "rebase on main", "atualiza com main", "sincroniza com main", + or asks to bring the worktree branch up to date with main. --- # Rebase on main @@ -20,9 +21,11 @@ Etapa do fluxo de entrega Teqo (worktree) **depois** de `/simplify` + `/impeccab ``` - [ ] 1. Detectar ambiente (branch, worktree, dirty tree) +- [ ] 1b. Se dirty: decidir commit vs stash (sem perguntar) e limpar a árvore - [ ] 2. Fetch origin/main - [ ] 3. Rebase da branch atual em cima de origin/main -- [ ] 4. Resolver conflitos (ou abortar e perguntar se intents colidem) +- [ ] 4. Resolver conflitos do rebase (ou abortar e perguntar se intents colidem) +- [ ] 4b. Se usou stash: stash pop e resolver conflitos do pop - [ ] 5. Verificar resultado e reportar ``` @@ -40,17 +43,71 @@ git log --oneline -5 HEAD git log --oneline -3 origin/main 2>/dev/null || true ``` +Se dirty, também colete (em paralelo com o restante do diagnóstico): + +```bash +git diff --stat +git diff --cached --stat +git diff --name-only +git diff --cached --name-only +git status --porcelain +git log --oneline origin/main..HEAD 2>/dev/null | head -20 +git diff --name-only origin/main...HEAD 2>/dev/null +``` + **Guards (pare e explique se verdadeiros):** -| Condição | Ação | -| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Branch é `main` / `master` | Não rebasear main em si. Pare. | -| Detached HEAD | Pare; peça branch nomeada ou checkout. | -| Working tree dirty (uncommitted) | Stash **só** se o usuário autorizar; preferência: commit primeiro via `ship-to-main` parcial, ou stash com mensagem `rebase-on-main:`. Sem dirty tree limpa → não invente stash. | -| Rebase / merge / cherry-pick já em andamento | Continue ou `git rebase --abort` **só** com confirmação explícita. | +| Condição | Ação | +| -------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Branch é `main` / `master` | Não rebasear main em si. Pare. | +| Detached HEAD | Pare; peça branch nomeada ou checkout. | +| Working tree dirty (uncommitted) | **Não pergunte.** Decida e execute sozinho — ver [Passo 1b](#passo-1b--árvore-suja-auto). | +| Rebase / merge / cherry-pick já em andamento | Continue ou `git rebase --abort` **só** com confirmação explícita. | Se `GIT_DIR != GIT_COMMON` (e não é submodule): está em worktree — OK; rebase acontece **aqui**, na feature branch. +## Passo 1b — Árvore suja (auto) + +Objetivo: working tree limpa **antes** do rebase, sem pedir autorização. Escolha **um** caminho e anuncie em uma linha (“dirty → commit …” ou “dirty → stash …”). + +### Decisão (avaliar nesta ordem; primeira que bater vence) + +| # | Condição | Caminho | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| 1 | Há secretos / credenciais / env local no dirty set (`.env`, `.env.*`, `credentials.json`, `*.pem`, tokens óbvios no diff) | **stash** | +| 2 | Dirty é só ruído local / efêmero: untracked scratch, IDE/OS (`.DS_Store`, `.idea/`, `*.swp`), cache, arquivos fora de `src/` `tests/` `docs/` `scripts/` `.cursor/skills/` `.github/` e sem relação com o tip da branch | **stash** | +| 3 | Dirty é mistura: parte parece WIP da feature **e** parte é ruído/local/secrets → **não** faça commit parcial no escuro | **stash** | +| 4 | Diff é WIP intencional da feature: toca os mesmos paths (ou domínio) de `origin/main...HEAD`, ou continua o intent dos commits recentes da branch; dá para redigir **uma** mensagem de commit coerente com o estilo do repo | **commit** | +| 5 | Branch ainda sem commits próprios (`origin/main..HEAD` vazio) mas o dirty é claramente o trabalho da feature (plano/roadmap item, arquivos de entrega) e a mensagem fecha em uma frase | **commit** | +| 6 | Caso ambíguo / WIP quebrado demais para descrever sem inventar história / não passa no feeling de “um commit” | **stash** | + +**Default se empatar:** **stash** (reversível; não polui o histórico). **Nunca** invente stash/commit com árvore já limpa. + +### Caminho A — commit + rebase + +Siga o protocolo de commit do usuário (status/diff/log → mensagem → `git add` seletivo → `git commit` via HEREDOC). Regras extras desta skill: + +- Commit **só** o dirty set da feature; não stageie secrets (se aparecerem no meio, **mude para stash** em vez de commitar o resto). +- Mensagem: 1–2 frases no estilo do `git log` da branch; foque no porquê (evitar “wip:” genérico salvo se o log local já usa isso). +- Se o pre-commit hook **falhar**: corrija e faça um **novo** commit (não `--amend`, salvo as regras do usuário). Se ficar claro que o dirty não era commitável, e o commit desta invocação ainda é o tip local não-pushed que **você** criou agora: `git reset --soft HEAD~1`, então mude para **stash** e siga. +- Depois do commit limpo → Passo 2 → rebase → conflitos do rebase → Passo 5. +- Esse commit fica na branch (entra no `ship-to-main` depois). Não faça push aqui. + +### Caminho B — stash + rebase + stash pop + +```bash +git stash push -u -m "rebase-on-main:" +# … Passo 2 + rebase (Passo 3–4) até a branch estar rebased e limpa … +git stash pop +``` + +- Use `-u` para incluir untracked (senão o rebase ainda pode falhar / o “limpo” é mentira). +- Se o Passo 2 concluir “já sincronizado” (no-op): **mesmo assim** faça o `stash pop` (Passo 4b) antes de encerrar — o stash não pode ficar órfão. +- Se `stash pop` gerar conflitos: resolva como no Passo 4 (ler os dois lados; preservar intent). Depois `git add` nos resolvidos — **não** crie commit só do pop; deixe o resultado como dirty working tree restaurada, a menos que o usuário peça commit. +- Se `stash pop` falhar de forma estranha (stash parcialmente aplicado): pare, reporte `git stash list` + status, e **não** rode `stash drop` no escuro. +- Em sucesso do pop sem conflitos, o stash entry costuma dropar sozinho; confirme com `git stash list`. +- Conflitos de produto irreconciliáveis no pop → explique e pergunte (mesmo critério do Passo 4); não descarte o stash. + ## Passo 2 — Fetch ```bash @@ -65,7 +122,7 @@ git merge-base HEAD origin/main git log --oneline --left-right HEAD...origin/main | head -40 ``` -Se já está up-to-date (`HEAD` contém `origin/main` e não há commits de main à frente): reporte "já sincronizado" e pare (sucesso no-op). +Se já está up-to-date (`HEAD` contém `origin/main` e não há commits de main à frente): reporte "já sincronizado". Se o Passo 1b usou **stash**, ainda assim execute o Passo 4b (`stash pop`) antes de encerrar; senão pare (sucesso no-op). ## Passo 3 — Rebase @@ -122,21 +179,26 @@ Opcional e leve (se o rebase tocou schema/types): `pnpm exec tsc --noEmit` nos a **Resumo ao usuário (curto):** 1. Branch + worktree path -2. Quantos commits rebased / se no-op -3. Conflitos resolvidos (paths + uma linha de racional cada) **ou** “nenhum” -4. Se abortou: por quê + o que precisa de decisão -5. Próximo passo natural: `capture-review-debts` (se ainda não rodou), `ship-to-main`, ou o atalho `close-delivery` (rebase+debts+ship) +2. Se a árvore estava suja: caminho escolhido (**commit** com hash/msg **ou** **stash** + resultado do pop) e a regra da tabela que decidiu +3. Quantos commits rebased / se no-op +4. Conflitos resolvidos — rebase e/ou stash pop (paths + uma linha de racional cada) **ou** “nenhum” +5. Se abortou: por quê + o que precisa de decisão +6. Próximo passo natural: `capture-review-debts` (se ainda não rodou), `ship-to-main`, ou o atalho `close-delivery` (rebase+debts+ship) ## Anti-padrões -| Nunca | Em vez disso | -| -------------------------------------------- | ------------------------------------------------------------- | -| `git push --force` sem pedido | Deixe force-with-lease para `ship-to-main` / pedido explícito | -| Resolver conflito sem ler os dois lados | Abra o arquivo + `git show :1/:2/:3:` se precisar | -| Continuar rebase com markers `<<<<<<<` | Grep por `^<<<<<<<` antes de `--continue` | -| Rebasear em cima de `main` local stale | Sempre `fetch` + `origin/main` | -| `reset --hard` para “limpar” conflito | Abort ou resolve; hard reset só com pedido explícito | -| Expandir escopo (refatorar além do conflito) | Só o necessário para o rebase passar | +| Nunca | Em vez disso | +| ----------------------------------------------- | ----------------------------------------------------------------------------- | +| Perguntar commit vs stash com árvore suja | Decida pela tabela do Passo 1b e execute | +| `git push --force` sem pedido | Deixe force-with-lease para `ship-to-main` / pedido explícito | +| Commitar `.env` / secrets para “limpar” a árvore | Stash (regra 1); nunca stageie secret | +| Stash sem `-u` deixando untracked no caminho | `git stash push -u -m "rebase-on-main:"` | +| Resolver conflito sem ler os dois lados | Abra o arquivo + `git show :1/:2/:3:` se precisar | +| Continuar rebase com markers `<<<<<<<` | Grep por `^<<<<<<<` antes de `--continue` | +| Rebasear em cima de `main` local stale | Sempre `fetch` + `origin/main` | +| `reset --hard` para “limpar” conflito | Abort ou resolve; hard reset só com pedido explícito | +| Expandir escopo (refatorar além do conflito) | Só o necessário para o rebase passar | +| Dropar stash após pop conflictivo | Preserve o entry; reporte e pergunte | ## Posição no fluxo