O push para o Gitea criou o run, mas ele ficou em Waiting: a mensagem da instância é "No matching online runner with label: ubuntu-latest". A falha é na atribuição do job, não no enfileiramento — o gatilho funcionou e a branch está contemplada. O endpoint /actions/tasks devolvia lista vazia porque lista tasks aceitas por runner, não runs criados, e foi isso que confundiu a leitura. Com o diagnóstico fechado, duas coisas importam antes de um runner existir. Primeira: este repositório é público e o CI dispara em pull_request. Sem guarda, um fork traria código de terceiro para rodar na máquina do runner, com Docker à disposição. Os três jobs do ci.yml e o runner-check passam a exigir que o PR venha da própria origem; push continua rodando normalmente. Também entram `permissions: contents: read` e `concurrency` por ref, para não acumular fila de runs esperando runner. Segunda: `runner-check` é o primeiro workflow a rodar quando o runner subir. Não faz checkout, não instala dependência, não usa segredo e não toca rede externa. Só confirma a ligação e inventaria node, npm, docker, psql e python3 — o job `services` precisa de psql e a jornada assíncrona precisa de python3, e é melhor descobrir isso num workflow de dez segundos. A documentação registra a evidência (runs 666 em Waiting e 584 cancelado), recomenda act_runner 0.6.1-dind-rootless com escopo de repositório, e explica por que Docker-in-Docker em vez do socket do host: num repositório público, montar o socket entregaria o Docker da máquina ao workflow. Fica registrado também que os ambientes de aprovação manual do deploy são conceito do GitHub e não têm equivalente no Gitea. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adaptador de Planilhas
Módulo do Elo Pro para validar planilhas XLSX/CSV, mapear colunas para um modelo e gerar um arquivo novo. Não há login, senha ou tabela de usuários própria: a identidade é a do ecossistema.
| Ambiente | Host | ADAPTER_ENV |
Autenticação |
|---|---|---|---|
| Desenvolvimento | localhost:3000 |
development |
sessão local assinada, sem senha |
| Homologação | hml-ada.elopro.app |
staging |
Supabase Auth do projeto de homologação |
| Produção | ada.elopro.app |
production |
SSO do Elo Pro |
ADAPTER_ENV é independente de NODE_ENV: homologação roda com NODE_ENV=production (build otimizado) e política própria. Ver ADR 0007.
Desenvolvimento local
npm install
cp .env.example .env.local
No .env.local:
ADAPTER_ENV=development
ADAPTER_DEV_AUTH=1
ADAPTER_DEV_AUTH_SECRET=um-segredo-local
ADAPTER_JOB_MODE=inline
npm run dev
Abra /login e escolha uma identidade de desenvolvimento. Não há senha: apenas assume um vínculo que o seed cria em profiles e workspace_members.
Com Docker, igual ao artefato de homologação
Mesma imagem, dois processos, Postgres e Redis de verdade:
docker compose up --build
docker compose exec web node node_modules/tsx/dist/cli.mjs scripts/migrate.ts
Portas só em 127.0.0.1: web 3000, Postgres 55432, Redis 56379.
Homologação
O procedimento completo — DNS, Nginx, Supabase, banco, bucket, containers, worker, observabilidade e rollback — está em docs/spreadsheet-adapter/20-homologacao-runbook.md. O estado de validação está em 21-checklist-validacao.md.
Resumo:
cp .env.staging.example .env.staging # preencher; não versionar
npm run verify:config # recusa configuração incompleta
npm run check:bucket # o bucket precisa existir e ser privado
npm run migrate:check # mostra o que vai rodar
npm run migrate # aplica (passo com aprovação manual)
npm run seed:staging # liga identidades do Supabase a workspaces
docker compose -f docker-compose.staging.yml --env-file .env.staging up -d --no-deps web worker
ADAPTER_SMOKE_URL=https://hml-ada.elopro.app npm run smoke:staging
npm run flag -- <workspace_id> on # último passo, depois do smoke
ADAPTER_APPLY_IDENTITY_SHIM é obrigatória fora de development: 1 cria o shim local de identidade, 0 usa o schema existente do Elo Pro. Aplicar o shim sobre um schema real é destrutivo em potencial, por isso a escolha nunca é inferida.
Produção
Além das variáveis de homologação, produção exige SENTRY_DSN e ELO_PRO_LOGIN_URL, e recusa ADAPTER_STAGING_AUTH=1. A feature flag spreadsheet_adapter nasce desligada e é ligada por workspace com npm run flag, usando a conexão de serviço, nunca pelo browser.
O processamento usa ADAPTER_JOB_MODE=worker e o processo npm run worker. inline é recusado fora de development.
Produção não é liberada sem smoke real e ensaio de rollback registrados no checklist.
Saúde
| Rota | O que responde |
|---|---|
GET /api/health/live |
processo vivo, sem tocar dependência |
GET /api/health/ready |
banco, migrations pendentes, Redis (PING), bucket, configuração e modo de job |
Configuração inválida encerra o processo com código diferente de zero, em vez de deixar o servidor no ar respondendo erro.
Verificação
npm run lint
npm run typecheck
npm test
npm run build
Suíte que exige Postgres e Redis reais (roda no CI, e localmente com os dois serviços em pé):
ADAPTER_TEST_DATABASE_URL=postgres://... ADAPTER_TEST_REDIS_URL=redis://... npm run test:services
Sem essas variáveis os casos são ignorados, nunca aprovados.
O CI tem três jobs: verify (lint, typecheck, testes, build), services (Postgres e Redis reais: as quatro migrations, shim explícito, FORCE RLS, isolamento) e image (build da imagem Linux, conteúdo, fail-fast dos dois processos, compose completo, jornada assíncrona entre processos e desligamento gracioso do worker).