Automação de Onboarding
Onboarding Automation — ChiefOnboarding
Escopo único: automação técnica do Onboarding via ChiefOnboarding — sequences, webhooks de saída, provisionamento, coleta de dados do Client e integração com n8n. O processo comercial e os prazos (o “o quê”) vivem em business/07-onboarding-playbook; este doc cobre o “como técnico”.
Fonte: pesquisa ChiefOnboarding, 2026-06-07 (document-specialist via web). Glossário: canon/CONTEXT.md.
Papel do ChiefOnboarding na fábrica
O ChiefOnboarding é a camada de orquestração do Onboarding: o Client completa etapas no portal → webhooks de saída acionam o provisionamento automatizado → o dev de ops supervisiona o que resta de manual. É a materialização do princípio “tudo que pode ser automatizado deve ser automatizado” no processo de Onboarding.
Uso desviado: ChiefOnboarding é originalmente uma ferramenta de onboarding de colaboradores; a fábrica a usa para onboarding de Clients (
new hire= Client). As implicações estão mapeadas nos Pontos de ruptura.
Instância dedicada
- Instância separada da instância interna de RH/colaboradores da agência: admins = equipe Hendrios; new hires = Clients.
- Client A não vê Client B — cada um acessa só seu token/portal. Todos os admins internos veem todos os Clients. Aceitável para a fábrica (instância privada).
- Stack: Django 5.2 + Python 3.13 + PostgreSQL + Caddy. Docker Compose (3 serviços); ~300–500 MB em carga leve; reservar 1–2 GB RAM.
- Licença: AGPLv3 — self-host totalmente gratuito. Plano pago €249/mês = suporte por e-mail com SLA 2h (opcional).
- Deploy: na VPS da fábrica (Contabo 02), instância separada da House Stack; cabe junto se houver folga de RAM/disco.
Sequences — checklist versionado do Onboarding
O checklist de Onboarding é materializado como sequences do ChiefOnboarding:
- Disparadas por tempo (X dias antes/após
start_date= data de kick-off/assinatura) ou conclusão de tarefa. - Seções condicionais por porta de entrada (greenfield vs Rescue / Brownfield Onboarding) e por tier (R30: tier maior = staging incluído).
- Cada passo pode ser: to-do do Client (com/sem form), admin to-do interno, mensagem (e-mail), webhook de saída, resource ou badge.
Estrutura de sequence por porta
| Etapa | Greenfield | Rescue (Brownfield Onboarding) |
|---|---|---|
| Preboarding | Alias map + allowlist do Concierge | Alias map + allowlist + acesso ao repo/infra existente |
| Infra-Ready Gate | Webhook → tofu apply (provisionamento VPS) | Webhook → tofu apply + Discovery audit |
| GitHub App | Webhook → instalar GitHub App no repo | Webhook → instalar GitHub App + branch protection |
| Allowlist Concierge | Webhook → registrar números/usernames | Webhook → registrar números/usernames |
| Activation Gate | Notificação: primeira issue semeada | Notificação: Rescue entregue + primeira issue semeada |
Webhooks de saída → provisionamento
Feature-chave do ChiefOnboarding para a fábrica: chamadas HTTP arbitrárias (GET/POST/PUT/DELETE) a qualquer URL, com variáveis do Client ({{email}}, {{responses.0.campo}}); OAuth + headers custom suportados.
| Evento | Webhook → Ação |
|---|---|
| Preboarding concluído | POST /provisioning/start → dispara tofu apply |
| GitHub App form preenchido | POST /github/install-app → instala GitHub App no repo |
| Allowlist form submetido | POST /concierge/register-allowlist → registra no perfil do Client no Concierge |
| Rescue audit concluído | POST /provisioning/rescue-start → abre escopo do orçamento |
Limitação: API do ChiefOnboarding não expõe respostas de formulário (
GET /form-answers/inexistente). Workaround obrigatório: webhook de saída disparado na conclusão do to-do entrega os dados ao backend da fábrica. Config manual por tipo de form.
n8n como cola e ramificação condicional
ChiefOnboarding não suporta ramificação por valor de formulário — só por conclusão de tarefa. A lógica “se AWS → webhook X, se GCP → webhook Y” exige intermediário.
n8n (já na VPS01) é o middleware:
- Recebe webhook de saída do ChiefOnboarding.
- Aplica ramificação condicional (provider escolhido, tier, porta de entrada, flags do Client).
- Dispara o endpoint correto de provisionamento, GitHub App ou Concierge.
ChiefOnboarding (webhook de saída) → n8n (ramificação condicional) → endpoint de provisionamento tofu → endpoint GitHub App → endpoint Concierge (allowlist)Portal e preboarding — coleta de dados do Client
Preboarding (antes do kick-off)
Link único por token enviado ao Client antes da primeira reunião. Coleta:
| Dado | Uso |
|---|---|
| Alias map de repositórios | Apelidos → repos; o Concierge resolve o campo “onde” nas issues (R8) |
| Allowlist do Concierge | Números de WhatsApp + usernames Telegram dos usuários nomeados |
| Escolha de provider VPS | Hetzner (padrão) ou Vultr-SP p/ BR — decidido 2026-06-08 (Q8) |
| Credenciais de acesso ao repo/infra | Rescue apenas; guardadas no Vaultwarden |
Portal web (pós-kick-off)
- Login por e-mail/senha ou SSO (Google/OIDC).
- Terminologia visível customizável por template de mensagem e preboarding.
- Labels do painel admin (hardcoded: “employee”, “new hire”, “buddy”, “manager”) não têm i18n completo — limitação conhecida; aceitável pois só a equipe Hendrios vê o painel admin.
Forms via webhook
- Campos suportados:
input(texto livre) echoice(opções fixas ou de URL externa). - Encadeamento:
{{responses.0.campo}}nas URLs/headers dos webhooks seguintes. - Entrega: ao completar o to-do com form, ChiefOnboarding dispara o webhook de saída configurado com os dados.
- Config manual por tipo de form — cada campo a ser capturado precisa de uma integração configurada na sequence.
API REST (uso interno)
| Endpoint | Uso na fábrica |
|---|---|
POST /api/users/ | Cria o Client (new hire) e dispara sequences automaticamente |
GET /api/sequences/ | Verificar status do Onboarding de um Client |
POST /api/offboarding/ | Dispara sequência de Offboarding no cancelamento |
- Token de API sem expiração (gerado via
manage.py); exigeAPI_ACCESS=Trueno.env. - Token guardado no Vaultwarden (platform/04-secrets-security).
- Chamadas autenticadas pelo backend da fábrica (Brain ou n8n).
Pontos de ruptura (uso desviado)
| Limitação | Mitigação |
|---|---|
| Não é multi-tenant (1 instância = 1 org) | Instância dedicada “clientes”; Clients não se vêem entre si |
| API não expõe respostas de formulário | Webhook de saída na conclusão do to-do entrega os dados ao backend |
| Sem ramificação por valor de form | n8n como middleware condicional |
start_date obrigatória | Mapear para data de kick-off/assinatura |
| Terminologia “employee/new hire” em labels UI hardcoded | Aceitar no painel admin (só equipe interna); reescrever textos de mensagem/preboarding |
Imagem de sandbox por-repo (opt-in)
Por padrão, todos os repos usam a imagem genérica factory-sandbox-exec:phase1. Se o repo precisar de dependências pré-instaladas (ex.: npm ci + Prisma) ou de serviços embarcados (ex.: MariaDB, Redis), o Client pode fornecer um Dockerfile próprio que a fábrica builda localmente.
Por que pré-instalar dependências? O
npm cidentro do sandbox (após o git clone) consome tempo e rede a cada job. Bakeandonode_modulesna imagem o primeiro build paga o custo uma vez; jobs subsequentes reusam o cache até o lockfile mudar.
Passo a passo
(a) Copiar o template e adicionar as dependências/serviços do repo
No repo do Client, copie o template disponível em image/sandbox.Dockerfile.template (no repo da fábrica):
mkdir -p .factorycp image/sandbox.Dockerfile.template .factory/sandbox.DockerfileEdite .factory/sandbox.Dockerfile adicionando apenas as camadas necessárias (dependências npm, ferramentas apt-get, serviços). Regras obrigatórias:
- NÃO faça
COPYdo código-fonte — o código vem porgit cloneno início de cada job (sandcastle 0.7.0). Copiar o código na imagem seria ignorado/sobrescrito e causaria confusão. - NÃO declare
USER— o runner opera como root (uid 0);USERquebra ocheckImageUiddo sandcastle. - NÃO sobrescreva
CMD— oCMD ["sleep","infinity"]é obrigatório para manter o container vivo paradocker exec.
(b) Garantir FROM factory-sandbox-exec:phase1
A primeira linha do Dockerfile deve ser exatamente:
FROM factory-sandbox-exec:phase1Isso herda o contrato completo: OpenCode CLI, opencode.json, identidade git, WORKDIR /home/agent/workspace, sem USER, CMD sleep infinity. Não troque a base — imagens externas quebram o contrato.
(c) Registrar sandboxBuild no deploy-map.json na box
Na Contabo 02, edite /app/receiver/deploy-map.json (ou o caminho configurado em DEPLOY_MAP_PATH) e adicione o campo sandboxBuild na entrada do repo:
{ "owner/repo": { "endpoint": "...", "triggerWorkflow": "...", "containers": [...], "sandboxBuild": { "dockerfile": ".factory/sandbox.Dockerfile" } }}O caminho em dockerfile é relativo à raiz do repo clonado. A imagem resultante recebe a tag local <repo-sanitizado>-sandbox:<hash-do-dockerfile+lockfile>. Repos sem o campo sandboxBuild continuam usando factory-sandbox-exec:phase1 — zero regressão.
(d) Opcional: strict: true para falha dura
Por padrão (strict: false), se o build da imagem falhar a fábrica loga um aviso, emite uma métrica e degrada para a imagem genérica — o job continua. Com strict: true, uma falha de build torna o job inválido (falha dura), útil quando a imagem customizada é pré-requisito funcional (ex.: o repo não instala deps em runtime):
"sandboxBuild": { "dockerfile": ".factory/sandbox.Dockerfile", "strict": true}Serviços single-container
O sandcastle 0.7.0 é single-container — não há sidecars nem compose. Se o repo precisa de banco/cache para os checks, instale o pacote apt no Dockerfile e suba o serviço em runtime (no script de check do repo), não no CMD:
# No .factory/sandbox.Dockerfile:RUN apt-get update && apt-get install -y --no-install-recommends \ mariadb-server redis-server \ && rm -rf /var/lib/apt/lists/*# Em .factory/check.sh (executado pelo agente dentro do sandbox):service mariadb startservice redis-server startnpm testO CMD ["sleep","infinity"] permanece inalterado — o sandcastle precisa do container vivo para injetar o prompt via docker exec.
QA configuration
Cada entrada do repos.json pode carregar um bloco opcional qa que habilita o pipeline de QA por-cliente (checks visuais contra um staging, cenários em Gherkin, etc.). O bloco é NULL-SAFE: um repo sem qa é tratado como qa: { enabled: false } — os repos existentes continuam carregando e despachando agentes dev sem nenhuma mudança.
O parser vive em src/receiver/deploy.mjs (normalizeQa / loadRepoRegistry) e é consumido pela triagem/roteamento. O schema é:
{ "owner/repo": { "level": 2, // ... demais campos de deploy (endpoint, containers, ...) ...
"qa": { "enabled": true, // bool — master switch (false = pipeline de QA pulado) "frontendGlobs": [ // string[] — globs que selecionam arquivos de frontend p/ checks visuais "src/**/*.tsx", "web/**/*.vue" ], "stagingUrl": "https://staging.cliente.test", // string — URL base do staging sob teste // stagingAuthRef (Wave 1) — DEPRECADO p/ multi-role (staging-users S4), // mas mantido por retrocompat. Nome da env var da credencial única de staging. "stagingAuthRef": "CLIENTE_STAGING_TOKEN", // staging-users S4 (multi-role) — path do manifesto de usuários + nome da // env var com a senha compartilhada. Substitui stagingAuthRef para cenários // com múltiplos papéis (staging-users S6: admin/cfo/gerente/staff/supervisor). "stagingUsersManifest": "qa/staging-users.json", "stagingPasswordRef": "DOBECO_QA_PASSWORD", "scenariosFormat": "gherkin-comment" // whitelist: 'gherkin-comment' (formato autor de cenários) } }}Semântica dos campos
| Campo | Tipo | Default | Descrição |
|---|---|---|---|
enabled | boolean | false | Liga/desliga o pipeline de QA para o repo. |
frontendGlobs | string[] | [] | Globs que selecionam os arquivos de frontend relevantes para os checks visuais. |
stagingUrl | string | "" | URL base do ambiente de staging que o runner de QA vai exercitar. |
stagingAuthRef | string | "" | DEPRECADO (Wave 1, single-secret). Nome da env var (ex.: CLIENTE_STAGING_TOKEN) que carrega a credencial única de staging. Referência resolvida em runtime — o valor do segredo nunca entra no repos.json. Mantido por retrocompat; repos novos em multi-role devem usar stagingUsersManifest + stagingPasswordRef. |
stagingUsersManifest | string | "" | staging-users S4 (multi-role) + S6 (role-aware Gherkin). Path (relativo à raiz do repo) do manifesto JSON que mapeia papel (role) → usuário (email + stagingPasswordRef). Ex.: "qa/staging-users.json". Substitui stagingAuthRef para cenários Gherkin com múltiplos papéis. As roles válidas no manifesto são as 5 canônicas do S6: admin, cfo, gerente, staff, supervisor (ver image/skills/scenario-author/SKILL.md § “Role-aware Gherkin”). Ausência = null-safe (default "", equivalente a “não configurado”). |
stagingPasswordRef | string | "" | staging-users S4 (multi-role). Nome da env var que carrega a senha compartilhada referenciada por cada usuário no manifesto. Ex.: "DOBECO_QA_PASSWORD". Referência resolvida em runtime — o valor do segredo nunca entra no repos.json. Típico: mesmo valor de stagingAuthRef quando o repo já estava no modelo Wave 1. |
scenariosFormat | 'gherkin-comment' | 'gherkin-comment' | Formato autor dos cenários. Lista fechada (gherkin-comment); valores não reconhecidos são tratados como misconfig. |
NULL-SAFE e FAIL-SAFE
- NULL-SAFE (happy path): um repo sem bloco
qarecebeqa: { enabled: false }ao carregar o registry. Repos existentes não precisam de migração para continuar funcionando — a triagem e o dispatch de agentes dev seguem idênticos. - FAIL-SAFE: um bloco
qamalformado (tipos errados em qualquer campo, ouscenariosFormatfora da whitelist) é logado (warn) e o repo cai paraqa: { enabled: false }. O carregamento do registry nunca crasha por causa de um blocoqainválido — o operador vê o warn e corrige a entrada.
Migração / materialização do bloco
Embora o bloco seja opcional, um operador pode materializá-lo explicitamente (com defaults) para edição in-place usando scripts/qa-config-add.mjs:
# Pré-visualiza o patch JSON sem escrever (recomendado antes de aplicar):node scripts/qa-config-add.mjs atractivorbis/doBeco_Controlo_Manutencao --dry-run
# Aplica no registry de produção (aponta --registry p/ /etc/hendrios-factory/repos.json na box,# ou exporta REPOS_REGISTRY_PATH):node scripts/qa-config-add.mjs atractivorbis/doBeco_Controlo_Manutencao \ --enabled \ --staging-url https://staging.test \ --staging-auth-ref DOBECO_STAGING_TOKEN \ --frontend-globs "src/**/*.tsx,web/**/*.vue"O script é idempotente: re-executar sobrescreve o bloco qa existente com o novo (preservando os demais campos da entrada). A flag --dry-run imprime o patch JSON em stdout e não toca no arquivo.
Segurança:
stagingAuthRefé o nome de uma env var resolvida em runtime pelo runner de QA. Nunca cole o valor do token/credencial norepos.json— apenas o nome da variável (ex.:DOBECO_STAGING_TOKEN). Os segredos ficam em/etc/hendrios-factory/na VPS (fora do repo; ver 04-secrets-security).
Runbook do operador — onboarding QA do repo atractivorbis/doBeco_Controlo_Manutencao
Este runbook é a sequência manual que o operador executa na VPS para ligar o pipeline de QA no repo doBeco. O agente não tem acesso ao VPS — ele só entrega o dry-run correto + este runbook; a aplicação real é responsabilidade do operador.
Pré-condições
- Acesso SSH à VPS da fábrica (Contabo 02) com permissões para editar
/etc/hendrios-factory/. - Repo
atractivorbis/doBeco_Controlo_Manutencaojá registrado em/etc/hendrios-factory/repos.json(entrada comleveldefinido — Passo 2 do Onboarding técnico de repo). - Vaultwarden contém a credencial de staging (token/cookie/etc.) que será referenciada por
DOBECO_STAGING_AUTH.
Bloco qa alvo
O patch que o script deve imprimir em --dry-run é exatamente:
{ "qa": { "enabled": true, "frontendGlobs": ["frontend/src/**/*.{tsx,jsx,css}", "index.html"], "stagingUrl": "https://dobeco.hendriosace.com", "stagingAuthRef": "DOBECO_STAGING_AUTH", "scenariosFormat": "gherkin-comment" }}Atenção com
frontendGlobs: o globfrontend/src/**/*.{tsx,jsx,css}usa uma brace list com vírgulas internas. O parser de--frontend-globsfoi tornado ciente de brace lists (não divide vírgulas dentro de{...}); passe o argumento como uma única string, com a vírgula entre os dois globs fora das chaves. Detalhes em scripts/qa-config-add.mjs.
Passo A — Pré-visualizar o patch (na estação do dev, ou em VPS via checkout limpo do repo)
Da raiz do repo da fábrica, sem apontar para o registry de produção:
# Saída esperada: o patch JSON acima.node scripts/qa-config-add.mjs \ atractivorbis/doBeco_Controlo_Manutencao \ --dry-run \ --enabled \ --staging-url https://dobeco.hendriosace.com \ --staging-auth-ref DOBECO_STAGING_AUTH \ --frontend-globs 'frontend/src/**/*.{tsx,jsx,css},index.html'Confirme manualmente que o JSON contém qa.enabled: true, qa.frontendGlobs com exatamente ["frontend/src/**/*.{tsx,jsx,css}", "index.html"], e qa.stagingAuthRef: "DOBECO_STAGING_AUTH" (sem o valor da credencial — só o nome da env var). Se não bater, não prossiga — investigue antes de aplicar.
Passo B — Aplicar o patch em /etc/hendrios-factory/repos.json (VPS, fronteira de segredo)
Fronteira de segredo:
/etc/hendrios-factory/repos.jsonestá no mesmo bind-mount que o container do brain usa em/app/receiver/repos.json. Editá-lo na VPS muda a configuração vista pelo brain. Faça backup antes:
# 1. Backup do registry atual (VPS):sudo cp /etc/hendrios-factory/repos.json /etc/hendrios-factory/repos.json.bak.$(date +%Y%m%d-%H%M%S)
# 2. Aplicar o patch (o script edita o arquivo in-place; --registry aponta# para o caminho de produção):sudo REPOS_REGISTRY_PATH=/etc/hendrios-factory/repos.json \ node scripts/qa-config-add.mjs \ atractivorbis/doBeco_Controlo_Manutencao \ --enabled \ --staging-url https://dobeco.hendriosace.com \ --staging-auth-ref DOBECO_STAGING_AUTH \ --frontend-globs 'frontend/src/**/*.{tsx,jsx,css},index.html'
# 3. Validar que o arquivo continua sendo JSON válido e contém o bloco:sudo python3 -c 'import json,sys; r=json.load(open("/etc/hendrios-factory/repos.json")); \ print(json.dumps(r["atractivorbis/doBeco_Controlo_Manutencao"]["qa"], indent=2))'# Esperado: o mesmo bloco do Passo A.Não cole o valor do token de staging no repos.json. O campo stagingAuthRef é uma referência — apenas o nome DOBECO_STAGING_AUTH é persistido no registry; o segredo vai no secrets.env (Passo C).
Passo C — Adicionar a credencial em /etc/hendrios-factory/secrets.env (NÃO commitar)
NUNCA commitar segredos —
/etc/hendrios-factory/secrets.envestá fora do repo e já é gitignored pela VPS. Não copie o conteúdo para nenhum arquivo versionado.
# 1. Editar o arquivo de segredos do brain (VPS):sudo nano /etc/hendrios-factory/secrets.env
# 2. Adicionar a linha com a credencial — pegar o valor do Vaultwarden:DOBECO_STAGING_AUTH=<colar-aqui-o-token-ou-cookie-de-staging>
# 3. Garantir permissões mínimas (somente root lê):sudo chmod 600 /etc/hendrios-factory/secrets.envsudo chown root:root /etc/hendrios-factory/secrets.envO nome da env var tem que bater com
qa.stagingAuthRefdo Passo B (case-sensitive). Se não bater, o runner de QA não resolve a credencial em runtime e o staging fica sem auth — falha visível no primeiro job de QA.
Passo D — Reiniciar o brain para recarregar o cache do registry
O brain lê repos.json no boot; mudanças no arquivo não pegam sem restart. Use o caminho de deploy padrão da VPS (deploy-gate + manual-deploy-cli) ou, para mudanças só de config sem rebuild de imagem, um restart do container:
# Restart do container do brain — recarrega o registry do bind-mount:sudo docker compose -f /etc/hendrios-factory/docker-compose.yml restart factory-brain
# Verificar que subiu limpo (sem warn sobre qa malformado):sudo docker logs factory-brain --since 2m | grep -E "(loadRepoRegistry|qa)" || echo "no qa log lines — OK"Alternativa menos invasiva (sem restart): se o
loadRepoRegistryfoi instrumentado com reload por SIGHUP ouPOST /api/reload-registry(versrc/receiver/deploy.mjs), prefira essa via. Caso contrário, restart é a forma canônica.
Passo E — Validar end-to-end
- Abrir uma issue de QA no repo
atractivorbis/doBeco_Controlo_Manutencaocom o labelqa:visual(ou abrir uma issue normal — a triagem do brain decide se despacha QA com base emqa.enabled). - Nos logs do brain, confirmar que o dispatch foi feito contra
https://dobeco.hendriosace.come que o runner resolveuDOBECO_STAGING_AUTH(procure pela linha estruturadaqa-dispatchou equivalente). - Conferir que nenhum warn de
malformed qa blockaparece — garantia de quenormalizeQaaceitou o bloco.
Rollback
Se a aplicação causar regressão visível (QA dispara em jobs que não deviam, ou staging auth falha):
# 1. Restaurar o registry:sudo cp /etc/hendrios-factory/repos.json.bak.<timestamp> /etc/hendrios-factory/repos.json
# 2. (Opcional) Remover a linha de segredos — preferir comentar (#) em vez de# deletar, para histórico:sudo sed -i 's/^DOBECO_STAGING_AUTH=/#DOBECO_STAGING_AUTH=/' /etc/hendrios-factory/secrets.env
# 3. Restart do brain:sudo docker compose -f /etc/hendrios-factory/docker-compose.yml restart factory-brainPor que este runbook é separado do dry-run: o dry-run (Passo A) é executável de qualquer máquina com checkout do repo, sem acesso à VPS. Os Passos B–E exigem privilégios na VPS e acesso ao Vaultwarden. Por design, o agente entrega A; o operador entrega B–E — mantendo a fronteira de segredo explícita.
Links relacionados
- canon/CONTEXT.md — Onboarding, Activation, Infra-Ready Gate, Activation Gate, Seed Issue, Brownfield Onboarding, Rescue
- platform/04-secrets-security — Vaultwarden (token da API, credenciais Rescue)
- platform/08-deploy-client-vps — House Stack, provisionamento tofu, Rescue (10 itens)
- platform/09-concierge — allowlist e alias map coletados no preboarding
- business/07-onboarding-playbook — processo white-glove, Infra-Ready Gate, Activation Gate, prazos por porta (o “o quê” comercial)
Onboarding técnico de repo (passos de plataforma)
Esta subseção documenta os passos técnicos para vincular um repo novo à fábrica — executados uma vez por repo durante o onboarding (entre o passo “GitHub App instalado” e o “Activation Gate”). É a parte mecânica do onboarding — corre em sequência, com scripts versionados, sem dependência de conhecimento tribal. O fluxo comercial (prazos, aprovação do cliente, kick-off) vive em business/07-onboarding-playbook.
Pré-condição: o GitHub App hendrios-factory (App ID 4000771) já está instalado no repo de destino (passo automático via ChiefOnboarding POST /github/install-app; ver Webhooks de saída).
Passo 1 — Provisionar o conjunto canônico de labels
A fábrica usa um conjunto estável de labels para a state machine de triage (→ 02-pipeline.md §3.1). A taxonomia é versionada em deploy/factory-labels.json — fonte-da-verdade única (não use gh label clone a partir do tracer; o tracer é consumidor, não fonte). O provisionamento é idempotente (usa gh label create --force por baixo, e detecta labels já existentes via gh label view para não gastar round-trips).
# Da raiz do repo da fábrica (com GH_TOKEN de instalação no env, ou `gh auth` ativo):node scripts/onboard-labels.mjs <owner/repo># Exemplo (repo doBeco — issue #55 do 2026-06-22):node scripts/onboard-labels.mjs atractivorbis/doBeco_Controlo_ManutencaoSaída (resumo): um JSON por label aplicado (action: created|updated|unchanged) + linha final de tally. Exit 0 se tudo OK, 1 se houve falha parcial (a label com erro é logada + segue; relatório failed lista nome + stderr).
Por que este passo é necessário (e não opcional): sem o conjunto de labels, a primeira triagem da primeira issue do repo cai com failed to update ...: 'triage' not found — gh issue edit falha o comando inteiro se qualquer --add-label ou --remove-label referenciar uma label que não existe. Era exatamente o sintoma do doBeco #55 antes do fix.
Defesa em profundidade (independente do passo 1): defaultApplyLabels em src/receiver/triage.mjs agora é tolerante a labels faltantes — se o bulk gh issue edit falhar com “not found”, o wrapper re-executa as operações em granularidade por-label. Para cada label a adicionar, executa primeiro gh label create <name> --force -R <repo> (cria ou atualiza idempotentemente — este é o comando que suporta --force, não gh issue edit) e depois gh issue edit <n> -R <repo> --add-label <name>. Remoções com “not found” são toleradas em silêncio. Assim, mesmo que o passo 1 tenha sido pulado, a triagem nunca quebra — ela auto-cria os labels que faltam. O comentário de triagem lista os labels aplicados, então o operador vê o que pegou e o que não. (Testes em src/receiver/triage.test.mjs cobrem os dois caminhos: happy path e fallback “not found”.)
Passo 2 — Adicionar entry no repos.json
Editar /etc/hendrios-factory/repos.json na box (bind-mount em /app/receiver/repos.json no container; ver 13-self-improvement.md §4) e adicionar a entrada do novo repo. Exemplo de shape (também em deploy/repos.example.json):
{ "atractivorbis/doBeco_Controlo_Manutencao": { "level": 2, "endpoint": 1, "triggerWorkflow": "release", "containers": ["<container-do-app>"], "baseBranch": "master" }}Níveis (zero-downtime — ver 13-self-improvement.md §4):
level | Libera |
|---|---|
0 (default, não-registrado) | nada — factory ignora o repo na entrada |
1 | triagem (classifica/rotula; NÃO aplica agent-ready) |
2 | nível 1 + execução AFK e PR |
3 | nível 2 + deploy (sub-objeto endpoint/triggerWorkflow/containers passa a ser consultado) |
Recomendação de ramp-up: começar em level: 1 para validar a triagem numa issue real (verifica que os labels foram criados no passo 1, que o triage comment é postado, que nenhum dispatch acontece). Subir para 2 quando a triagem estiver limpa. Subir para 3 quando o deploy já foi validado no whatsapp-tracer e o sub-objeto de deploy está preenchido.
baseBranch — branch base das PRs da fábrica (issue #1386)
O campo baseBranch define contra qual branch a fábrica abre as PRs factory/issue-N. Ausente →
main (default, backward-compat). Valores inválidos (ref git malformada) → warn + fallback main
(validação em src/receiver/brain-probes.mjs:resolveBaseBranch / isValidGitRefName).
Repos com feature/epic branches: quando o cliente roda um workflow de epic (branch intermediária,
ex.: epic/1386, que agrega as sub-issues e só depois é mergeada na main), declare a branch
intermediária:
{ "owner/repo": { "level": 2, "baseBranch": "epic/1386", "priority": "P1" }}Por que o deploy de produção fica protegido por construção: o deploy-gate só dispara no workflow
triggerWorkflow (default release) bem-sucedido em main — guard isReleaseDeployTrigger
(src/receiver/deploy.mjs, usado em onDeploy/onSelfDeploy em src/receiver/brain.mjs). O merge
de cada sub-issue vai para a epic branch, então o workflow_run carrega head_branch = epic/1386
e nunca satisfaz o guard — a fábrica não entrega produção prematuramente de uma sub-issue.
Apenas o merge final epic/1386 → main (cujo release roda em main) dispara o deploy.
Nenhuma flag nova é necessária (ver 18-new-repo-pre-connect-checklist.md §4.1.1 e deploy/repos.example.json _comment5).
Passo 3 — Validar a primeira triagem
Abrir uma issue de seed com triage aplicado (manualmente ou via pre-semeio) e verificar nos logs do brain:
- O webhook é recebido e enfileirado (
server.mjs). - O consumer drena e chama
triageIssue(brain.mjs:onTriage). - A triagem é chamada;
defaultApplyLabelsaplicaadd: [..., 'cx:trivial', ...]+remove: ['triage']sem erros. - O comentário de triagem é postado com
Tags aplicadas: .... - Se
level: 1, a transição vai paratriage-only(nãoagent-ready). - Se
level: 2+, a transição vai paraagent-readye o dispatch é disparado.
Se algum dos passos falhar, ver doBeco #55 (2026-06-22) — workaround que motivou este doc: foi a falta do passo 1 que causou o not found.
Próximos passos (fora deste doc)
- Provisionar VPS + House Stack (→ 08-deploy-client-vps.md).
- Permitlist do Concierge (→ 09-concierge.md).
- Alias map de repos (este doc §Portal e preboarding — coleta de dados do Client).
Mudanças no conjunto de labels
Para adicionar, remover ou renomear uma label do conjunto canônico:
- Editar
deploy/factory-labels.json(adicionar entrada comcolor+descriptionhex 6-dígitos sem#). - Re-rodar
node scripts/onboard-labels.mjs <owner/repo>em cada repo registrado norepos.json— o--forcedogh label createatualizacolor/descriptionde labels existentes; oview-then-skipno script detecta labels já em conformidade e não desperdiça round-trips. - Se a label tem semântica nova no state machine (ex.: novo estado terminal), atualizar também
src/receiver/triage.mjs(ALLOWED_STATE_LABELS,TERMINAL_LABELS, branches emdecideTransition). - Atualizar 02-pipeline.md §3.1 com a nova entrada na tabela de taxonomia.
- Atualizar
src/harness/gabarito.mjs(gabarito de acurácia de triage) se a label aparece em algum caso de teste do AC2.
A label “fonte da verdade” é o JSON. O create-labels.sh legado virou wrapper compat em scripts/onboard-labels.mjs; o shell script delega ao Node para evitar drift entre as duas listas.