@spec-wave/cli 0.26.0 → 0.27.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 (34) hide show
  1. package/package.json +1 -1
  2. package/src/api/github-rest.mjs +25 -0
  3. package/src/commands/decompose.mjs +166 -39
  4. package/src/commands/doctor.mjs +190 -3
  5. package/src/commands/generate-bug.mjs +22 -16
  6. package/src/commands/generate-plan.mjs +72 -23
  7. package/src/commands/generate-spec.mjs +19 -15
  8. package/src/commands/implement.mjs +40 -20
  9. package/src/commands/run.mjs +51 -30
  10. package/src/commands/validate.mjs +84 -17
  11. package/src/config.mjs +18 -0
  12. package/src/lib/artifact-pr.mjs +272 -0
  13. package/src/lib/artifact-publish.mjs +169 -0
  14. package/src/lib/doc-availability.mjs +23 -1
  15. package/src/lib/doc-source.mjs +162 -0
  16. package/src/lib/flow-run.mjs +9 -218
  17. package/src/lib/next-step.mjs +27 -4
  18. package/src/lib/pr-branch.mjs +10 -0
  19. package/src/lib/repo-links.mjs +8 -2
  20. package/src/plugin/.claude-plugin/plugin.json +1 -1
  21. package/src/plugin/skills/bug/SKILL.md +2 -2
  22. package/src/plugin/skills/decompose/SKILL.md +4 -4
  23. package/src/plugin/skills/plan/SKILL.md +1 -1
  24. package/src/plugin/skills/run/SKILL.md +3 -1
  25. package/src/plugin/skills/spec/SKILL.md +4 -4
  26. package/src/plugin/skills/workflow/SKILL.md +2 -2
  27. package/src/templates/skill/SKILL.md +7 -7
  28. package/src/templates/workflows/code-review.yml +13 -2
  29. package/src/templates/workflows/critique.yml +1 -1
  30. package/src/templates/workflows/decompose.yml +13 -2
  31. package/src/templates/workflows/generate-bug.yml +17 -6
  32. package/src/templates/workflows/generate-plan.yml +20 -7
  33. package/src/templates/workflows/generate-spec.yml +20 -7
  34. package/src/templates/workflows/qa.yml +13 -0
@@ -31,7 +31,7 @@ Toda a CLI é invocada como `npx @spec-wave/cli@latest <comando>`.
31
31
  - **Action:** aplique a label de gatilho (`spec-wave:spec`, `spec-wave:plan`, `spec-wave:decompose`);
32
32
  - **Local:** `npx @spec-wave/cli@latest generate-spec|generate-plan|decompose --issue-number <n>` na sua sessão.
33
33
 
34
- O modo é detectado pelo ambiente (`GITHUB_ACTIONS`), sem flag. Os dois geram, commitam, fazem push, comentam na issue e sincronizam o board — o board é a fonte de verdade independentemente de onde rodou. Local exige a chave de IA no seu ambiente e um `.spec-wave.json`. Exceção à regra: revisar/melhorar um documento já gerado.
34
+ O modo é detectado pelo ambiente (`GITHUB_ACTIONS`), sem flag. Os dois geram, abrem um **Pull Request** com o documento, comentam na issue e sincronizam o board — o board é a fonte de verdade independentemente de onde rodou. Local exige a chave de IA no seu ambiente e um `.spec-wave.json`. Exceção à regra: revisar/melhorar um documento já gerado.
35
35
  2. **Nunca crie Story ou Task avulsa.** Elas nascem do `decompose`, já em **✅ Ready** e vinculadas ao pai. Criadas à mão caem em 📥 Backlog e **não aparecem em tela nenhuma** da UI (o inbox do PM lista Features, a tela do Dev lê 🚧 Desenvolvimento, a fila do TL lê ✅ Ready).
36
36
  3. **Nunca use `gh issue create`** para work items — não adiciona ao Project, a issue fica sem Etapa e some das telas. Use `npx @spec-wave/cli@latest issue`.
