🧭 Conceito: setup como state machine
Instalador comum trata o setup como script linear: roda do início ao fim, e se quebrar no meio o user fica órfão. O install gate trata como state machine: cada execução pergunta "em que estado eu estou?", decide o próximo passo, executa, salva o novo estado. Re-rodar é seguro. Parar no meio é seguro. Voltar amanhã e continuar é a operação padrão.
🔀 O ciclo de cada execução
┌──────────────────────────────────────────────────────────────┐
│ install.sh inicia │
└──────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Lê _install-state.json │
│ (cria se não existir) │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ phase = ? │
└─────────────────────────────┘
│
┌─────────────┬───────┴───────┬────────────────┐
▼ ▼ ▼ ▼
[fresh] [brand-ctx] [skills-install] [welcome]
│ │ │ │
▼ ▼ ▼ ▼
pergunta coleta dados instala skills quick-win
básicos de marca do catálogo opcional
│ │ │ │
└─────────────┴───────┬───────┴────────────────┘
▼
┌─────────────────────────────┐
│ Marca passo como done │
│ Salva _install-state.json │
│ Imprime próximo passo │
└─────────────────────────────┘
│
▼
[exit 0]
Rodar 100x = rodar 1x.
Parou? Continua de onde parou.
O JSON conta a história.
Migra entre versões do OS.
A virada conceitual: o instalador não é dono do tempo do user. Ele oferece passos, registra o que foi feito, sai limpo. O user volta quando quiser e o gate sabe exatamente onde retomar.
📦 _install-state.json: o schema completo
Um único arquivo na raiz do projeto carrega o estado. Pequeno, legível, versionado em git
(ou em .gitignore se for por-máquina). É o contrato entre
o instalador, os hooks e o próprio user.
_install-state.json (real, após 2ª fase)
{
"version": "1.2.0",
"schema": "iamasters-os/install-state/v1",
"phase": "skills-install",
"fresh": false,
"started_at": "2026-05-18T20:11:43-03:00",
"updated_at": "2026-05-18T20:38:09-03:00",
"completed_steps": [
{
"id": "brand-context",
"blocking": true,
"completed_at": "2026-05-18T20:14:22-03:00",
"artifact": "context/brand.md"
},
{
"id": "claude-md-bootstrap",
"blocking": true,
"completed_at": "2026-05-18T20:16:01-03:00",
"artifact": "CLAUDE.md"
}
],
"pending_steps": [
{ "id": "skills-install", "blocking": true, "estimated_minutes": 5 },
{ "id": "hooks-install", "blocking": true, "estimated_minutes": 3 },
{ "id": "welcome-quick-win", "blocking": false, "estimated_minutes": 4 }
],
"skipped_steps": [],
"machine": {
"os": "linux",
"shell": "bash",
"claude_code_version": "2.5.1"
}
}
📥 Campos obrigatórios
- version — semver do OS instalado.
- schema — versão do próprio schema.
- phase — estado atual da máquina.
- fresh — true só na 1ª execução.
- completed_steps[] — auditoria.
- pending_steps[] — roadmap restante.
📤 Campos opcionais úteis
- skipped_steps[] — auditar o que foi pulado conscientemente.
- machine.* — diagnóstico em suporte.
- artifact — caminho do arquivo que o passo produziu.
- estimated_minutes — expectativa pro user.
_install-state.template.json (canônico, no repo)
{
"version": "1.2.0",
"schema": "iamasters-os/install-state/v1",
"phase": "brand-context",
"fresh": true,
"completed_steps": [],
"pending_steps": [
{ "id": "brand-context", "blocking": true, "estimated_minutes": 6 },
{ "id": "claude-md-bootstrap", "blocking": true, "estimated_minutes": 2 },
{ "id": "skills-install", "blocking": true, "estimated_minutes": 5 },
{ "id": "hooks-install", "blocking": true, "estimated_minutes": 3 },
{ "id": "welcome-quick-win", "blocking": false, "estimated_minutes": 4 },
{ "id": "mcp-extras", "blocking": false, "estimated_minutes": 5 }
],
"skipped_steps": []
}
💡 Tip — o template é um contrato versionado
Mantenha o _install-state.template.json versionado em git.
Toda mudança é PR. Quando o user faz upgrade da versão do OS, o instalador compara
o state dele com o template novo e infere automaticamente os passos
faltantes. Migração vira diff de JSON, não cirurgia.
🚦 Fases bloqueantes vs opcionais
Nem todo passo é igual. Alguns precisam acontecer antes do OS sequer funcionar (bloqueantes). Outros melhoram a experiência mas o sistema roda sem (opcionais). O install gate distingue os dois explicitamente — porque misturar gera o pior dos dois mundos: ou o user é forçado a coisas que não precisa, ou pula coisas que vão quebrar depois.
🔒 Bloqueantes — não pula
- brand-context — sem isso o agente não sabe quem ele representa. Pergunta nome, tom, público, do/dont's. Vira
context/brand.md. - claude-md-bootstrap — gera o
CLAUDE.mdraiz. Sem isso, nenhum hook ou skill carrega contexto certo. - skills-install — instala o set mínimo da trilha escolhida. Sem skills, é só LLM solto.
- hooks-install — registra SessionStart e PostToolUse. Sem isso, o greeting nunca aparece.
🎁 Opcionais — pode pular
- welcome-quick-win — gera 1 demo concreta em
projects/welcome/. Pular não quebra nada, mas perde a "primeira vitória". - mcp-extras — instala MCPs adicionais (Gmail, Drive). Pode ser feito depois pelo catálogo.
- analytics-opt-in — telemetria local. Default é off, pergunta uma vez.
- seed-templates — copia templates de PRD/ADR. Útil mas trivial de adicionar depois.
📊 Por que separar importa
Dados de campo: em onboardings sem gate explícito, ~90% dos users abandonam entre os passos 3 e 5 — exatamente quando o instalador linear força tarefas opcionais misturadas com bloqueantes. O user perde paciência num passo que ele "achou que era opcional" e nunca volta.
Com gate separando os dois, a taxa de conclusão dos passos bloqueantes sobe pra ~85%, e os opcionais ficam disponíveis pro user pegar quando quiser, sem culpa.
brand, claude.md, skills, hooks.
welcome, mcp-extras, analytics.
[obrigatório] vs [opcional] no prompt.
--skip-optional flag.
🎉 Welcome quick-win: a 1ª vitória em <5min
Logo após os passos bloqueantes, o gate oferece uma demonstração concreta que prova o OS funciona pro caso real do user. Não é tutorial. Não é vídeo. É um artefato gerado com os dados que ele acabou de fornecer no brand-context, em <5min.
Estrutura de projects/welcome/ após o quick-win
projects/welcome/
├── README.md # "Sua primeira vitória — feita pelo OS"
├── brand-applied-demo.html # landing com cores/tom do brand-context
├── prompt-usado.md # transparência: o prompt que gerou o artefato
└── proximos-passos.md # 3 sugestões de próximos projetos
✓ Quick-win bom
- Usa dados reais do brand-context.
- Gera artefato visível (HTML, PDF, doc).
- Tem "abra esse arquivo" no final.
- Roda em <5 minutos.
- Mostra o prompt que produziu (transparência).
✗ Quick-win ruim
- "Hello world" com dados fake.
- Só texto no terminal, sem artefato.
- Termina com "agora explore!" (e nada mais).
- Demora >10 min (vira outro install).
- Esconde como foi feito (mágica = desconfiança).
O quick-win não é marketing — é o teste de fumaça do próprio OS. Se ele falha, o user sabe imediatamente que algo está errado antes de tentar usar pra valer. Se passa, o user já tem 1 caso concreto pra mostrar pro chefe/cliente na próxima reunião.
♻️ Re-run idempotente: 100x = 1x
Idempotência é a regra de ouro do install gate: rodar ./install.sh
cem vezes seguidas deve produzir o mesmo resultado de rodar uma vez. Nenhum arquivo do user
é sobrescrito sem aviso. Nenhum passo já feito é refeito. Nenhuma pergunta já respondida volta.
install.sh — núcleo com case statement
#!/usr/bin/env bash
set -euo pipefail
STATE_FILE="_install-state.json"
TEMPLATE_FILE=".iamasters/_install-state.template.json"
# 1. Inicializa state se não existir (fresh=true)
if [[ ! -f "$STATE_FILE" ]]; then
cp "$TEMPLATE_FILE" "$STATE_FILE"
jq '.fresh = true | .started_at = (now | todate)' \
"$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
fi
# 2. Lê fase atual
PHASE=$(jq -r '.phase' "$STATE_FILE")
# 3. Executa o handler da fase (case dispatch)
case "$PHASE" in
brand-context)
run_brand_context_step # só pergunta se faltar
advance_phase "claude-md-bootstrap"
;;
claude-md-bootstrap)
run_claude_md_step # gera só se CLAUDE.md ausente
advance_phase "skills-install"
;;
skills-install)
run_skills_install # checa cada skill, pula se já instalada
advance_phase "hooks-install"
;;
hooks-install)
run_hooks_install # idempotente via jq merge em settings
advance_phase "welcome-quick-win"
;;
welcome-quick-win)
run_quick_win_step # opcional, pergunta antes
advance_phase "done"
;;
done)
echo "✓ Install já completo. Use --reset para reinstalar."
exit 0
;;
*)
echo "✗ Fase desconhecida: $PHASE"; exit 1
;;
esac
🚨 Alerta — re-run que apaga progresso = user nunca volta
O pior bug do install gate é parecer idempotente e não ser. Casos clássicos:
sobrescrever CLAUDE.md que o user já editou, regenerar brand.md apagando
customizações, "resetar" silenciosamente o state quando encontra um campo desconhecido.
Regra: qualquer arquivo do user com mtime > started_at do install precisa pedir confirmação antes de tocar. Sempre. Sem exceção.
Sempre lê state antes de agir.
advance_phase só após sucesso.
Reinstalar é opt-in com flag.
👋 First-time greeting via SessionStart hook
Depois do install, na primeira vez que o user abre o Claude Code no projeto, um hook
SessionStart lê o _install-state.json, vê
fresh=true, mostra um checklist com os próximos 5 passos sugeridos —
e marca fresh=false para nunca mais aparecer automaticamente.
✓ Fazer — greeting só na 1ª vez
- Hook checa
fresh === true. - Mostra 3-5 próximos passos concretos.
- Aponta para arquivos reais (welcome demo, CLAUDE.md).
- Marca
fresh = falseao final. - Tem comando explícito pra reativar:
/welcome.
✗ Evitar — greeting toda vez
- Mostrar o checklist em todo SessionStart.
- "Bem-vindo de volta!" antes de qualquer prompt.
- Dicas aleatórias que poluem o contexto.
- Mensagens longas que o user vai começar a fechar no automático.
- Nenhum jeito de desativar.
Saída do greeting (primeira sessão)
╭─────────────────────────────────────────────────────────────╮
│ Bem-vindo ao iAmasters OS — instalação completa ✓ │
├─────────────────────────────────────────────────────────────┤
│ Próximos 5 passos sugeridos: │
│ │
│ 1. Abrir projects/welcome/brand-applied-demo.html │
│ 2. Ler CLAUDE.md (3 min) — entender as regras do OS │
│ 3. Rodar /skills para ver o que está instalado │
│ 4. Criar seu 1º projeto: /new-project <nome> │
│ 5. Quando travar, rodar /doctor │
│ │
│ Este checklist não aparece de novo. Use /welcome para ver. │
╰─────────────────────────────────────────────────────────────╯
💡 Princípio — silêncio é respeito
O greeting é caro: aparece uma vez, tem que valer. Depois disso, o OS volta a ser silencioso
por default. Confiança se constrói não pedindo atenção desnecessária. O /welcome
fica disponível como comando explícito pra quem quiser revisitar.
📝 Resumo do módulo
Próximo módulo:
4.5 — Operação diária: rotinas, telemetria local e o ciclo de manutenção do OS