raptor-aios 0.14.1 → 0.16.0

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +196 -1
  2. package/README.md +15 -8
  3. package/dist/_core/dist/agents/prompt-builder.js +19 -5
  4. package/dist/_core/dist/audit/logger.js +16 -5
  5. package/dist/_core/dist/audit/schema.js +10 -0
  6. package/dist/_core/dist/audit/validate.js +15 -0
  7. package/dist/_core/dist/constitution/core.js +4 -0
  8. package/dist/_core/dist/constitution/index.js +1 -1
  9. package/dist/_core/dist/design/asset-normalize.js +10 -2
  10. package/dist/_core/dist/design/downloader.js +38 -2
  11. package/dist/_core/dist/design/fetcher.js +41 -9
  12. package/dist/_core/dist/design/figma-rest.js +5 -2
  13. package/dist/_core/dist/design/mapper.js +2 -0
  14. package/dist/_core/dist/design/scaffold.js +85 -8
  15. package/dist/_core/dist/design/screens.js +23 -1
  16. package/dist/_core/dist/frontmatter/index.js +26 -0
  17. package/dist/_core/dist/gates/builtin.js +32 -9
  18. package/dist/_core/dist/gates/design-gates.js +162 -9
  19. package/dist/_core/dist/gates/m7-gates.js +28 -7
  20. package/dist/_core/dist/jira/mapper.js +100 -2
  21. package/dist/_core/dist/models/checklist.js +10 -6
  22. package/dist/_core/dist/models/clarify.js +69 -1
  23. package/dist/_core/dist/models/impl-log.js +92 -0
  24. package/dist/_core/dist/prompts/resumption.js +16 -2
  25. package/dist/_core/dist/state/feature.js +1 -1
  26. package/dist/_core/dist/transport/mcp.js +1 -0
  27. package/dist/_core/dist/verify/fidelity.js +7 -3
  28. package/dist/_core/package.json +1 -1
  29. package/dist/_core/templates/plan.md.hbs +4 -6
  30. package/dist/_core/templates/raptor.yml.hbs +0 -1
  31. package/dist/_core/templates/spec.md.hbs +29 -19
  32. package/dist/commands/approve.js +27 -11
  33. package/dist/commands/clarify.js +2 -1
  34. package/dist/commands/design/fidelity.js +41 -10
  35. package/dist/commands/design/pull.js +9 -1
  36. package/dist/commands/design/status.js +42 -6
  37. package/dist/commands/design/sync.js +27 -6
  38. package/dist/commands/doctor.js +0 -8
  39. package/dist/commands/implement.js +118 -7
  40. package/dist/commands/new.js +37 -6
  41. package/dist/commands/preset/add.js +8 -12
  42. package/dist/commands/taskstoissues.js +16 -5
  43. package/dist/commands/trace.js +2 -0
  44. package/dist/commands/upgrade.js +10 -13
  45. package/dist/commands/verify/fidelity.js +4 -0
  46. package/dist/shared/design.js +2 -1
  47. package/dist/shared/init-core.js +2 -1
  48. package/dist/shared/project.js +4 -4
  49. package/dist/shared/scaffold.js +15 -16
  50. package/package.json +1 -1
  51. package/scripts/bash/common.sh +77 -31
  52. package/scripts/bash/create-new-feature.sh +75 -14
  53. package/scripts/bash/setup-plan.sh +3 -3
  54. package/scripts/check-naming.sh +16 -8
  55. package/scripts/powershell/common.ps1 +51 -9
  56. package/scripts/powershell/create-new-feature.ps1 +3 -1
  57. package/scripts/prepare-npm.mjs +1 -1
  58. package/templates/commands/analyze.md +1 -1
  59. package/templates/commands/clarify.md +31 -12
  60. package/templates/commands/constitution.md +3 -3
  61. package/templates/commands/implement.md +38 -9
  62. package/templates/commands/plan.md +2 -2
  63. package/templates/commands/specify.md +22 -4
  64. package/templates/plan-template.md +1 -1
  65. package/templates/spec-template.md +2 -2