37
37
  4. **A Etapa só avança, nunca retrocede.** O campo **Status** (Todo / In Progress / Done) mede o progresso *dentro* da Etapa e reinicia a cada avanço. Prefira `move`, `task start|done` e `story review` a mutações GraphQL manuais — os comandos embutem essas regras.
@@ -88,7 +88,7 @@ Depois do `generate-plan` e **antes** de qualquer issue nascer no `decompose`, u
88
88
  | Reprovou depois de | O que corrigir | Como retomar |
89
89
  |--------------------|----------------|--------------|
90
90
  | `generate-plan` | `plan.md` (ou a `spec.md` que o embasa) | corrija/regere → remova `critique-failed` → reaplique `spec-wave:ready` |
91
- | `decompose` | **`decomposition.md`** — os achados citam `Story N` / `Task N.M`, títulos **desse arquivo** | edite e commite → remova `critique-failed` → reaplique `spec-wave:decompose` (critica o arquivo como está, sem regerar) |
91
+ | `decompose` | **`decomposition.md`** — os achados citam `Story N` / `Task N.M`, títulos **desse arquivo** | edite no Pull Request → remova `critique-failed` → reaplique `spec-wave:decompose` (critica o arquivo como está, sem regerar) |
92
92
 
93
93
  > ⚠️ No `decompose` os achados são sobre as **Stories propostas**, não sobre o `plan.md`. Corrigir o plan não muda o rascunho já gravado.
94
94
 
@@ -86,7 +86,7 @@ O `run` decide o passo pelo estado da issue (documentos existentes + labels), re
86
86
 
87
87
  ## Regra fundamental
88
88
 
89
- **Nunca gere `spec.md` ou `plan.md` diretamente.** Acione o passo — a label (modo actions) ou o `run` (modo local) — e deixe o spec-wave gerar o arquivo. Isso garante que o arquivo seja commitado no repositório e referenciado na issue.
89
+ **Nunca gere `spec.md` ou `plan.md` diretamente.** Acione o passo — a label (modo actions) ou o `run` (modo local) — e deixe o spec-wave gerar o arquivo. Isso garante que o arquivo chegue à main por **Pull Request** e seja referenciado na issue.
90
90
 
91
91
  Exceção: se o usuário pedir explicitamente para revisar ou melhorar um documento já gerado, use o Write tool para editar o arquivo local.
92
92
 
@@ -294,7 +294,7 @@ A saída da crítica é **estruturada e validada por schema**: `severity` só ac
294
294
  | Reprovou depois de | O que corrigir | Como retomar |
295
295
  |--------------------|----------------|--------------|
296
296
  | `generate-plan` | `plan.md` (ou a `spec.md` que o embasa) | corrija → remova `spec-wave:critique-failed` → reaplique **`spec-wave:critique`** (re-critica o arquivo como está). `spec-wave:ready` apenas valida, **não** re-critica; e `spec-wave:plan` **regera** o plano, descartando a correção. |
297
- | `decompose` (rascunho) | **`decomposition.md`** — os achados citam **`Story N`** e **`Task N.M`**, que são os títulos desse arquivo | edite o arquivo e commite → remova `spec-wave:critique-failed` → reaplique `spec-wave:decompose` (ele **critica o arquivo como está**, sem regerar) |
297
+ | `decompose` (rascunho) | **`decomposition.md`** — os achados citam **`Story N`** e **`Task N.M`**, que são os títulos desse arquivo | edite o arquivo **no Pull Request** → remova `spec-wave:critique-failed` → reaplique `spec-wave:decompose` (ele **critica o arquivo como está**, sem regerar) |
298
298
 
299
299
  > ⚠️ No contexto do `decompose`, os achados são sobre as **Stories propostas**, não sobre o `plan.md`. Corrigir o plan não influencia o rascunho já gravado — edite o `decomposition.md` diretamente. Foi exatamente essa confusão que fazia o ciclo não convergir.
