Skip to content

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

EtapaGreenfieldRescue (Brownfield Onboarding)
PreboardingAlias map + allowlist do ConciergeAlias map + allowlist + acesso ao repo/infra existente
Infra-Ready GateWebhook → tofu apply (provisionamento VPS)Webhook → tofu apply + Discovery audit
GitHub AppWebhook → instalar GitHub App no repoWebhook → instalar GitHub App + branch protection
Allowlist ConciergeWebhook → registrar números/usernamesWebhook → registrar números/usernames
Activation GateNotificação: primeira issue semeadaNotificaçã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.

EventoWebhook → Ação
Preboarding concluídoPOST /provisioning/start → dispara tofu apply
GitHub App form preenchidoPOST /github/install-app → instala GitHub App no repo
Allowlist form submetidoPOST /concierge/register-allowlist → registra no perfil do Client no Concierge
Rescue audit concluídoPOST /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:

DadoUso
Alias map de repositóriosApelidos → repos; o Concierge resolve o campo “onde” nas issues (R8)
Allowlist do ConciergeNúmeros de WhatsApp + usernames Telegram dos usuários nomeados
Escolha de provider VPSHetzner (padrão) ou Vultr-SP p/ BR — decidido 2026-06-08 (Q8)
Credenciais de acesso ao repo/infraRescue 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) e choice (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)

EndpointUso 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); exige API_ACCESS=True no .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çãoMitigaçã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árioWebhook de saída na conclusão do to-do entrega os dados ao backend
Sem ramificação por valor de formn8n como middleware condicional
start_date obrigatóriaMapear para data de kick-off/assinatura
Terminologia “employee/new hire” em labels UI hardcodedAceitar 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 ci dentro do sandbox (após o git clone) consome tempo e rede a cada job. Bakeando node_modules na 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):

Terminal window
mkdir -p .factory
cp image/sandbox.Dockerfile.template .factory/sandbox.Dockerfile

Edite .factory/sandbox.Dockerfile adicionando apenas as camadas necessárias (dependências npm, ferramentas apt-get, serviços). Regras obrigatórias:

  • NÃO faça COPY do código-fonte — o código vem por git clone no 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); USER quebra o checkImageUid do sandcastle.
  • NÃO sobrescreva CMD — o CMD ["sleep","infinity"] é obrigatório para manter o container vivo para docker exec.

(b) Garantir FROM factory-sandbox-exec:phase1

A primeira linha do Dockerfile deve ser exatamente:

FROM factory-sandbox-exec:phase1

Isso 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/*
Terminal window
# Em .factory/check.sh (executado pelo agente dentro do sandbox):
service mariadb start
service redis-server start
npm test

O 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

CampoTipoDefaultDescrição
enabledbooleanfalseLiga/desliga o pipeline de QA para o repo.
frontendGlobsstring[][]Globs que selecionam os arquivos de frontend relevantes para os checks visuais.
stagingUrlstring""URL base do ambiente de staging que o runner de QA vai exercitar.
stagingAuthRefstring""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.
stagingUsersManifeststring""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”).
stagingPasswordRefstring""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 qa recebe qa: { 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 qa malformado (tipos errados em qualquer campo, ou scenariosFormat fora da whitelist) é logado (warn) e o repo cai para qa: { enabled: false }. O carregamento do registry nunca crasha por causa de um bloco qa invá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:

Terminal window
# 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 no repos.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_Manutencao já registrado em /etc/hendrios-factory/repos.json (entrada com level definido — 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 glob frontend/src/**/*.{tsx,jsx,css} usa uma brace list com vírgulas internas. O parser de --frontend-globs foi 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:

Terminal window
# 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.json está 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:

Terminal window
# 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.env está fora do repo e já é gitignored pela VPS. Não copie o conteúdo para nenhum arquivo versionado.

Terminal window
# 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.env
sudo chown root:root /etc/hendrios-factory/secrets.env

O nome da env var tem que bater com qa.stagingAuthRef do 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:

Terminal window
# 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 loadRepoRegistry foi instrumentado com reload por SIGHUP ou POST /api/reload-registry (ver src/receiver/deploy.mjs), prefira essa via. Caso contrário, restart é a forma canônica.

Passo E — Validar end-to-end

  1. Abrir uma issue de QA no repo atractivorbis/doBeco_Controlo_Manutencao com o label qa:visual (ou abrir uma issue normal — a triagem do brain decide se despacha QA com base em qa.enabled).
  2. Nos logs do brain, confirmar que o dispatch foi feito contra https://dobeco.hendriosace.com e que o runner resolveu DOBECO_STAGING_AUTH (procure pela linha estruturada qa-dispatch ou equivalente).
  3. Conferir que nenhum warn de malformed qa block aparece — garantia de que normalizeQa aceitou o bloco.

Rollback

Se a aplicação causar regressão visível (QA dispara em jobs que não deviam, ou staging auth falha):

Terminal window
# 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-brain

Por 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.



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).

Terminal window
# 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_Manutencao

Saí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 foundgh 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):

levelLibera
0 (default, não-registrado)nada — factory ignora o repo na entrada
1triagem (classifica/rotula; NÃO aplica agent-ready)
2nível 1 + execução AFK e PR
3ní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/1386main (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:

  1. O webhook é recebido e enfileirado (server.mjs).
  2. O consumer drena e chama triageIssue (brain.mjs:onTriage).
  3. A triagem é chamada; defaultApplyLabels aplica add: [..., 'cx:trivial', ...] + remove: ['triage'] sem erros.
  4. O comentário de triagem é postado com Tags aplicadas: ....
  5. Se level: 1, a transição vai para triage-only (não agent-ready).
  6. Se level: 2+, a transição vai para agent-ready e 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)


Mudanças no conjunto de labels

Para adicionar, remover ou renomear uma label do conjunto canônico:

  1. Editar deploy/factory-labels.json (adicionar entrada com color + description hex 6-dígitos sem #).
  2. Re-rodar node scripts/onboard-labels.mjs <owner/repo> em cada repo registrado no repos.json — o --force do gh label create atualiza color/description de labels existentes; o view-then-skip no script detecta labels já em conformidade e não desperdiça round-trips.
  3. 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 em decideTransition).
  4. Atualizar 02-pipeline.md §3.1 com a nova entrada na tabela de taxonomia.
  5. 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.