package/CHANGELOG.md CHANGED
@@ -3,7 +3,202 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
- ## [Unreleased]
6
+ ## [0.16.0] - 2026-06-24
7
+
8
+ Correções P0/P1/P2 do re-teste KAN-20 v2 (DuCorre/FastBoy): fechar o **processo
9
+ de fidelidade** para o erro não nascer, não só ser pego no fim, e endurecer
10
+ rastreabilidade/determinismo nas pontas (resiliência de gate, hash unificado,
11
+ backbone de fidelidade instalado, clarify determinístico, drift acionável). Esta
12
+ versão também **entrega os dois deferidos** — constituição em **fonte única** e
13
+ **edições pós-implement auditadas** — e incorpora os achados de uma **revisão
14
+ integrada adversarial** que a suíte verde escondia (corrupção latente do
15
+ `audit.jsonl` por campo `undefined`; vazamento residual do laço de fidelidade).
16
+
17
+ ### Added
18
+
19
+ - **`gate.design.anatomy`** (M3, _required_) — a `## Anatomy` (contrato de fidelidade)
20
+ precisa **nascer do pixel real**: cada tela com anatomia tem que registrar um
21
+ `figma_screenshot:` (capturado via `get_screenshot`) presente em disco e ≥ 1 KB. Sem
22
+ isso o gate bloqueia o verify — impede o contrato de nascer cego e o `implement`
23
+ construir fielmente a coisa errada (F10-2). Os prompts (`spec.md.hbs`, `/raptor-specify`)
24
+ agora mandam capturar o screenshot ANTES de autorar a anatomia.
25
+ - **Backbone de fidelidade instalado no projeto** — `init`/`upgrade` agora copiam
26
+ `scripts/review/{hex-scan,diff-tokens}.mjs` e `scripts/app/screenshot.sh` para
27
+ `.raptor/scripts/`, e o prompt do scorer referencia `.raptor/scripts/...`. Antes os
28
+ comandos do scorer apontavam para `scripts/...` que nenhum comando instalava — hex-scan
29
+ e o screenshot do app ficavam indisponíveis no caminho documentado (F8-1).
30
+ - **`implement --finalize` audita edições pós-implement** — quando o finalize absorve um
31
+ artefato que mudou após ser registrado (drift), emite o evento `implement.drift_detected`
32
+ (dedup por `(path|hash)`, idempotente), tornando a edição pós-Fase-7 visível no trilho de
33
+ audit em vez de reconciliada em silêncio. A detecção é pura (`reconstructImplLogFromAudit`
34
+ expõe os drifts); o `verify` segue read-only e o gate M7 segue puro (F7-untracked).
35
+
36
+ ### Changed
37
+
38
+ - **`raptor design fidelity` virou laço fechado** — sai **não-zero** enquanto houver
39
+ região < threshold, scorecard com problema ou `design-decision` pendente, e **0** só na
40
+ convergência. Um laço de agente/CI itera até passar, em vez de a convergência depender
41
+ de disciplina manual (F10-3). O `evaluateFeatureFidelity` agora expõe
42
+ `pendingDesignDecisions`, e tanto o loop quanto a lente `raptor verify fidelity` passam a
43
+ reportar **não-ok** numa `design-decision` pendente — alinhados com `gate.design.fidelity`
44
+ (antes a lente dava verde enquanto o gate bloqueava).
45
+ - **`raptor design fidelity` converge ao veredito do gate (vazamento residual do F10-3)** — a
46
+ lente do laço só olhava os scorecards, então o comando podia sair **0** ("converged") enquanto
47
+ `raptor verify` falharia por falta de evidência de screenshot ou cobertura por região da
48
+ anatomia. Agora o comando roda `gate.design.fidelity` e `gate.design.anatomy` como autoridade
49
+ da convergência (só engajam com spec aprovada). Um laço de agente que parava no verde otimista
50
+ passa a iterar até o gate de fato passar.
51
+ - **Constituição em fonte única** — `.raptor/constitution.md` passa a ser a **única** cópia; o
52
+ espelho `.raptor/memory/constitution.md` foi removido. `init` não o escreve mais, `upgrade`
53
+ remove o órfão (com backup antes), e `preset add`/`doctor` só olham o canônico. Os prompts
54
+ (`templates/commands/*`, `plan.md.hbs`), o `scripts/bash/common.sh` (`CONSTITUTION_PATH`) e os
55
+ docs foram repointados; snapshots E2E regenerados. Fim da duplicação que o `upgrade` mantinha
56
+ em sync (F5-1b). _(Scripts PowerShell ficam como follow-up.)_
57
+ - **Skill `/raptor-clarify` alinhada ao caminho determinístico** — a skill 2.1.0 deixou de
58
+ hand-editar `## Clarifications` num formato (`### Session` + `- Q:→A:`) divergente do CLI;
59
+ agora emite `answers.json` e roda `raptor clarify --apply` (o CLI é o dono único da seção,
60
+ formato canônico `- **label** — answer`, UPSERT idempotente). Fim do spec híbrido de dois
61
+ escritores (F3-1/2). Os snapshots E2E passam a cobrir `clarify` (trava o formato).
62
+ - **Drift do impl-log agora é acionável** — `gate.m7.diff_hash_matches` já re-hasha o working
63
+ tree no `verify` e flaga arquivo que mudou/sumiu após ser registrado; a mensagem agora nomeia
64
+ a causa (**impl-log STALE** — edição pós-implement não re-logada) e a remediação
65
+ (`raptor implement --finalize` ou reverter) (F7-stale).
66
+
67
+ ### Fixed
68
+
69
+ - **`.gitignore` não engole mais os assets do app** — o `--design-fetch` deixou de
70
+ ignorar os diretórios `assets/{images,icons,fonts}` inteiros; agora ignora apenas os
71
+ arquivos exatos que o RAPTOR baixou (bloco gerenciado único, idempotente). Assets
72
+ próprios do app (fontes, imagens de onboarding) param de sumir silenciosamente do
73
+ controle de versão (F1-2).
74
+ - **`/raptor-specify` não duplica mais uma feature já seedada** — `create-new-feature.sh`
75
+ ganhou guarda determinística por **slug** (espelhando o `raptor new`): um
76
+ `.raptor/specs/NNN-<slug>` existente é **reusado** (`REUSED: true`), não recriado com um
77
+ número novo que orfanaria a feature seedada (F2-1). `--number` e `--timestamp` optam por
78
+ fora.
79
+ - **`gate.design.ready` resiliente a YAML inválido** — um campo irmão malformado no
80
+ frontmatter de uma tela (ex.: um `screen_role` não-quotado escrito pelo agente) fazia o
81
+ `parseFrontmatter` lançar e o gate descartar o `name`, caindo no slug do arquivo que nunca
82
+ casa (`[screen: X]` reportado como ausente). Novo helper `frontmatterField()` lê o campo
83
+ por line-scan quando o parse YAML falha; usado em `gate.design.ready` e `gate.design.anatomy`
84
+ (F9-1).
85
+ - **Hash da constituição unificado** — dentro do mesmo comando, o prompt-context gravava o
86
+ hash whole-file enquanto `plan`/audit gravavam o hash do bloco RAPTOR-CORE. `buildCanonicalPrompt`
87
+ passa a registrar o **mesmo** core-block hash (helper `constitutionHashFromContent`), então
88
+ prompt e artefato concordam em "qual constituição governou isto" (F5-1).
89
+ - **Serializer do `audit.jsonl` robusto a `undefined`** — o `stableStringify` emitia
90
+ `"chave":undefined` (JSON inválido) para uma propriedade com valor `undefined`, corrompendo a
91
+ linha e a cadeia `prev_hash` a partir dela — e isso **disparava de fato** em `raptor plan`/`init`
92
+ num projeto sem preset. Agora omite chaves `undefined` e mapeia `undefined` em array para `null`
93
+ (espelhando `JSON.stringify`). Achado da revisão integrada adversarial.
94
+ - **`upgrade` não apaga mais o espelho da constituição sem backup** (`backup()` antes do `rmSync`),
95
+ e a regex que remove a chave morta `branch_prefix` foi **ancorada na coluna 0** (não casa uma
96
+ chave homônima aninhada sob outra seção). Achados da revisão integrada.
97
+
98
+ #### Polimento (P2)
99
+
100
+ - **`raptor approve` emite `spec.approved`** (evento distinto, não mais o genérico `spec.updated`) e
101
+ grava `audit_ref` no frontmatter apontando para o id da entrada do audit — link bidirecional
102
+ artefato↔audit (C4), antes sempre null (F4-1/F4-2).
103
+ - **`clarify --apply` faz backup do spec** antes de sobrescrever (a recuperação não depende mais só
104
+ do ledger/hashes) (F3-3).
105
+ - **Warning de scope não empurra mais `--design-scope file`** quando outras refs foram capturadas —
106
+ uma ref sem node-id agora vira aviso suave ("linke o frame específico"), e o escape hatch
107
+ file-wide só aparece quando NADA foi capturado (F1-warn).
108
+ - **Nome de tela duplicado é desambiguado** — dois frames Figma com o mesmo `name` ganham um sufixo
109
+ de node-id no 2º+ , para `[screen: X]` resolver para um arquivo só (F1-dup).
110
+ - **`audit` `file_keys` deduplicado**; **tabela `Post-Design Check` do `plan.md.hbs`** consertada
111
+ (separador/linha partidos quebravam a renderização) (F5-2).
112
+ - **`audit.jsonl` ganha cadeia tamper-evident** — cada evento grava `prev_hash` (sha256 da linha
113
+ anterior; null no genesis), e `raptor verify audit --strict` valida a cadeia e detecta adulteração
114
+ de eventos passados. Campo opcional no schema (back-compat com logs pré-cadeia) (prev_hash).
115
+ - **Branch do `/raptor-specify` (bash) alinhada à convenção do `raptor new`** — o branch agora carrega
116
+ o prefixo `feat/` (`feat/NNN-slug`); o diretório da spec continua o `NNN-slug` puro. `get_next_feature_number`
117
+ e `check_feature_branch` passam a entender o prefixo. `raptor upgrade` remove a chave morta
118
+ `branch_prefix` de projetos legados e o label "legacy cleanup" só aparece quando algo foi de fato
119
+ limpo (F2-2 / F0-2 / F0-3).
120
+ - **`raptor design status --check`** valida o token figma-rest de verdade (chamada `/v1/me`): um token
121
+ clobberado/revogado-mas-presente agora reporta NOT connected, em vez do falso "connected" por
122
+ presença (F0-1).
123
+
124
+ ## [0.15.0] - 2026-06-22
125
+
126
+ Tema do épico KAN-20: **determinismo e consentimento**. Tudo que antes "passava em
127
+ silêncio", "escrevia no card sem perguntar" ou "despejava o arquivo Figma inteiro"
128
+ agora é explícito, opt-in ou escopado por node.
129
+
130
+ ### Added
131
+
132
+ - **`--design-scope node|file`** (em `new`, `design sync`, `design pull`). Controla o
133
+ escopo da captura de telas/assets. Em `new` e `design sync` o default é `node` (só o
134
+ frame linkado pela URL); em `design pull` o default é `file` (inspeção do arquivo
135
+ inteiro). `--design-scope file` é o escape hatch para o comportamento legado (arquivo
136
+ todo). Uma ref COM node-id captura aquele node sob qualquer scope.
137
+ - **`raptor new --activate` / `--no-activate`.** `new` agora aponta
138
+ `.raptor/feature.json` para a feature recém-criada (default ligado). `--no-activate`
139
+ preserva a feature anterior.
140
+ - **`--sync-jira`** (em `approve`, `implement`, `taskstoissues`). Opt-in para escrever
141
+ de volta no card Jira (comentário de ciclo de vida + transição; em `taskstoissues`,
142
+ comentário-resumo dos issues criados). Sem a flag, só loga a ação pretendida.
143
+ - **`raptor implement --finalize`.** Reconstrói `impl-log.md` a partir dos eventos
144
+ `code.generated` do `audit.jsonl` quando as tasks foram fechadas sem `--task`.
145
+ Recupera traces de AC das tags `[AC-#]` do `tasks.md`; quando não há, deixa vazio
146
+ para o gate M7 cobrar (não fabrica pass). Respeita `--dry-run`.
147
+
148
+ ### Changed
149
+
150
+ - **`--design-fetch` agora é escopo por node-id por default** (era arquivo inteiro,
151
+ até 107 telas). Card sem node-id pula a captura com aviso acionável; a spec referencia
152
+ `assets-manifest.json` em vez de listar tudo. **Migração:** use `--design-scope file`
153
+ para o comportamento antigo.
154
+ - **`raptor new` ativa a feature criada por default.** `feature.json` é dobrado no
155
+ auto-commit para empilhar specs sem sujar a árvore. **Migração:** use `--no-activate`
156
+ se contava com a feature anterior continuar ativa.
157
+ - **Escrita no Jira é opt-in (`--sync-jira`)** em `approve`/`implement`/`taskstoissues`.
158
+ **Migração:** pipelines que esperavam status-sync automático precisam adicionar a flag.
159
+ - **`gate.design.fidelity` bloqueia por scorecards.** Avalia scorecards sempre que
160
+ existirem; retorna `skipped` (nunca mais `pass` falso) só quando não há scorecards
161
+ nem impl-log. **Migração:** projetos cujo fidelity passava verde sem evidência podem
162
+ ver o gate bloquear agora.
163
+ - **Gates M7 falham (não skipam) quando há tasks concluídas mas falta `impl-log.md`.**
164
+ Sem tasks concluídas continua `skipped`. A skill `/raptor-implement` foi reescrita
165
+ para registrar cada task via `raptor implement --task`.
166
+ - **Namespace de specs unificado em `.raptor/specs`.** `get_next_feature_number` deixa
167
+ de saltar numeração por varrer `specs/` legado (002 → 011); legado vira só fallback
168
+ de leitura. Espelhado no PowerShell.
169
+ - **`branch_prefix` removido do schema/`raptor.yml`** (dead config, 0 leituras).
170
+ **Migração:** pode remover do `raptor.yml` — não tinha efeito.
171
+ - **stderr do MCP silenciado por default** (`StdioClientTransport` usa `stderr:'ignore'`).
172
+ Reative diagnóstico com `RAPTOR_MCP_DEBUG=1`.
173
+ - **Assets do fetch isolados com `.gitignore` RAPTOR-managed + guarda de colisão** —
174
+ binários baixados não entram em versionamento por acidente; a captura nunca
175
+ sobrescreve arte já versionada.
176
+
177
+ ### Fixed
178
+
179
+ - **Falso negativo de cobertura de US eliminado** — `gate.spec.ready` normaliza IDs:
180
+ header `US-001` casa com `[US1]`.
181
+ - **Markers falsos não acusam mais** — detector ignora code-spans (`` `[PREENCHER]` ``)
182
+ e linhas de checklist negadoras; helper compartilhado em spec/plan/tasks.ready.
183
+ - **Checklist sem ID conta corretamente** — parser conta itens `[x]` mesmo sem `CK-#`
184
+ (contido em `status`; gates estruturados inalterados).
185
+ - **Path in-place no redesign** — `gate.tasks.ready` herda path por fase; reset por
186
+ heading preserva detecção de drift.
187
+ - **Hash stale agora orienta a correção** — `gate.plan/tasks.input_hash` sugere
188
+ `raptor resync <feature> --artifact plan|tasks`.
189
+ - **`clarify --apply` popula `## Clarifications`** (upsert das Q&A; CLI é dono único,
190
+ skill só produz `answers.json`), preserva frontmatter e não mascara marker real.
191
+ - **Wiki-markup do Jira → Markdown no seeding** — `flattenAdf` converte `{quote}`,
192
+ `{{...}}`, `||tabela||`, `hN.`, `{code}`/`{noformat}`, gateado por detecção de wiki
193
+ (não toca Markdown existente; preserva `5 * 4`).
194
+ - **Ator canônico único no audit** — `currentActor` fixa o `user` no `git user.email`
195
+ (fallback name → `USER`).
196
+ - **`HAS_GIT` correto no `--dry-run`** — `create-new-feature.sh` detecta git antes da
197
+ bifurcação dry-run. Skill `specify` passa `--short-name` (slug kebab-case).
198
+ - **Robustez do seeding de design** — tokens vazios direcionam ao MCP `get_variable_defs`
199
+ e o gate bloqueia `tokens.json` vazio (`radii` sempre presente); `gate.design.ready`
200
+ casa telas por `name` do frontmatter (tolerante a sufixo node-id); `design sync`
201
+ avisa (não apaga) telas órfãs da convenção antiga de slug.
7
202
 