300
300
 
@@ -309,7 +309,7 @@ O `decompose` **não cria issues direto**. São dois passos, com um artefato rev
309
309
 
310
310
  ```
311
311
  spec-wave:decompose
312
- ├─ decomposition.md ausente → gera via IA → commita → critica
312
+ ├─ decomposition.md ausente → gera via IA → abre Pull Request → critica
313
313
  └─ decomposition.md presente → critica o arquivo COMO ESTÁ (preserva suas edições)
314
314
  ├─ grave → +critique-failed, comentário citando Story N / Task N.M, exit 1
315
315
  └─ limpo → +decompose-ready, comentário "revise e aplique"
@@ -341,7 +341,7 @@ Corpo técnico.
341
341
  - a **posição** manda, não o número escrito — inserir uma Story no meio sem renumerar funciona;
342
342
  - `**Depende de:**` aceita **irmãs** (`Story 1, Story 3`, 1-based, só para trás — apontar para si mesma ou para frente é **erro**, não filtro silencioso) e **issues de outras Features** (`#412`, que precisam JÁ existir); as duas formas convivem na mesma linha (`Story 1, #412`), e `—` significa nenhuma;
343
343
  - o corpo aceita markdown livre (`## Backend`, cercas de código) — só `## Story N` e `### Task N.M` são estrutura;
344
- - **para gerar outro rascunho do zero:** apague o arquivo e reaplique `spec-wave:decompose`.
344
+ - **para gerar outro rascunho do zero:** feche o Pull Request e apague a branch `spec-wave/<n>-decompose` (ou apague o arquivo, se já mergeado) e reaplique `spec-wave:decompose`.
345
345
 
346
346
  ### Guard de idempotência do decompose (`spec-wave:decomposed`)
347
347
 
@@ -649,10 +649,10 @@ Para qualquer outro tipo (Spike, Bug, Story, Task, …) o Action **recusa** e co
649
649
  ```bash
650
650
  gh issue edit <número> --add-label "spec-wave:decompose"
651
651
  ```
652
- Informe: "Rascunho iniciado — vai commitar `decomposition.md` e passar pela crítica. **Nenhuma issue é criada nesta etapa.**"
652
+ Informe: "Rascunho iniciado — vai abrir um Pull Request com o `decomposition.md` e passar pela crítica. **Nenhuma issue é criada nesta etapa.**"
653
653
  3. Quando o Action terminar, leia o comentário na issue:
654
654
  - **`spec-wave:decompose-ready`** → o rascunho passou pela crítica. **Leia o `decomposition.md`** e mostre ao usuário o que será criado (Stories, Tasks, dependências). Ofereça editar o arquivo antes de aplicar.
655
- - **`spec-wave:critique-failed`** → o Action **falhou (exit 1)** e nada foi criado. Os achados citam `Story N` / `Task N.M` do `decomposition.md`. **Corrija esse arquivo** (não o `plan.md`), commite e reaplique `spec-wave:decompose` — o arquivo é criticado como está, sem ser regerado.
655
+ - **`spec-wave:critique-failed`** → o Action **falhou (exit 1)** e nada foi criado. Os achados citam `Story N` / `Task N.M` do `decomposition.md`. **Corrija esse arquivo** (não o `plan.md`) no Pull Request e reaplique `spec-wave:decompose` — o arquivo é criticado como está, sem ser regerado.
656
656
  - **`spec-wave:needs-human`** → a crítica esgotou as tentativas. Pare e envolva o usuário: as duas labels precisam sair à mão.
657
657
  4. **Etapa 2 — aplicar o rascunho aprovado** (só depois da revisão):
