Implantar Afresto Next
Stack: backend Go (push→GHCR→Watchtower ~5 min) · web React (Vite→Cloudflare Worker,
manual wrangler deploy) · mobile RN/Expo em repositório separado <repo-mobile>
(EAS OTA). Deploy de produção = sempre pedir permissão do usuário primeiro (eles aprovam cada vez).
🔴 Regra #1 — este repositório é frequentemente usado por MUITOS AGENTS EM VÁRIAS ABAS
git status frequentemente exibe arquivos modificados de outra sessão. Por isso:
- NÃO
git add -a/git commit -a/git add .— isso vai recolher trabalho inacabado de outra sessão no seu commit. - Stage SELETIVO de cada arquivo seu, depois verifique:
git diff --cached --name-only— certifique-se de que não há arquivos estranhos (os mais comuns:web/src/pages/ChatPage.tsx,backend/internal/db/dbgen/models.gose não foi sua regeneração). - Arquivos compartilhados entre duas sessões (ex. ExamPage) e você só quer seu hunk: não use
stash/reset(isso tira o arquivo do outro agente). Edite o index do Git via blob:git show :path > tmp→ remova os hunks deles detmp→SHA=$(git hash-object -w tmp)→git update-index --cacheinfo 100644,$SHA,path. O arquivo no disco não é tocado. - 🚨 A ferramenta
cddo Bash pode PERSISTIR de uma chamada anterior (ex. decd .../webou worktree). Entãogit add backend/...FALHA compathspec did not match(ou STAGE a árvore errada). Antes do stage seletivo,cd <repo-root> &&primeiro (ou use caminho absoluto). Verifiquegit diff --cached --name-onlyexibe caminhos relativos à raiz corretos (não../backend,src/...). - Após o deploy: verifique
git status— certifique-se de que os arquivos de trabalho de outra sessão ainda estão intactos.
Portão de qualidade Go (antes do commit backend)
Equivalente ao Pint + PHPStan do Laravel para Go. Execute localmente antes de stagear arquivos backend:
- Formatação:
cd backend && gofmt -l .→ deve estar VAZIO. Se houver saída →gofmt -w .(auto-correção), stage o resultado. (Equivalente apint --test.) - Build + vet:
go build ./... && go vet ./...verde. - Análise estática (target):
golangci-lint run ./...(engloba gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell). Equivalente aphpstan analyse. - ⚠️ Status: portão CI ainda não ativo. Ainda não há
backend/.golangci.ymlnem.github/workflows/ci.yml, estaticcheckestá desatualizado → por enquanto, format+build+vet manual. Plano de enforce no CI + config completa:docs/plan/00-tooling-quality-gate.md. Código geradointernal/db/dbgenexcluído do lint. - Testes acompanham as mudanças → skill afresto-testing (
go test ./...; integração pulada semTEST_DATABASE_URL).
🔴 Regra #00 — trabalho SIGNIFICATIVO vai para uma GitHub issue PRIMEIRO (desde 5 Set 2026)
Válida antes da Regra #0 (branch + PR): antes de tocar em qualquer código.
O objetivo não é formalidade. Pedidos que chegam por conversa são conhecidos apenas por duas pessoas — o time não consegue lê-los, o dono não tem onde atribuir, e o módulo não deixa rastro de quem está fazendo o quê.
Quando é OBRIGATÓRIO criar issue
Nova funcionalidade · mudança que será vista pela escola · alteração no schema do BD · qualquer coisa que exija decisão de produto · trabalho que gere mais de um PR.
Quando NÃO é necessário
Bug claro e pontual · correção de texto/rótulo · documentação · acompanhamento direto de comentário de revisão de PR.
Em dúvida → crie a issue. É barato, e pode ser fechada. Esse limite mantém a regra viva: se toda solicitação trivial virar issue, as pessoas vão começar a ignorá-la silenciosamente, e uma vez ignorada sem consequência, a regra morre.
O que o agente faz
- Explore o código o suficiente para preencher "descobertas iniciais" e encontrar bloqueadores — não chute.
- Crie a issue usando o template
.github/ISSUE_TEMPLATE/pekerjaan.md, coloque o rótulomodul:*(é isso que forma o registro por módulo), maisbutuh-keputusanse houver algo pendente. - PARE. Não code, não crie branch, não se atribua a tarefa. Atribuição é ação do dono; o time também precisa de espaço para dar feedback/revisar ideias.
- Informe o número da issue ao dono, junto com as decisões ainda pendentes (se houver).
🔑 O dono pode ignorar a regra dizendo "faça agora" — a issue ainda é criada como registro, o trabalho segue. Esta regra define o comportamento padrão, não prende ninguém.
Bloqueador descoberto NO MEIO do trabalho
Avise antes de continuar: comente na issue (para o time ver) e comunique ao dono.
Se a decisão interromper o trabalho → adicione o rótulo butuh-keputusan.
Não escolha silenciosamente sua própria interpretação e siga em frente — isso gera trabalho que
"está pronto" mas errado.
Fechando o ciclo
O PR deve obrigatoriamente conter Closes #<número> no corpo. É isso que conecta issue → PR → pessoa,
para que gh issue list --label modul:ujian --state all se torne o registro de quem fez o quê
naquele módulo. Sem essa linha, o rastro se perde.
🔴 Regra #0 — NÃO faça commit/push direto em main (time de 4 pessoas, desde 25 Jul 2026)
Todo trabalho via branch + Pull Request; apenas revisores fazem merge para main (merge = gatilho de deploy).
- Antes de começar:
git checkout main && git pull— se houver novas migrações, execute a migração primeiro. - Crie/checkout uma branch temática ANTES de tocar no código:
- funcionalidade →
feat/<nome>· bug →bugfix/<nome>· bug com issue → nova branch + estude a issue.
- funcionalidade →
- Commit seletivo →
git push -u origin <branch>(NÃO para main). - Abra um PR para
main:gh pr createseghestiver logado; senão, dê ao usuário a URLhttps://github.com/Afresto-Next/next/compare/main...<branch>?expand=1. - Revisor aprova & faz merge → deploy. NÃO faça merge do seu próprio PR.
- A seção "Push backend / Deploy WEB" abaixo = mecânica que ocorre APÓS o merge do PR em
main(geralmente por quem está encarregado do deploy), não é permissão para push direto em main. - Mudanças na skill também passam por PR (CODEOWNERS).
Sequência de etapas
1. Commit (Ferramenta Bash = Git Bash / sh, NÃO PowerShell)
- Mensagem multilinha: heredoc
git commit -q -F - <<'EOF' … EOF. NÃO use@'…'@(isso é here-string do PowerShell → o assunto fica poluído com@ ...). - Finalize a mensagem:
Co-Authored-By: Claude Opus 4.8 <[email protected]>. - Aviso "CRLF will be replaced by LF" = normal, ignore.
- Commit na sua branch de trabalho (
feat/*·bugfix/*), não emmain(veja Regra #0). O usuário commita quando solicitado.
🔴 NÃO use git add -A / git add <dir>/ — já removeu arquivos do usuário 3 vezes
Este repositório armazena muitos arquivos de trabalho do usuário não rastreados: docs/bskap/ (imagens+PDF),
docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (cache de conta). Uma vez com -A,
tudo vai junto.
Casos reais: git add docs/ arrastou PDF/xls/imagens do BSKAP (25 Jul & 2 Ago); git add -A
arrastou 446 arquivos incluindo wrangler-account.json (9 Ago).
git add path/para/arquivo1 path/para/arquivo2 # UM POR UM, sempre
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l # confere com o que você pretendia?
Se a quantidade estiver errada: git reset --soft HEAD~1 && git reset, refaça o stage corretamente.
Se já tiver dado push para sua própria branch, git push --force-with-lease é seguro.
2. Migração do BD — OBRIGATÓRIO estar no commit TOPO
.github/workflows/migrate.yml detecta migrações via git diff --name-only HEAD~1 HEAD (apenas o commit mais recente), executado após "Build & Push Docker Images" com sucesso (workflow_run).
- Se o push contiver migração → a migração deve estar no commit TOPO (um commit com migração+código = seguro).
- A migração é executada automaticamente no VM runner self-hosted após o build.
run --rm migratesincroniza → o job FALHA GRAVEMENTE se houver erro (não silencioso). - Verifique a migração: veja a aba Actions → "Migrate DB" verde para aquele commit (CLI
ghestá disponível:gh run list --workflow="Migrate DB (self-hosted)" --limit 1— o agente pode verificar sozinho, sem pedir ao usuário). Watchtower troca a imagemapina próxima verificação (~5 min), quase sempre após a migração terminar. - ANTES de push de migração, verifique o SQL via psql
BEGIN; … ROLLBACK;no BD local (v102 = produção). Veja a skillafresto-db-change/ armadilha-engenharia §4.
🔴 Número da migração: verifique novamente IMEDIATAMENTE ANTES DO MERGE, não ao criar a branch
Colegas de time podem ter feito merge de outra série de migrações enquanto sua branch estava aberta. Números duplicados fazem
goose ENTRAR EM PÂNICO em sortAndConnectMigrations — não é um erro SQL, então é fácil de interpretar mal.
✅ Sem danos aos dados: o pânico ocorre ao coletar a lista, antes de qualquer uma ser executada.
Caso real (9 Ago 2026): escolheu 00138 ao criar a branch; na hora do merge, o time já estava usando
00138–00140. A primeira correção nomeou como 00140 e ainda colidiu — porque chutou novamente.
Conte, não chute:
git pull # OBRIGATÓRIO primeiro, para ver as migrações dos colegas
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d # DEVE ESTAR VAZIO
Se já tiver acontecido: git mv para o novo número, PR pequeno, merge, e execute novamente o workflow
"Migrate DB (self-hosted)" da aba Actions.
3. Push do backend (APÓS o merge do PR em main — veja Regra #0)
Merge do PR → main atualizado → GHCR → Watchtower ~5 min troca a imagem. Sem alterações no backend = não precisa esperar o Watchtower. (Ao desenvolver: git push -u origin <branch> depois PR — não push em main.)
⚠️ APÓS o merge do backend: VERIFIQUE 502 (já ocorreu 6x)
Recriar o container api/worker frequentemente causa uma paralisação total — todos os containers Up mas
todas as rotas 502. Na prática, todo merge de backend é um jogo de dados.
curl -s -o /dev/null -w "%{http_code}
" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = SAUDÁVEL (pede autenticação) · 502 = paralisação
Se 502 → Actions → "Ops — pulihkan API" → restart-docker (sem SSH, recupera em <1 minuto).
Não analise a funcionalidade recém-implantada: 502 + container Up = reiniciar o daemon Docker.
Detalhes: docs/ops-auto-pulih.md.
4. Deploy do WEB — a partir de WORKTREE LIMPO (por causa de muitas abas de agentes)
npm run build lê TODO o working tree → trabalho inacabado de outra sessão é publicado junto. Portanto, construa e faça deploy a partir de um worktree no seu commit:
rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-do-seu-commit>
cd /c/wtd && git status --short # deve estar VAZIO (limpo)
cd /c/wtd/web && npm ci && npm run build
cd /c/wtd/cloudflare/web-worker && npx wrangler deploy
cd <repo-projectTwo> && git worktree remove --force /c/wtd && git worktree prune
- 🔥 O caminho do worktree DEVE SER CURTO (
/c/wtd) — espaço temporário + nome longo do PDF kaldik DKI → erro no Windows "Filename too long" durante checkout. - Verifique o bundle correto, se necessário:
grep -rl "<marcador da sua funcionalidade>" /c/wtd/web/dist/assets/*.js— mas cuidado com falso positivo (marcadores comuns podem vir de outras funcionalidades). - Cloudflare cacheia assets por ~20 s → teste com Ctrl+F5. (200 mas tamanho ~397 bytes = fallback, não o asset real.)
5. Mobile OTA — repositório separado, --environment OBRIGATÓRIO
cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
- 🔥
--environmentOBRIGATÓRIO (preview/production) — sem ele, a atualização vai para o environment errado / falha. - O repositório mobile tem remote GitHub (
Afresto-Next/afresto-next-mobile) → aplica-se a Regra #0: branch → PR → merge, não faça push direto emmain. - OTA é apenas JS/assets. Mudanças em módulo nativo → CRASH se for via OTA → obrigatório recompilar.
🔥 eas update envia o WORKING TREE, não o commit
Exatamente como npm run build no passo 4 — todo o diretório de trabalho é empacotado,
incluindo trabalho inacabado de outra sessão. O caractere * na linha Commit <hash>* no resultado
da publicação = árvore suja.
OBRIGATÓRIO imediatamente antes de publicar — imprima e depois confira com a linha Commit no resultado:
echo "publicar de: $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main # VAZIO = nenhum trabalho de outra pessoa ficou para trás
- Publique a partir de
main, não de uma branch de funcionalidade. De uma branch, o trabalho de outras pessoas que já foi mergeado DESAPARECE do canal — OTA substitui todo o bundle, não aplica patches. - Produção: isole primeiro as alterações não-commitadas de outra sessão
(
git stash push -- <arquivos>…git stash pop). Produção não pode levar código não revisado.
🚨 A branch pode MUDAR no meio do trabalho (muitas abas de agentes, um clone)
Caso 20 Ago 2026: publicação preview claramente de main @ d0938fa; o comando seguinte
constatou que HEAD já estava em outra branch @ 8126a50 — outra sessão mudou de branch no mesmo clone.
Produção chegou a executar código de 3 PRs a mais, sem nenhum erro.
➡️ Não presuma que a branch continua a mesma entre comandos. Imprima novamente a cada vez (comando acima). ➡️ Prevenção real: OTA a partir de WORKTREE, igual ao deploy web no passo 4 — outra sessão não pode mudar sua branch:
rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-do-seu-commit>
cd /c/wtm && git status --short # deve estar VAZIO
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune
🪤 Caminho do worktree curto (/c/wtm) — mesma razão do passo 4.
🪤 npm ci no worktree leva tempo; para OTA pequeno, pode ficar no clone principal desde que a verificação
de branch+commit acima seja executada e o resultado coincida.
🏢 GHCR & organização GitHub — repositório agora na org Afresto-Next
Os repositórios (next, afresto-next-mobile, afresto-claude-skills) foram movidos de ristology/* → org Afresto-Next (20 Jul 2026). Runbook de transferência completo: docs/runbook-transfer-next-org.md.
- 🚨 Transferir repositório para org = caminho do GHCR muda de dono → Watchtower CONGELA SEM AVISO. CI tagueia imagem como
ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate}— o dono segue o proprietário do repositório. Se
...[truncated for translation]