8
203
  ## [0.14.1] - 2026-06-21
9
204
 
package/README.md CHANGED
@@ -7,8 +7,8 @@
7
7
  [![npm](https://img.shields.io/npm/v/raptor-aios?color=ef4444&label=raptor-aios&logo=npm)](https://www.npmjs.com/package/raptor-aios)
8
8
  [![node](https://img.shields.io/badge/node-%E2%89%A5%2020-3c873a?logo=node.js&logoColor=white)](https://nodejs.org)
9
9
  [![platform](https://img.shields.io/badge/target-React%20Native-61dafb?logo=react&logoColor=black)](https://reactnative.dev)
10
- [![tests](https://img.shields.io/badge/tests-1481%20passing-22c55e)](#️-desenvolvimento-local)
11
- [![version](https://img.shields.io/badge/version-0.14.0-ef4444)](CHANGELOG.md)
10
+ [![tests](https://img.shields.io/badge/tests-1597%20passing-22c55e)](#️-desenvolvimento-local)
11
+ [![version](https://img.shields.io/badge/version-0.15.0-ef4444)](CHANGELOG.md)
12
12
  [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
13
13
 
14
14
  </div>
@@ -24,7 +24,7 @@ npm install -g raptor-aios
24
24
  cd meu-app-react-native
25
25
  raptor setup # 🧭 assistente guiado: IA/IDE, preset, Jira/Figma (ou 'raptor init' direto)
26
26
  raptor new login-biometrico -d "Permitir login com Face ID / digital"
27
- # → cria a feature E já troca para a branch feat/001-login-biometrico
27
+ # → cria a feature, forka a branch feat/001-login-biometrico do HEAD e a ativa
28
28
  ```
29
29
 
30
30
  ---
@@ -201,7 +201,7 @@ Isolamento e coerência **sem configurar nada** — o `init` já deixa tudo pron
201
201
 
202
202
  ### Uma branch por unidade de trabalho
203
203
 
204
- `raptor new` cria e troca para a branch certa, sempre ramificando de `main`:
204
+ `raptor new` cria e troca para a branch certa, **forkando do HEAD atual** (modelo empilhado, não de `main`): cada nova feature ramifica de onde você está, então specs se **empilham** umas sobre as outras. Ao final, a feature recém-criada vira a **ativa** em `.raptor/feature.json` (comandos seguintes a resolvem sem `--feature`); use `--no-activate` para manter a feature anterior ativa (`--activate` é o default).
205
205
 
206
206
  | Você roda | Branch criada |
207
207
  | -------------------------------------- | --------------------------------------------------- |
@@ -210,7 +210,7 @@ Isolamento e coerência **sem configurar nada** — o `init` já deixa tudo pron
210
210
  | `raptor new refactor-auth --kind=debt` | `debt/003-refactor-auth` |
211
211
  | `raptor new login --jira APP-1234` | `feat/004-login.app-1234` *(tipo inferido do Jira)* |
212
212
 
213
- Numeração sequencial global, prefixo por tipo (`feat`/`bugfix`/`debt`), e a chave do Jira no fim quando houver. Já vem ligado (`git.branch_per_feature`); desligue com `--no-branch` num comando ou `no_git` no projeto.
213
+ Numeração sequencial global, prefixo por tipo (`feat`/`bugfix`/`debt`), e a chave do Jira no fim quando houver. Já vem ligado (`git.branch_per_feature`); desligue com `--no-branch` num comando ou `no_git` no projeto. Por padrão a spec scaffoldada é **commitada** na própria branch (para o próximo `raptor new` empilhar em cima); `--no-commit` deixa a spec sem commit.
214
214
 
215
215
  ### Commits coerentes, sem travar seu fluxo
216
216
 
@@ -237,7 +237,7 @@ Você os executa **dentro do seu agente de IA**. São prompts versionados e rico
237
237
 
238
238
  ### 🧭 `/raptor-constitution`
239
239
 
240
- Cria ou atualiza os princípios do projeto (`.raptor/memory/constitution.md`). É o "contrato social" que todas as fases seguintes respeitam. Use no início, ou quando quiser ratificar uma mudança de regra (com bump de versão).
240
+ Cria ou atualiza os princípios do projeto (`.raptor/constitution.md`). É o "contrato social" que todas as fases seguintes respeitam. Use no início, ou quando quiser ratificar uma mudança de regra (com bump de versão).
241
241
 
242
242
  ### 📝 `/raptor-specify`
243
243
 
@@ -271,9 +271,11 @@ Gera checklists de qualidade temáticos (code quality, testes, segurança, perfo
271
271
 
272
272
  Executa as tarefas em ordem de fase, respeitando paralelização e (se exigido) TDD. Marca cada tarefa `[x]` ao concluir e registra a **evidência M7** (qual arquivo cobre qual critério de aceitação).
273
273
 
274
+ > ⚠️ **`impl-log.md` é inegociável.** Cada `raptor implement --task <id> --files … --acceptance …` atualiza o `impl-log.md` com as execuções, artefatos e traces. Se ele estiver **ausente após a implementação**, os gates M7 **bloqueiam** o `verify` (deixam de "pular" por arquivo inexistente e passam a falhar). Se o agente esqueceu de registrar tarefa por tarefa via `--task`, rode `raptor implement <feat> --finalize` para **reconstruir** o `impl-log.md` a partir dos eventos `code.generated` do `audit.jsonl` — traces de aceitação ausentes ficam vazios para o gate M7 cobrar (complete-os com `--amend --acceptance <AC-#>`).
275
+
274
276
  ### 🎫 `/raptor-taskstoissues`
275
277
 
276
- Converte as tarefas em issues de um tracker (GitHub via MCP, Jira via integração).
278
+ Converte as tarefas em issues de um tracker (GitHub via MCP, Jira via integração). Criar as issues é o propósito do comando, mas **escrever de volta no card pai** (comentário de resumo) é **opt-in via `--sync-jira`** — sem a flag, nada é mutado no ticket de origem.
277
279
 
278
280
  ### 🌿 `/raptor-commit-review`
279
281
 
@@ -521,6 +523,8 @@ raptor jira pull APP-1234 # importa a issue
521
523
  raptor new login --jira APP-1234 # semeia a spec a partir da issue
522
524
  ```
523
525
 
526
+ > 🔒 **Escrita no Jira é opt-in.** O `raptor new --jira` só **lê** o card. Os comandos do ciclo que poderiam escrever de volta no ticket de origem (`approve`, `implement`, `taskstoissues`) **só mutam o Jira** — comentário de lifecycle e transição opcional — quando você passa **`--sync-jira`**. Sem a flag, o Raptor apenas anuncia o que faria.
527
+
524
528
  > 📖 Como o card é capturado **por inteiro** (campos ricos, links de Figma) e redistribuído na spec — com os gates cobrando: veja [docs/jira-spec-enrichment.md](docs/jira-spec-enrichment.md).
525
529
 
526
530
  ### 🎨 Integração com Figma (design determinístico)
@@ -535,6 +539,8 @@ raptor design sync home # re-captura idempotente (nunca sobrescreve
535
539
 
536
540
  O provider **`figma-rest`** usa a REST API da Figma com um PAT (`${FIGMA_TOKEN}` no `raptor.yml`), é determinístico e roda em CI. A captura gera `design/tokens.json` (colors/typography/spacing/radii), `design/screens/<slug>.md` (frames de topo) e `design/assets-manifest.json` (assets baixados com SHA-256, guarda anti-SSRF). Quando há captura real, os prompts fazem o **flip REDISTRIBUIR**: o agente passa a *refinar e redistribuir* os artefatos em disco em vez de "olhar o Figma" — cobrado pelo `gate.design.ready`.
537
541
 
542
+ A captura no `raptor new` é **opt-in via `--design-fetch`** (default ligado): com o Figma conectado, ela puxa tokens/telas/assets e semeia `design/`; com `--no-design-fetch` apenas scaffolda o link, sem buscar. O **escopo** é controlado por `--design-scope`: `node` (default) captura **só o frame linkado** (o node-id na URL); `file` é uma escotilha de fuga que puxa o **arquivo Figma inteiro** (comportamento legado). O mesmo `--design-scope` vale para `raptor design sync`.
543
+
538
544
  > 🎯 **Fidelidade visual:** depois do `implement`, um loop garante que a tela **replica** o Figma (anatomia + nota por micro-região + `gate.design.fidelity`) — veja [🎯 Fidelidade visual (Figma → React Native)](#-fidelidade-visual-figma--react-native).
539
545
 
540
546
  > 📖 Detalhes, providers e limitações: [docs/design-figma-sync.md](docs/design-figma-sync.md) e [docs/design-gate.md](docs/design-gate.md).
@@ -557,7 +563,7 @@ O provider **`figma-rest`** usa a REST API da Figma com um PAT (`${FIGMA_TOKEN}`
557
563
  🎨 Figma/Design design connect · status · pull · sync · disconnect
558
564
  ```
559
565
 
560
- Rode `raptor <comando> --help` para os detalhes (flags, exemplos) de cada um.
566
+ Rode `raptor <comando> --help` para os detalhes (flags, exemplos) de cada um, ou veja a **[📖 Referência de flags (docs/cli-reference.md)](docs/cli-reference.md)** — flags relevantes e novas da v0.15.0, por comando, com defaults.
561
567
 
562
568
  ### 💬 Quando você erra um comando
563
569
 
@@ -622,6 +628,7 @@ Comece pelo **[📖 Glossário Canônico](docs/glossary.md)** — a referência
622
628
  | Documento | Sobre |
623
629
  | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
624
630
  | **[📖 docs/glossary.md](docs/glossary.md)** | Glossário canônico: conceitos, hierarquias, catálogo de 48 gates e de comandos CLI |
631
+ | [📖 docs/cli-reference.md](docs/cli-reference.md) | Referência de flags por comando (novas/relevantes da v0.15.0) com defaults |
625
632
  | [🧭 docs/setup-wizard.md](docs/setup-wizard.md) | Assistente de instalação `raptor setup`: onboarding, IA/IDE, preset, Jira/Figma |
626
633
  | [🛡️ docs/readiness-gates.md](docs/readiness-gates.md) | Gates de prontidão (`spec/plan/tasks.ready`) em detalhe |
627
634
  | [🎨 docs/design-figma-sync.md](docs/design-figma-sync.md) | Captura determinística do Figma (`figma-rest`): tokens/telas/assets/fontes + flip REDISTRIBUIR |
@@ -1,6 +1,7 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { hashString, hashFile } from "../audit/hash.js";
4
+ import { constitutionHashFromContent } from "../constitution/core.js";
4
5
  import { wrapSpec, wrapUntrusted } from "./prompt-markers.js";
5
6
  const PHASE_MAP = {
6
7
  new: {
@@ -80,14 +81,14 @@ const PHASE_MAP = {
80
81
  "2. Extraia do Figma os valores REAIS do nó com `get_design_context` / `get_variable_defs` /",
81
82
  " `get_screenshot`. REGRA DE OURO: o nó vence a percepção — nunca chute px/cor/raio/peso.",
82
83
  "3. Capture o app RODANDO (evidência, não invente): rode",
83
- " `scripts/app/screenshot.sh design/visual-review/_shots/<tela>.<estado>.app.png` e salve o",
84
+ " `.raptor/scripts/app/screenshot.sh design/visual-review/_shots/<tela>.<estado>.app.png` e salve o",
84
85
  " screenshot do Figma como `…<estado>.figma.png`.",
85
86
  "4. Compare Figma×app POR MICRO-REGIÃO e pontue 60–100 em cada dimensão",
86
87
  " (layout, spacing, color, typography, dimensions, icon, states). A nota da região = a",
87
88
  " dimensão MAIS FRACA (o elo). Mínimo de aprovação = 95 (ou o threshold do preset).",
88
- "5. Backbone determinístico (não falsifique): rode `node scripts/review/hex-scan.mjs src --json`",
89
+ "5. Backbone determinístico (não falsifique): rode `node .raptor/scripts/review/hex-scan.mjs src --json`",
89
90
  " e preencha `deterministic.hex_hardcoded` com o resultado REAL; rode",
90
- " `node scripts/review/diff-tokens.mjs <figma-tokens.json> design/tokens.json --json` p/ o drift.",
91
+ " `node .raptor/scripts/review/diff-tokens.mjs <figma-tokens.json> design/tokens.json --json` p/ o drift.",
91
92
  "6. Registre cada divergência `{ dimension, figma, rn, fix, severity, status }`. `status`:",
92
93
  " `resolved` (corrigida), `open` (ainda falha) ou `design-decision` (desvio semântico —",
93
94
  " troca de ícone/cor/copy — que EXIGE aprovação humana antes de passar o gate).",
@@ -158,6 +159,9 @@ const PHASE_MAP = {
158
159
  "6. Se a feature tiver design (pasta `design/`): consuma os assets já catalogados em",
159
160
  " `design/assets-manifest.json` (baixados em `assets/{images,icons,fonts}`) e baixe via",
160
161
  " Figma MCP só o que faltar; referencie por caminho concreto (tags `[screen: X]` / `[asset: X]`).",
162
+ "7. PRESERVE o frontmatter existente de `tasks.md` ao reescrevê-lo — em especial o",
163
+ " campo `plan_hash` (e qualquer outro já presente). NÃO o remova nem o reescreva:",
164
+ " o gate de staleness o usa para detectar `tasks.md` desatualizado. (NÃO NEGOCIÁVEL)",
161
165
  ].join("\n"),
162
166
  },
163
167
  implement: {
@@ -215,6 +219,13 @@ const PHASE_MAP = {
215
219
  "",
216
220
  "### Saída esperada:",
217
221
  'Um arquivo JSON contendo um array de objetos `{ "label": string, "answer": string }`.',
222
+ "",
223
+ "### Regras de escrita (NÃO NEGOCIÁVEL):",
224
+ "1. Produza APENAS `answers.json`. NÃO edite `spec.md` — nem a seção",
225
+ " `## Clarifications` nem os marcadores inline.",
226
+ "2. A escrita no spec é determinística e exclusiva do CLI (`raptor clarify --apply`):",
227
+ " ele resolve os marcadores e popula `## Clarifications` a partir do seu `answers.json`.",
228
+ " Editar o spec aqui causaria dupla-escrita e regrediria o determinismo.",
218
229
  ].join("\n"),
219
230
  },
220
231
  analyze: {
@@ -274,7 +285,10 @@ export function buildCanonicalPrompt(input) {
274
285
  ? join(raptorDir, "constitution.md")
275
286
  : join(featureDir, inp.filename);
276
287
  if (existsSync(path)) {
277
- inputs.push({ path: inp.filename, hash: hashFile(path), role: inp.role });
288
+ const hash = inp.filename === "constitution.md"
289
+ ? constitutionHashFromContent(readFileSync(path, "utf8"))
290
+ : hashFile(path);
291
+ inputs.push({ path: inp.filename, hash, role: inp.role });
278
292
  }
279
293
  }
280
294
  let body = phase.bodyTemplate;
@@ -57,15 +57,25 @@ export function nextSequence(jsonlPath) {
57
57
  }
58
58
  return lines.length + 1;
59
59
  }
60
+ export function lastEventHash(jsonlPath) {
61
+ if (!existsSync(jsonlPath))
62
+ return null;
63
+ const trimmed = readFileSync(jsonlPath, "utf8").trimEnd();
64
+ if (trimmed.length === 0)
65
+ return null;
66
+ const lines = trimmed.split("\n");
67
+ return hashString(lines[lines.length - 1]);
68
+ }
60
69
  function stableStringify(obj) {
61
70
  if (obj === null || typeof obj !== "object")
62
71
  return JSON.stringify(obj);
63
72
  if (Array.isArray(obj))
64
- return "[" + obj.map(stableStringify).join(",") + "]";
65
- const keys = Object.keys(obj).sort();
66
- const parts = keys.map((k) => JSON.stringify(k) +
67
- ":" +
68
- stableStringify(obj[k]));
73
+ return ("[" +
74
+ obj.map((v) => (v === undefined ? "null" : stableStringify(v))).join(",") +
75
+ "]");
76
+ const o = obj;
77
+ const keys = Object.keys(o).filter((k) => o[k] !== undefined).sort();
78
+ const parts = keys.map((k) => JSON.stringify(k) + ":" + stableStringify(o[k]));
69
79
  return "{" + parts.join(",") + "}";
70
80
  }
71
81
  export function appendAuditEvent(jsonlPath, event) {
@@ -75,6 +85,7 @@ export function appendAuditEvent(jsonlPath, event) {
75
85
  id: id ?? uuidv7(),
76
86
  sequence: sequence ?? nextSequence(jsonlPath),
77
87
  ts: ts ?? new Date().toISOString(),
88
+ prev_hash: lastEventHash(jsonlPath),
78
89
  ...rest,
79
90
  };
80
91
  assertC5(full);
@@ -27,6 +27,10 @@ export const auditEventSchema = {
27
27
  format: "date-time",
28
28
  description: "ISO 8601 with timezone, millisecond precision recommended.",
29
29
  },
30
+ prev_hash: {
31
+ type: ["string", "null"],
32
+ description: "sha256 of the previous event's serialized line — a tamper-evident chain; null for the genesis event. Optional for back-compat with pre-chain logs.",
33
+ },
30
34
  event: {
31
35
  type: "string",
32
36
  enum: [
@@ -34,6 +38,7 @@ export const auditEventSchema = {
34
38
  "feature.archived",
35
39
  "spec.generated",
36
40
  "spec.updated",
41
+ "spec.approved",
37
42
  "clarify.added",
38
43
  "clarify.scanned",
39
44
  "clarify.applied",
@@ -48,6 +53,7 @@ export const auditEventSchema = {
48
53
  "checklist.generated",
49
54
  "checklist.created",
50
55
  "task.completed",
56
+ "implement.drift_detected",
51
57
  "gate.evaluated",
52
58
  "gate.skipped",
53
59
  "gate.approved",
@@ -152,6 +158,10 @@ export const auditEventSchema = {
152
158
  if: { properties: { event: { const: "code.generated" } } },
153
159
  then: { required: ["outputs", "task"] },
154
160
  },
161
+ {
162
+ if: { properties: { event: { const: "implement.drift_detected" } } },
163
+ then: { required: ["outputs", "detector"] },
164
+ },
155
165
  {
156
166
  if: {
157
167
  properties: {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { Ajv2020, } from "ajv/dist/2020.js";
3
3
  import { auditEventSchema } from "./schema.js";
4
+ import { hashString } from "./hash.js";
4
5
  let cachedValidator = null;
5
6
  function getValidator() {
6
7
  if (cachedValidator)
@@ -75,6 +76,7 @@ export function validateAuditFile(jsonlPath, opts = {}) {
75
76
  let validLines = 0;
76
77
  let prevSequence = 0;
77
78
  let prevTs = "";
79
+ let prevRaw = null;
78
80
  for (let i = 0; i < lines.length; i++) {
79
81
  const lineNum = i + 1;
80
82
  const raw = lines[i];
@@ -87,6 +89,8 @@ export function validateAuditFile(jsonlPath, opts = {}) {
87
89
  });
88
90
  continue;
89
91
  }
92
+ const prevLine = prevRaw;
93
+ prevRaw = raw;
90
94
  let parsed;
91
95
  try {
92
96
  parsed = JSON.parse(raw);
@@ -125,6 +129,17 @@ export function validateAuditFile(jsonlPath, opts = {}) {
125
129
  keyword: "monotonicTs",
126
130
  });
127
131
  }
132
+ if (obj.prev_hash !== undefined) {
133
+ const expected = prevLine === null ? null : hashString(prevLine);
134
+ if (obj.prev_hash !== expected) {
135
+ errors.push({
136
+ line: lineNum,
137
+ path: "/prev_hash",
138
+ message: `prev_hash chain broken — expected ${expected ?? "null (genesis)"}, got ${obj.prev_hash ?? "null"}`,
139
+ keyword: "chain",
140
+ });
141
+ }
142
+ }
128
143
  prevSequence = obj.sequence;
129
144
  prevTs = obj.ts;
130
145
  }
@@ -89,6 +89,10 @@ export function extractCoreBlock(source) {
89
89
  endIndex: endMarkerIdx + CORE_END_MARKER.length,
90
90
  };
91
91
  }
92
+ export function constitutionHashFromContent(content) {
93
+ const extracted = extractCoreBlock(content);
94
+ return extracted ? extracted.declaredHash : hashString(content);
95
+ }
92
96
  export function rewriteCoreBlock(source) {
93
97
  const canonical = coreBlock();
94
98
  const extracted = extractCoreBlock(source);
@@ -1 +1 @@
1
- export { CORE_VERSION, CORE_ARTICLES_TEXT, CORE_END_MARKER, CORE_START_MARKER_PREFIX, CORE_START_MARKER_RE, coreBlock, coreHash, extractCoreBlock, rewriteCoreBlock, } from "./core.js";
1
+ export { CORE_VERSION, CORE_ARTICLES_TEXT, CORE_END_MARKER, CORE_START_MARKER_PREFIX, CORE_START_MARKER_RE, coreBlock, coreHash, constitutionHashFromContent, extractCoreBlock, rewriteCoreBlock, } from "./core.js";
@@ -7,6 +7,7 @@ export function normalizeImagesToAssets(renders, fills, opts = {}) {
7
7
  const renderType = classifyAssetType(fmt);
8
8
  const renderExt = extForFormat(fmt);
9
9
  const renderDir = assetDirFor(renderType);
10
+ const screen = opts.screen?.trim() ? opts.screen.trim() : undefined;
10
11
  for (const [nodeId, url] of Object.entries(renders)) {
11
12
  if (!url)
12
13
  continue;
@@ -18,7 +19,14 @@ export function normalizeImagesToAssets(renders, fills, opts = {}) {
18
19
  path = `${renderDir}/${base}.${renderExt}`;
19
20
  }
20
21
  seen.add(path);
21
- out.push({ name: human || base, type: renderType, path, url, figma_node_id: nodeId });
22
+ out.push({
23
+ name: human || base,
24
+ type: renderType,
25
+ path,
26
+ url,
27
+ figma_node_id: nodeId,
28
+ ...(screen ? { screen } : {}),
29
+ });
22
30
  }
23
31
  for (const [ref, url] of Object.entries(fills)) {
24
32
  if (!url)
@@ -31,7 +39,7 @@ export function normalizeImagesToAssets(renders, fills, opts = {}) {
31
39
  path = `assets/images/${base}.${ext}`;
32
40
  }
33
41
  seen.add(path);
34
- out.push({ name: base, type: "image", path, url });
42
+ out.push({ name: base, type: "image", path, url, ...(screen ? { screen } : {}) });
35
43
  }
36
44
  return out;
37
45
  }
@@ -1,3 +1,4 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import { createHash } from "node:crypto";
2
3
  import { mkdirSync, writeFileSync } from "node:fs";
3
4
  import { dirname, isAbsolute, join, resolve, sep } from "node:path";
@@ -64,16 +65,31 @@ export async function downloadAll(pending, root, opts = {}) {
64
65
  if (isAbsolute(rel) || (within !== rootAbs && !within.startsWith(rootAbs + sep))) {
65
66
  throw new DownloadError(`asset path escapes project root: ${a.path}`, 0);
66
67
  }
68
+ if (isGitTracked(rootAbs, rel)) {
69
+ const w = `asset "${a.name}" colidiria com arquivo versionado ${rel} — não sobrescrito`;
70
+ if (opts.onWarn)
71
+ opts.onWarn(w);
72
+ else
73
+ process.stderr.write(`Warning: ${w}\n`);
74
+ results[i] = { ...base, status: "pending" };
75
+ continue;
76
+ }
67
77
  const destAbs = join(root, rel);
68
78
  const { hash } = await downloadAsset(a.url, destAbs, opts);
69
79
  results[i] = { ...base, hash, status: "downloaded" };
70
80
  }
71
81
  catch (err) {
72
82
  const msg = err instanceof Error ? err.message : String(err);
83
+ const where = assetWhere(a);
84
+ const oversized = err instanceof DownloadError && /exceeds max_bytes/.test(msg);
85
+ const advice = oversized
86
+ ? " — pending, fora do manifest; eleve design.assets.max_bytes ou rebaixe via MCP"
87
+ : "";
88
+ const line = `asset "${a.name}"${where} not downloaded: ${msg}${advice}`;
73
89
  if (opts.onWarn)
74
- opts.onWarn(`asset "${a.name}" not downloaded: ${msg}`);
90
+ opts.onWarn(line);
75
91
  else
76
- process.stderr.write(`Warning: asset "${a.name}" not downloaded: ${msg}\n`);
92
+ process.stderr.write(`Warning: ${line}\n`);
77
93
  results[i] = { ...base, status: "pending" };
78
94
  }
79
95
  }
@@ -81,6 +97,26 @@ export async function downloadAll(pending, root, opts = {}) {
81
97
  await Promise.all(Array.from({ length: Math.min(concurrency, pending.length) }, worker));
82
98
  return results;
83
99
  }
100
+ function isGitTracked(rootAbs, rel) {
101
+ try {
102
+ const r = spawnSync("git", ["ls-files", "--error-unmatch", "--", rel], {
103
+ cwd: rootAbs,
104
+ stdio: "ignore",
105
+ });
106
+ return r.status === 0;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ function assetWhere(a) {
113
+ const parts = [];
114
+ if (a.screen)
115
+ parts.push(`screen: ${a.screen}`);
116
+ if (a.figma_node_id)
117
+ parts.push(`node ${a.figma_node_id}`);
118
+ return parts.length ? ` (${parts.join(", ")})` : "";
119
+ }
84
120
  function assertSafeAssetUrl(url, allowHttp) {
85
121
  let u;
86
122
  try {