658
658
  ```bash
@@ -728,7 +728,7 @@ Gera o **`bug.md`** de um defeito: `docs/bugs/<slug>/bug.md`, com reprodução,
728
728
 
729
729
  1. Confirme que a issue é do tipo **Bug** (para outros tipos o Action pula, remove a label e comenta).
730
730
  2. `gh issue edit <n> --add-label "spec-wave:bug"`
731
- 3. Avise: o Action `generate-bug.yml` gera e commita o arquivo.
731
+ 3. Avise: o Action `generate-bug.yml` gera o arquivo e abre um Pull Request com ele.
732
732
  4. Ofereça revisar **duas seções**: **Causa Raiz** e **Teste de Regressão**. São elas que decidem se a correção ataca o defeito ou o sintoma.
733
733
  5. Validar: `spec-wave:ready` → confere as seis seções e aplica `spec-wave:bug-approved`.
734
734
 
@@ -18,7 +18,15 @@ jobs:
18
18
  # Modo de execução: `spec-wave mode local` cria a variável de repositório
19
19
  # SPEC_WAVE_EXECUTION=local e este job passa a ser PULADO — run aparece como
20
20
  # skipped e job que não roda não é faturado. `mode actions` remove a variável.
21
- if: vars.SPEC_WAVE_EXECUTION != 'local'
21
+ # PR de DOCUMENTO não é PR de implementação. O fluxo publica spec.md,
22
+ # plan.md, bug.md e decomposition.md em branches `spec-wave/*`, e deixar este
23
+ # workflow rodar em cima delas move o board por um PR que não implementa
24
+ # nada. Nos Actions um PR aberto pelo GITHUB_TOKEN não dispara workflow, mas
25
+ # um aberto no modo local (ou via GH_PR_TOKEN) dispara.
26
+ #
27
+ # A guarda cobre também `spec-wave/update-v*`, do `update --branch` — que
28
+ # tampouco implementa uma Story.
29
+ if: vars.SPEC_WAVE_EXECUTION != 'local' && !startsWith(github.event.pull_request.head.ref, 'spec-wave/')
22
30
  runs-on: ubuntu-latest
23
31
  permissions:
24
32
  issues: read
@@ -44,7 +52,10 @@ jobs:
44
52
  move:
45
53
  needs: resolve
46
54
  # PR sem vínculo explícito não resolve Feature nenhuma — e não move nada.
47
- if: vars.SPEC_WAVE_EXECUTION != 'local' && needs.resolve.outputs.feature != ''
55
+ if: >
56
+ vars.SPEC_WAVE_EXECUTION != 'local' &&
57
+ !startsWith(github.event.pull_request.head.ref, 'spec-wave/') &&
58
+ needs.resolve.outputs.feature != ''
48
59
  runs-on: ubuntu-latest
49
60
  # MESMO namespace do decompose.yml: um `decompose-apply` em curso na Feature
50
61
  # segura este job na fila em vez de disputar a subárvore com ele. Foi essa
@@ -35,7 +35,7 @@ jobs:
35
35
  permissions:
36
36
  issues: write
37
37
  # read: a crítica LÊ o plan.md do checkout e não escreve arquivo nenhum —
38
- # ao contrário do generate-plan, que commita o documento gerado.
38
+ # ao contrário do generate-plan, que publica o documento gerado.
39
39
  contents: read
40
40
 
41
41
  steps:
@@ -16,7 +16,7 @@ on:
16
16
  # proteção.
17
17
  #
18
18
  # generate-spec, generate-plan e generate-bug usam o MESMO namespace por outro
19
- # motivo: os quatro commitam e empurram na branch default, e este aqui ainda lê
19
+ # motivo: os quatro publicam documentos em branch própria + Pull Request, e este aqui ainda lê
20
20
  # o spec.md/plan.md do checkout — que uma geração concorrente estaria trocando
21
21
  # debaixo dele.
22
22
  concurrency:
@@ -56,8 +56,18 @@ jobs:
56
56
  runs-on: ubuntu-latest
57
57
  permissions:
58
58
  issues: write
59
- # A etapa de rascunho commita docs/**/decomposition.md no branch default.
60
59
  contents: write
