spec-first-copilot 0.7.0 → 0.8.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,437 @@
1
+ ---
2
+ name: sf-onboard
3
+ description: |
4
+ Brownfield onboarding. Clona repo existente em projetos/, lê o código e gera
5
+ TRD + docs/ + projetos.yaml sem PRD. Cartógrafo descritivo (não arquiteto).
6
+ Trigger: /sf-onboard <git-url> [--branch=X] [--tag=Y] [--name=Z]
7
+ author: GustavoMaritan
8
+ version: 1.0.0
9
+ date: 2026-05-11
10
+ ---
11
+
12
+ # /sf-onboard $ARGUMENTS
13
+
14
+ Skill atômica de **brownfield onboarding**. Clona um repositório existente em
15
+ `projetos/`, lê o código e gera **TRD + `docs/` + `projetos.yaml`** sem PRD.
16
+
17
+ **Filosofia**: cartógrafo, não arquiteto. Mapeia stack, padrões e uso sem julgar.
18
+ Coders futuros SEGUEM o que já existe — propostas de mudança entram como
19
+ features explícitas do dev.
20
+
21
+ ## Argumento
22
+
23
+ ```
24
+ /sf-onboard <git-url> [--branch=<branch>] [--tag=<tag>] [--name=<scope-name>]
25
+ ```
26
+
27
+ | Param | Default | Descrição |
28
+ |-------|---------|-----------|
29
+ | `<git-url>` | (obrigatório) | URL git clonável (https/ssh). Auth é pré-req do user (SSH key/PAT configurado) |
30
+ | `--branch` | `main` | Branch a clonar |
31
+ | `--tag` | — | Tag específica (mutuamente exclusivo com `--branch`) |
32
+ | `--name` | inferido do URL | Nome do scope. Default: `onboard_<repo-name>` |
33
+
34
+ **Multi-repo** (poly-repo): re-executar a skill N vezes, uma por repo. Cada
35
+ execução adiciona uma entry em `projetos.yaml` e merge aditivo no TRD.
36
+
37
+ ## Multi-agent
38
+
39
+ | Agente | Modelo | Papel |
40
+ |--------|--------|-------|
41
+ | **DB Reader** | Sonnet | Lê migrations, schemas, ORMs → fragmento §Área-DB |
42
+ | **Backend Reader** | Sonnet | Lê routes/controllers/services → fragmento §Área-Backend + §6 Integrações |
43
+ | **Frontend Reader** | Sonnet | Lê rotas, componentes, state → fragmento §Área-Frontend |
44
+ | **Infra Reader** | Sonnet | Lê Dockerfile, CI/CD, k8s, configs → fragmento §Área-Infra |
45
+ | **Dependency Reader** | Sonnet | Lê package.json / *.csproj / pom.xml / go.mod → fragmento §1.1 Stack |
46
+ | **Security Reader** | Sonnet | Lê auth/authz/secrets/headers → fragmento §7 Segurança |
47
+ | **Patterns Reader** | Sonnet | Identifica padrões observados (estrutura, naming, error handling, validação, testes, logging) → fragmento §1.6 |
48
+ | **Analyzer** | Opus | Consolida TODOS fragmentos → TRD completo + `docs/` + `projetos.yaml` |
49
+
50
+ ## Pré-condições
51
+
52
+ | # | Validação | Se falhar |
53
+ |---|-----------|-----------|
54
+ | 1 | `<git-url>` informado | Parar → "Informe a URL do repositório. Ex: /sf-onboard https://github.com/org/app.git" |
55
+ | 2 | `git` instalado no PATH (`git --version`) | Parar → "Git não encontrado no PATH" |
56
+ | 3 | **Auth git pré-verificada** (ver passo 0) | Parar com instrução específica conforme tipo de URL |
57
+ | 4 | `--branch` e `--tag` não usados juntos | Parar → "Use --branch OU --tag, não ambos" |
58
+ | 5 | Scope `onboard_<repo>` não está em `analyzing` | Se sim → "Onboarding anterior incompleto. Rode novamente pra retomar" |
59
+
60
+ ### Passo 0 — Verificar autenticação ANTES de clonar
61
+
62
+ Detectar tipo de URL e testar auth proativamente. Falhar cedo com mensagem
63
+ clara é melhor que esperar o `git clone` quebrar sem contexto.
64
+
65
+ **Detectar tipo de URL**:
66
+
67
+ | Padrão | Tipo |
68
+ |--------|------|
69
+ | `git@<host>:<org>/<repo>.git` | SSH |
70
+ | `ssh://git@<host>/...` | SSH |
71
+ | `https://<host>/...` | HTTPS |
72
+
73
+ **Se SSH** — testar agente + conectividade:
74
+
75
+ ```bash
76
+ # 1. ssh-agent rodando com chave carregada?
77
+ ssh-add -l
78
+ # Saída esperada: linha(s) com fingerprint. Se "The agent has no identities",
79
+ # user precisa rodar `ssh-add ~/.ssh/id_ed25519` (ou similar).
80
+ # Se "Could not open a connection to your authentication agent",
81
+ # user precisa iniciar o ssh-agent.
82
+
83
+ # 2. Host autenticável? (extrair host da URL, ex: github.com)
84
+ ssh -T -o StrictHostKeyChecking=accept-new -o BatchMode=yes git@<host>
85
+ # GitHub retorna exit 1 com "Hi <user>! You've successfully authenticated"
86
+ # GitLab/Bitbucket têm mensagens análogas.
87
+ # Exit 255 ou "Permission denied (publickey)" → chave não autorizada.
88
+ ```
89
+
90
+ **Se HTTPS** — testar credential helper ou GH CLI:
91
+
92
+ ```bash
93
+ # 1. Git credential helper configurado?
94
+ git config --get credential.helper
95
+ # Vazio → user usa GH CLI ou vai ser perguntado credencial no clone.
96
+
97
+ # 2. (Se host = github.com) GH CLI autenticado?
98
+ gh auth status --hostname github.com
99
+ # Exit 0 + "Logged in to github.com" → ok
100
+ # Exit 1 → não logado
101
+
102
+ # 3. Alternativa: tentar ls-remote sem clonar (mais leve que clone)
103
+ GIT_TERMINAL_PROMPT=0 git ls-remote --heads <git-url> HEAD
104
+ # Exit 0 → auth ok
105
+ # Exit ≠ 0 + "Authentication failed" → credencial ausente/inválida
106
+ # Exit ≠ 0 + "could not read Username" → terminal prompt bloqueado, sem credential helper
107
+ ```
108
+
109
+ **Mensagens de erro específicas**:
110
+
111
+ | Cenário | Mensagem |
112
+ |---------|----------|
113
+ | SSH sem ssh-agent | "ssh-agent não está rodando. Inicie com `eval $(ssh-agent)` (Linux/Mac) ou `Start-Service ssh-agent` (Windows)" |
114
+ | SSH agent vazio | "Nenhuma chave SSH carregada. Rode `ssh-add ~/.ssh/id_ed25519` (ajuste o nome se diferente)" |
115
+ | SSH host nega | "Chave SSH não autorizada em `<host>`. Verifique se a chave pública está cadastrada em `<host>/settings/keys` (GitHub) ou equivalente" |
116
+ | HTTPS sem helper nem GH CLI | "Credencial HTTPS não configurada. Opções: (1) `gh auth login` se host=github.com; (2) configurar credential helper (`git config --global credential.helper manager`); (3) usar URL SSH (`git@<host>:...`)" |
117
+ | HTTPS GH CLI deslogado | "GH CLI não autenticado. Rode `gh auth login` e tente novamente" |
118
+ | `ls-remote` falha por motivo desconhecido | "Falha ao acessar o repo: `<stderr do ls-remote>`. Verifique a URL e suas credenciais" |
119
+
120
+ **Sucesso**: log breve "Auth OK ({tipo})" e prosseguir para o Passo 1.
121
+
122
+ > **Nota Windows/PowerShell**: comandos `ssh-add`, `ssh -T`, `gh`, `git`
123
+ > assumem disponíveis no PATH. Em ambientes sem ssh-agent nativo (Windows
124
+ > antigo), instruir uso do GH CLI ou HTTPS+PAT.
125
+
126
+ ## Passos
127
+
128
+ ### 1. Resolver nome do scope e path
129
+
130
+ - `<repo-name>` = último segmento da URL sem `.git`
131
+ - Ex: `https://github.com/org/app-legado.git` → `app-legado`
132
+ - `scope = --name || "onboard_<repo-name>"`
133
+ - `path = "projetos/<repo-name>"`
134
+
135
+ Se `--branch` ausente e `--tag` ausente → branch = `main`.
136
+
137
+ ### 2. Detectar re-onboard
138
+
139
+ | Condição | Comportamento |
140
+ |----------|---------------|
141
+ | `workspace/Output/<scope>/.context.md` não existe | **Primeira vez** — criar tudo do zero |
142
+ | `.context.md` existe com tipo=onboard | **Re-onboard** — pull no clone existente, merge aditivo |
143
+ | `.context.md` existe com tipo≠onboard | Parar → "Scope <scope> já é usado por pipeline normal. Use --name pra dar outro nome" |
144
+
145
+ ### 3. Clonar / atualizar o repositório
146
+
147
+ **Primeira vez**:
148
+ ```bash
149
+ git clone --branch <branch> <git-url> projetos/<repo-name>
150
+ # OU se --tag:
151
+ git clone --depth 1 --branch <tag> <git-url> projetos/<repo-name>
152
+ ```
153
+
154
+ **Re-onboard**:
155
+ ```bash
156
+ cd projetos/<repo-name>
157
+ git fetch
158
+ git checkout <branch-ou-tag>
159
+ git pull --ff-only
160
+ ```
161
+
162
+ Capturar `commit_hash` resultante (`git rev-parse HEAD`) — vai pro snapshot.
163
+
164
+ ### 4. Criar `.context.md`
165
+
166
+ ```yaml
167
+ ---
168
+ nome: "<scope>"
169
+ tipo: "onboard"
170
+ first_run: true
171
+ prd_existe: false
172
+ trd_existe: false
173
+ prd_aprovado: false
174
+ trd_aprovado: false
175
+ areas_tocadas: []
176
+ input_path: "workspace/Input/<scope>/"
177
+ repo_url: "<git-url>"
178
+ repo_branch: "<branch-ou-tag>"
179
+ repo_path: "projetos/<repo-name>"
180
+ status: "cloned"
181
+ ultima_skill: "/sf-onboard"
182
+ atualizado_em: "<ISO_DATETIME>"
183
+ ---
184
+ ```
185
+
186
+ ### 5. Gerar snapshot rastreável
187
+
188
+ Criar `workspace/Input/<scope>/code-snapshot.md`:
189
+
190
+ ```markdown
191
+ # Code Snapshot — <repo-name>
192
+
193
+ **Repo**: <git-url>
194
+ **Branch/Tag**: <branch-ou-tag>
195
+ **Commit**: <commit_hash>
196
+ **Onboard em**: <ISO_DATETIME>
197
+
198
+ ## Arquivos lidos
199
+
200
+ | Arquivo | Hash | Tipo | Classificação | Lido por |
201
+ |---------|------|------|---------------|----------|
202
+ | `package.json` | a3f29c1d | dependency | NOVO | Dependency Reader |
203
+ | `src/api/UserController.cs` | b8e221f0 | source | NOVO | Backend Reader, Patterns Reader |
204
+ | ... | | | | |
205
+ ```
206
+
207
+ **Regras**:
208
+ - Hash SHA-256 truncado em 8 chars
209
+ - Classificação: NOVO / MODIFICADO / INALTERADO (vs snapshot anterior)
210
+ - Re-onboard: append nova seção `## Snapshot N — <ISO_DATETIME>` (append-only)
211
+ - Ignorar: `node_modules/`, `bin/`, `obj/`, `dist/`, `build/`, `.git/`, `vendor/`, `target/`, lock files binários
212
+
213
+ ### 6. Atualizar status → analyzing
214
+
215
+ ```yaml
216
+ status: "analyzing"
217
+ atualizado_em: "<ISO_DATETIME>"
218
+ ```
219
+
220
+ ### 7. Disparar readers em paralelo
221
+
222
+ Cada Reader recebe:
223
+ - Path do clone: `projetos/<repo-name>/`
224
+ - Lista de arquivos relevantes pra área (pre-filtrado por glob)
225
+ - Snapshot anterior (se re-onboard) pra detectar MODIFICADO
226
+
227
+ | Reader | Globs (exemplos) | Output |
228
+ |--------|------------------|--------|
229
+ | **Dependency** | `package.json`, `*.csproj`, `pom.xml`, `go.mod`, `requirements.txt`, `Pipfile`, `Gemfile`, `composer.json` | Linguagem, framework, versões, libs principais |
230
+ | **DB** | `**/migrations/**`, `**/Migrations/**`, `**/*.sql`, `**/schema.prisma`, `**/models/**`, `**/Entities/**` | Tabelas, colunas, índices, FKs, ORM detectado |
231
+ | **Backend** | `**/Controllers/**`, `**/routes/**`, `**/api/**`, `**/handlers/**`, `**/services/**`, `**/usecases/**` | Endpoints (método+rota+auth), services, integrações |
232
+ | **Frontend** | `**/pages/**`, `**/routes/**`, `**/components/**`, `**/store/**`, `**/hooks/**` | Rotas, componentes, state lib, design system |
233
+ | **Infra** | `Dockerfile*`, `docker-compose*.yml`, `.github/workflows/**`, `.gitlab-ci.yml`, `**/*.tf`, `k8s/**`, `helm/**` | Containers, CI/CD, deploy, env vars esperadas |
234
+ | **Security** | `**/*auth*`, `**/*authz*`, `**/*middleware*`, `**/*.env.example`, `.env*` (só nomes de var, nunca valores), config files | Mecanismo de auth, papéis, CORS, secrets management |
235
+ | **Patterns** | Mesmos do Backend + Frontend, amostragem representativa | Estrutura, naming, error handling, validação, testes, logging — com snippets reais |
236
+
237
+ **Cada Reader produz** um fragmento estruturado seguindo as seções correspondentes
238
+ do `TRD.template.md`. Não interpreta — só cataloga. Se algo está ambíguo entre
239
+ 2 arquivos (ex: 2 padrões diferentes de error handling), Reader reporta **ambos
240
+ com contagem** ("X em 12 arquivos, Y em 3"). Analyzer decide o dominante depois.
241
+
242
+ ### 8. Analyzer consolida (Opus)
243
+
244
+ Recebe todos fragmentos. Gera:
245
+
246
+ #### 8.1 `workspace/Output/<scope>/TRD.md`
247
+
248
+ Seguindo `templates/feature/TRD.template.md`:
249
+
250
+ - **§1 §Sistema** (GATE=SIM sempre)
251
+ - §1.1 Stack (do Dependency Reader)
252
+ - §1.2 Arquitetura C4 (inferida da estrutura de pastas + dependências)
253
+ - §1.3 Ambientes (do Infra Reader)
254
+ - §1.4 Deploy (do Infra Reader)
255
+ - §1.5 Segurança baseline (do Security Reader)
256
+ - **§1.6 Padrões Observados** (do Patterns Reader — OBRIGATÓRIO em onboard)
257
+ - **§2-§5 §Áreas** — GATE=SIM para áreas que existem no código, GATE=NÃO pras ausentes
258
+ - **§6 Integrações Externas** (do Backend Reader)
259
+ - **§7 Segurança Detalhada** (do Security Reader)
260
+ - **§8 Estratégia de Testes** (do Patterns Reader)
261
+ - **§9 Decisões Técnicas (ADRs)** — gerar ADRs **inferidas** com flag `Inferido: true`
262
+ - **§10 Ambiguidades** — VAZIA em onboard (código resolve tudo)
263
+ - **§11 Rastreabilidade** — citar arquivo:linha por seção
264
+
265
+ **Regras absolutas do Analyzer em onboard**:
266
+ - ❌ **NUNCA** marcar ambiguidade. Se 2 padrões divergem, escolher o **mais frequente**
267
+ e documentar a divergência em §1.6.7 sem julgar.
268
+ - ❌ **NUNCA** sugerir mudanças, "melhorias" ou best practices.
269
+ - ❌ **NUNCA** usar linguagem prescritiva ("deveria", "recomendado"). Sempre descritiva ("usa", "implementa").
270
+ - ✅ Citar arquivo:linha em snippets de §1.6.
271
+
272
+ #### 8.2 `docs/` (5 arquivos cross-feature)
273
+
274
+ Gerar todos os 5 a partir do TRD recém-gerado, seguindo templates de `estrutura/`:
275
+
276
+ | Arquivo | Origem |
277
+ |---------|--------|
278
+ | `docs/architecture.md` | TRD §1.1 + §1.2 + §1.3 + §1.4 |
279
+ | `docs/domain.md` | TRD §Área-DB + visão de negócio inferida da nomenclatura |
280
+ | `docs/conventions.md` | TRD §1.6 + §7 + §8 |
281
+ | `docs/apiContracts.md` | TRD §2.1 + §6 |
282
+ | `docs/decisions.md` | TRD §9 (todas ADRs com flag `Inferido: true`) |
283
+
284
+ Cada arquivo gerado tem changelog inicial:
285
+ ```markdown
286
+ | <DATA> | onboard_<repo> | INICIAL | Gerado por /sf-onboard a partir do código existente |
287
+ ```
288
+
289
+ #### 8.3 `projetos.yaml`
290
+
291
+ Popular com o repo clonado:
292
+
293
+ ```yaml
294
+ org: "<org-inferida-do-url>"
295
+ project: "<nome-do-projeto>"
296
+
297
+ repos:
298
+ <nome-curto>:
299
+ repo: "<org>/<repo-name>"
300
+ path: "projetos/<repo-name>"
301
+ existing: true # SEMPRE true em onboard
302
+ areas: [<áreas-detectadas>] # do Analyzer baseado nos GATEs do TRD
303
+ stack: "<stack>" # do TRD §1.1
304
+ branch_prefix: "feature/" # ou detectar convenção do repo
305
+ ```
306
+
307
+ Se já existe `projetos.yaml` (re-onboard ou multi-repo) → **merge aditivo**:
308
+ adiciona novo repo, não sobrescreve outros.
309
+
310
+ ### 9. Detecção de monorepo (opcional)
311
+
312
+ Se o repo clonado contém múltiplos `package.json` / `pom.xml` / `*.csproj` em
313
+ subdirs (não na raiz), pode ser monorepo. Comportamento:
314
+
315
+ - Cada subprojeto identificado vira uma entry em `projetos.yaml`
316
+ - Mesma URL git, paths diferentes (`projetos/<repo>/services/api`, `projetos/<repo>/web`)
317
+ - TRD documenta arquitetura geral em §1.2
318
+
319
+ ### 10. Atualizar `.context.md` → done
320
+
321
+ ```yaml
322
+ status: "done"
323
+ prd_existe: false
324
+ trd_existe: true
325
+ prd_aprovado: false # n/a — sempre false em onboard
326
+ trd_aprovado: true # SEMPRE true em onboard (código é a verdade)
327
+ areas_tocadas: [<áreas-com-GATE=SIM>]
328
+ ultima_skill: "/sf-onboard"
329
+ atualizado_em: "<ISO_DATETIME>"
330
+ ```
331
+
332
+ ### 11. Auto-publish (se `sfw.config.yml` configurado)
333
+
334
+ Se `sfw.config.yml` existe:
335
+
336
+ ```
337
+ /sf-publish <scope> TRD
338
+ ```
339
+
340
+ (Não publica PRD — não existe. Não publica Progresso — não há /sf-plan em onboarding.)
341
+
342
+ > **Nota**: `docs/` é local-only por design (ver `.github/skills/sf-publish/SKILL.md`).
343
+ > Se quiser publicar no Confluence, copiar manualmente ou aguardar feature futura.
344
+
345
+ ### 12. Saída obrigatória
346
+
347
+ ```
348
+ /sf-onboard — completo
349
+
350
+ Repo: <git-url> (<branch-ou-tag> @ <commit-hash>)
351
+ Clone: projetos/<repo-name>
352
+
353
+ Artefatos gerados:
354
+ - workspace/Output/<scope>/TRD.md
355
+ - workspace/Output/<scope>/.context.md
356
+ - workspace/Input/<scope>/code-snapshot.md
357
+ - docs/architecture.md
358
+ - docs/domain.md
359
+ - docs/conventions.md
360
+ - docs/apiContracts.md
361
+ - docs/decisions.md (<N> ADRs inferidas)
362
+ - projetos.yaml (entry: <nome-curto>)
363
+
364
+ Áreas detectadas: BACK, FRONT, DB, INFRA (conforme TRD)
365
+ Padrões observados: ver TRD §1.6 (<N> categorias)
366
+ ADRs inferidas: <N> (revisar e remover flag `Inferido` ao confirmar)
367
+
368
+ Próximos passos:
369
+ 1. [dev] Revisar workspace/Output/<scope>/TRD.md
370
+ - Especialmente §1.6 Padrões Observados (coders vão seguir)
371
+ - §9 ADRs inferidas (confirmar ou ajustar; remover flag `Inferido` ao validar)
372
+ 2. [dev] Revisar docs/ (5 arquivos)
373
+ 3. Para onboardar outro repo do mesmo projeto: /sf-onboard <outra-url>
374
+ 4. Para começar feature nova: /sf-start feat_<nome>
375
+ ```
376
+
377
+ ## Re-onboard
378
+
379
+ Quando o código legado evolui e quer atualizar a base documental:
380
+
381
+ ```
382
+ /sf-onboard <mesma-url> [--branch=<outra>]
383
+ ```
384
+
385
+ Skill detecta `tipo=onboard` no `.context.md` existente e:
386
+ 1. `git pull` no clone
387
+ 2. Snapshot novo com classificação NOVO/MODIFICADO/INALTERADO
388
+ 3. Readers reprocessam **apenas arquivos MODIFICADOS ou NOVOS**
389
+ 4. Analyzer faz **merge aditivo** no TRD existente
390
+ 5. `docs/` atualizado com changelog `re-onboard <DATA>`
391
+ 6. `projetos.yaml` inalterado (mesmo repo)
392
+
393
+ ## Multi-repo
394
+
395
+ App = N repos (api, web, worker). Rodar a skill N vezes:
396
+
397
+ ```
398
+ /sf-onboard https://github.com/org/app-api.git --name=onboard_app
399
+ /sf-onboard https://github.com/org/app-web.git --name=onboard_app
400
+ /sf-onboard https://github.com/org/app-worker.git --name=onboard_app
401
+ ```
402
+
403
+ Mesmo `--name` → mesmo `.context.md`, mesmo TRD (merge aditivo), `projetos.yaml`
404
+ recebe 3 entries. Áreas distribuídas: api=[BACK,DB], web=[FRONT], worker=[BACK].
405
+
406
+ Se `--name` diferentes → scopes separados (raro, mas suportado).
407
+
408
+ ## Erros
409
+
410
+ | Erro | Ação |
411
+ |------|------|
412
+ | Auth falha (capturado no Passo 0) | Parar com mensagem específica conforme cenário (ver tabela do Passo 0) |
413
+ | Clone falha mesmo com auth OK | Reportar `stderr` do git; verificar se a URL e branch existem |
414
+ | Repo vazio | Parar — "Nada a onboardar" |
415
+ | Nenhum reader produziu fragmento (stack desconhecida) | Parar — "Stack não reconhecida. Detalhe TRD manualmente ou peça suporte" |
416
+ | `projetos/<repo-name>/` já existe e NÃO é clone do mesmo URL | Parar — "Path já em uso por outro repo. Use --name pra dar outro nome" |
417
+ | `sfw.config.yml` aponta backend mas /sf-publish falhou | Reportar warning, manter artefatos locais |
418
+
419
+ ## Diferença vs `/sf-start`
420
+
421
+ | Aspecto | `/sf-start` | `/sf-onboard` |
422
+ |---------|--------------|----------------|
423
+ | Entrada | Insumos brutos do PM em `workspace/Input/` | URL git de código existente |
424
+ | Gera PRD? | Sim (se insumos têm produto) | Não |
425
+ | Gera TRD? | Sim (após aprovação) | Sim (auto-aprovado) |
426
+ | Gera `docs/`? | No `/sf-design` (first-run) | Diretamente |
427
+ | Gera `projetos.yaml`? | No `/sf-design` (first-run) | Diretamente |
428
+ | Ambiguidades? | Sim (PRD §12, TRD §10) | Não (código resolve) |
429
+ | Checkpoint humano? | Sim (PM + dev) | Não (one-shot) |
430
+ | Filosofia | Design upfront | Cartógrafo descritivo |
431
+
432
+ ## Notas
433
+
434
+ - `/sf-onboard` é **ortogonal** ao pipeline `/sf-start`. Não chama discovery/sf-extract/sf-design.
435
+ - Após `/sf-onboard`, qualquer feature nova entra pelo `/sf-start feat_<nome>` normal — `first_run` da feature será `false` porque `docs/` já existe.
436
+ - Coder de feature futura **DEVE** ler TRD §1.6 Padrões Observados antes de implementar.
437
+ - Re-onboard NÃO atualiza `specs/` de features já desenvolvidas (são imutáveis pós-`/sf-dev`).