60
+ # `contents: write` continua necessário: o commit vai por Git Data API
61
+ # (createTree/createCommit/createRef), que é autorizada por ele. O que
62
+ # mudou foi o DESTINO — branch própria do documento, nunca a default.
63
+ #
64
+ # `pull-requests: write` é a permissão NOVA deste fluxo. Sem ela o commit
65
+ # é criado e o PR não, e o documento fica numa branch que ninguém vê.
66
+ #
67
+ # Atenção: o GITHUB_TOKEN só abre PR se "Allow GitHub Actions to create and
68
+ # approve pull requests" estiver ligado (Settings → Actions → General) —
69
+ # desligado por padrão em muitas organizações. `GH_PR_TOKEN` é a saída.
70
+ pull-requests: write
61
71
 
62
72
  steps:
63
73
  - uses: actions/checkout@v4
@@ -78,6 +88,7 @@ jobs:
78
88
  ${{ github.event.label.name == 'spec-wave:decompose-apply' && '--apply' || '' }}
79
89
  env:
80
90
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
91
+ GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}
81
92
  PROJECT_TOKEN: ${{ secrets.GH_PROJECT_TOKEN }}
82
93
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
83
94
  CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
@@ -5,12 +5,11 @@ on:
5
5
  types: [labeled]
6
6
 
7
7
  # Trava por ITEM, no mesmo namespace de generate-spec/plan/decompose e do job
8
- # `move` do code-review.yml. Este job GRAVA no repositório (commit + pull
9
- # --rebase + push na branch default) e escreve o relatório de uso no comentário
10
- # da issue com leitura-modificação-escrita; a trava impede que outro job do
11
- # spec-wave na MESMA issue faça as duas coisas ao mesmo tempo. Entre issues
12
- # diferentes a disputa é resolvida no laço de repetição de push do
13
- # lib/flow-run.mjs — serializar o repositório inteiro mataria a vazão.
8
+ # `move` do code-review.yml. Este job PUBLICA um documento (branch própria +
9
+ # Pull Request) e escreve o relatório de uso no comentário da issue com
10
+ # leitura-modificação-escrita; a trava impede que outro job do spec-wave na
11
+ # MESMA issue faça as duas coisas ao mesmo tempo. Entre issues diferentes não há
12
+ # mais disputa: cada documento tem a sua branch.
14
13
  concurrency:
15
14
  # Um run que NÃO vai fazer nada não pode disputar este grupo.
16
15
  #
@@ -42,6 +41,17 @@ jobs:
42
41
  permissions:
43
42
  issues: write
44
43
  contents: write
44
+ # `contents: write` continua necessário: o commit vai por Git Data API
45
+ # (createTree/createCommit/createRef), que é autorizada por ele. O que
46
+ # mudou foi o DESTINO — branch própria do documento, nunca a default.
47
+ #
48
+ # `pull-requests: write` é a permissão NOVA deste fluxo. Sem ela o commit
49
+ # é criado e o PR não, e o documento fica numa branch que ninguém vê.
50
+ #
51
+ # Atenção: o GITHUB_TOKEN só abre PR se "Allow GitHub Actions to create and
52
+ # approve pull requests" estiver ligado (Settings → Actions → General) —
53
+ # desligado por padrão em muitas organizações. `GH_PR_TOKEN` é a saída.
54
+ pull-requests: write
45
55
 
46
56
  steps:
47
57
  - uses: actions/checkout@v4
@@ -59,6 +69,7 @@ jobs:
59
69
  run: spec-wave generate-bug --issue-number ${{ github.event.issue.number }}
60
70
  env:
61
71
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72
+ GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}
62
73
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
63
74
  CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
64
75
  OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
@@ -7,18 +7,19 @@ on:
7
7
  # Trava por ITEM, no mesmo namespace do decompose.yml e do job `move` do
8
8
  # code-review.yml. Duas razões:
9
9
  #
10
- # 1. spec, plan e decompose GRAVAM no repositório (commit + pull --rebase +
11
- # push na branch default). Em namespaces separados, aplicar `spec-wave:spec`
12
- # e `spec-wave:plan` juntos na mesma Feature põe os dois para rodar em
13
- # paralelo — e o plan lê o spec.md do checkout, então pode gerar o plano
14
- # sem a spec, em silêncio (generate-plan.mjs:160-169).
10
+ # 1. spec, plan e decompose PUBLICAM documentos (branch própria + Pull
11
+ # Request). Em namespaces separados, aplicar `spec-wave:spec` e
12
+ # `spec-wave:plan` juntos na mesma Feature põe os dois para rodar em
13
+ # paralelo — e o plan lê a spec, que ainda estaria num PR sem merge.
14
+ # Hoje isso é recusa explícita, não plano gerado sem spec; ainda assim,
15
+ # serializar evita queimar um run para descobrir isso.
15
16
  # 2. Os dois escrevem o relatório de uso no MESMO comentário da issue, com
16
17
  # leitura-modificação-escrita (usage-report.mjs:152-160). Em paralelo, um
17
18
  # sobrescreve o custo registrado pelo outro.
18
19
  #
19
20
  # Continua sendo por issue de propósito: serializar o repositório inteiro
20
- # mataria a vazão, e a disputa entre issues diferentes é resolvida onde ela
21
- # acontece no laço de repetição de push do lib/flow-run.mjs.
21
+ # mataria a vazão. A disputa ENTRE issues diferentes deixou de existir — cada
22
+ # documento tem branch própria, e o commit vai por API.
22
23
  concurrency:
23
24
  # Um run que NÃO vai fazer nada não pode disputar este grupo.
24
25
  #
@@ -50,6 +51,17 @@ jobs:
50
51
  permissions:
51
52
  issues: write
52
53
  contents: write
54
+ # `contents: write` continua necessário: o commit vai por Git Data API
55
+ # (createTree/createCommit/createRef), que é autorizada por ele. O que
56
+ # mudou foi o DESTINO — branch própria do documento, nunca a default.
57
+ #
58
+ # `pull-requests: write` é a permissão NOVA deste fluxo. Sem ela o commit
59
+ # é criado e o PR não, e o documento fica numa branch que ninguém vê.
60
+ #
61
+ # Atenção: o GITHUB_TOKEN só abre PR se "Allow GitHub Actions to create and
62
+ # approve pull requests" estiver ligado (Settings → Actions → General) —
63
+ # desligado por padrão em muitas organizações. `GH_PR_TOKEN` é a saída.
64
+ pull-requests: write
53
65
 
54
66
  steps:
55
67
  - uses: actions/checkout@v4
@@ -67,6 +79,7 @@ jobs:
67
79
  run: spec-wave generate-plan --issue-number ${{ github.event.issue.number }}
68
80
  env:
69
81
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
+ GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}
70
83
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
71
84
  CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
72
85
  OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
@@ -7,18 +7,19 @@ on:
7
7
  # Trava por ITEM, no mesmo namespace do decompose.yml e do job `move` do
8
8
  # code-review.yml. Duas razões:
9
9
  #
10
- # 1. spec, plan e decompose GRAVAM no repositório (commit + pull --rebase +
11
- # push na branch default). Em namespaces separados, aplicar `spec-wave:spec`
12
- # e `spec-wave:plan` juntos na mesma Feature põe os dois para rodar em
13
- # paralelo — e o plan lê o spec.md do checkout, então pode gerar o plano
14
- # sem a spec, em silêncio (generate-plan.mjs:160-169).
10
+ # 1. spec, plan e decompose PUBLICAM documentos (branch própria + Pull
11
+ # Request). Em namespaces separados, aplicar `spec-wave:spec` e
12
+ # `spec-wave:plan` juntos na mesma Feature põe os dois para rodar em
13
+ # paralelo — e o plan lê a spec, que ainda estaria num PR sem merge.
14
+ # Hoje isso é recusa explícita, não plano gerado sem spec; ainda assim,
15
+ # serializar evita queimar um run para descobrir isso.
15
16
  # 2. Os dois escrevem o relatório de uso no MESMO comentário da issue, com
16
17
  # leitura-modificação-escrita (usage-report.mjs:152-160). Em paralelo, um
17
18
  # sobrescreve o custo registrado pelo outro.
18
19
  #
19
20
  # Continua sendo por issue de propósito: serializar o repositório inteiro
20
- # mataria a vazão, e a disputa entre issues diferentes é resolvida onde ela
21
- # acontece no laço de repetição de push do lib/flow-run.mjs.
21
+ # mataria a vazão. A disputa ENTRE issues diferentes deixou de existir — cada
22
+ # documento tem branch própria, e o commit vai por API.
22
23
  concurrency:
23
24
  # Um run que NÃO vai fazer nada não pode disputar este grupo.
24
25
  #
@@ -50,6 +51,17 @@ jobs:
50
51
  permissions:
51
52
  issues: write
52
53
  contents: write
54
+ # `contents: write` continua necessário: o commit vai por Git Data API
55
+ # (createTree/createCommit/createRef), que é autorizada por ele. O que
56
+ # mudou foi o DESTINO — branch própria do documento, nunca a default.
57
+ #
58
+ # `pull-requests: write` é a permissão NOVA deste fluxo. Sem ela o commit
59
+ # é criado e o PR não, e o documento fica numa branch que ninguém vê.
60
+ #
61
+ # Atenção: o GITHUB_TOKEN só abre PR se "Allow GitHub Actions to create and
62
+ # approve pull requests" estiver ligado (Settings → Actions → General) —
63
+ # desligado por padrão em muitas organizações. `GH_PR_TOKEN` é a saída.
64
+ pull-requests: write
53
65
 
54
66
  steps:
55
67
  - uses: actions/checkout@v4
@@ -67,6 +79,7 @@ jobs:
67
79
  run: spec-wave generate-spec --issue-number ${{ github.event.issue.number }}
68
80
  env:
69
81
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
+ GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }}
70
83
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
71
84
  CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
72
85
  OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
@@ -13,8 +13,21 @@ jobs:
13
13
  # Modo de execução: `spec-wave mode local` cria a variável de repositório
14
14
  # SPEC_WAVE_EXECUTION=local e este job passa a ser PULADO — run aparece como
15
15
  # skipped e job que não roda não é faturado. `mode actions` remove a variável.
16
+ # PR de DOCUMENTO não é PR de implementação. O fluxo publica spec.md,
17
+ # plan.md, bug.md e decomposition.md em branches `spec-wave/*`, e deixar este
18
+ # workflow rodar em cima delas move o board por um PR que não implementa
19
+ # nada. Nos Actions um PR aberto pelo GITHUB_TOKEN não dispara workflow, mas
20
+ # um aberto no modo local (ou via GH_PR_TOKEN) dispara.
21
+ #
22
+ # A guarda cobre também `spec-wave/update-v*`, do `update --branch` — que
23
+ # tampouco implementa uma Story.
24
+ #
25
+ # Aqui a guarda é INDISPENSÁVEL, não defensiva: `pull_request_review`
26
+ # dispara por uma aprovação HUMANA, independentemente de quem abriu o
27
+ # PR. Aprovar o PR da spec moveria a Feature para 🧪 QA.
16
28
  if: >
17
29
  vars.SPEC_WAVE_EXECUTION != 'local' &&
30
+ !startsWith(github.event.pull_request.head.ref, 'spec-wave/') &&
18
31
  github.event.review.state == 'approved'
19
32
  runs-on: ubuntu-latest
20
33
  permissions: