@brunosps00/dev-workflow 1.0.6 → 1.1.1

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 (55) hide show
  1. package/README.md +51 -29
  2. package/bin/dev-workflow.js +6 -0
  3. package/lib/constants.js +2 -2
  4. package/lib/init.js +13 -3
  5. package/lib/install-deps.js +88 -3
  6. package/lib/migrate-gsd.js +1 -1
  7. package/lib/post-update.js +153 -0
  8. package/lib/setup-wsl-browser.js +149 -0
  9. package/lib/uninstall.js +1 -1
  10. package/package.json +1 -1
  11. package/scaffold/en/agent-instructions.md +1 -1
  12. package/scaffold/en/commands/dw-analyze-project.md +22 -0
  13. package/scaffold/en/commands/dw-autopilot.md +3 -1
  14. package/scaffold/en/commands/dw-functional-doc.md +11 -3
  15. package/scaffold/en/commands/dw-generate-pr.md +2 -2
  16. package/scaffold/en/commands/dw-help.md +29 -14
  17. package/scaffold/en/commands/dw-qa.md +1 -1
  18. package/scaffold/en/commands/dw-redesign-ui.md +2 -2
  19. package/scaffold/en/commands/dw-review.md +7 -5
  20. package/scaffold/en/commands/dw-secure-audit.md +72 -15
  21. package/scaffold/en/commands/dw-update.md +16 -1
  22. package/scaffold/en/templates/constitution-template.md +8 -3
  23. package/scaffold/en/templates/functional-doc/playwright.flow.mjs.tpl +17 -0
  24. package/scaffold/pt-br/agent-instructions.md +1 -1
  25. package/scaffold/pt-br/commands/dw-analyze-project.md +22 -0
  26. package/scaffold/pt-br/commands/dw-autopilot.md +3 -1
  27. package/scaffold/pt-br/commands/dw-functional-doc.md +11 -3
  28. package/scaffold/pt-br/commands/dw-generate-pr.md +2 -2
  29. package/scaffold/pt-br/commands/dw-help.md +29 -14
  30. package/scaffold/pt-br/commands/dw-qa.md +1 -1
  31. package/scaffold/pt-br/commands/dw-redesign-ui.md +2 -2
  32. package/scaffold/pt-br/commands/dw-review.md +7 -5
  33. package/scaffold/pt-br/commands/dw-secure-audit.md +72 -15
  34. package/scaffold/pt-br/commands/dw-update.md +16 -1
  35. package/scaffold/pt-br/templates/constitution-template.md +8 -3
  36. package/scaffold/pt-br/templates/functional-doc/playwright.flow.mjs.tpl +17 -0
  37. package/scaffold/scripts/cdp-relay/Cargo.toml +16 -0
  38. package/scaffold/scripts/cdp-relay/src/main.rs +49 -0
  39. package/scaffold/scripts/functional-doc/generate-dossier.mjs +7 -9
  40. package/scaffold/scripts/functional-doc/run-playwright-flow.mjs +192 -183
  41. package/scaffold/scripts/lib/capture-screenshots.mjs +133 -0
  42. package/scaffold/scripts/lib/resolve-browser.mjs +405 -0
  43. package/scaffold/scripts/lib/ui-slop-detect.mjs +102 -0
  44. package/scaffold/skills/dw-testing-discipline/references/playwright-recipes.md +50 -0
  45. package/scaffold/skills/dw-ui-discipline/SKILL.md +5 -0
  46. package/scaffold/skills/dw-ui-discipline/references/color-oklch.md +60 -0
  47. package/scaffold/skills/dw-ui-discipline/references/motion.md +52 -0
  48. package/scaffold/skills/dw-ui-discipline/references/responsive.md +52 -0
  49. package/scaffold/skills/dw-ui-discipline/references/type-scale.md +48 -0
  50. package/scaffold/skills/dw-ui-discipline/references/ux-writing.md +52 -0
  51. package/scaffold/skills/security-review/SKILL.md +19 -0
  52. package/scaffold/skills/security-review/references/sast.md +67 -0
  53. package/scaffold/skills/security-review/references/secrets.md +46 -0
  54. package/scaffold/en/templates/functional-doc/playwright.spec.ts.tpl +0 -19
  55. package/scaffold/pt-br/templates/functional-doc/playwright.spec.ts.tpl +0 -19
@@ -1,7 +1,13 @@
1
1
  <system_instructions>
2
- Você é o orquestrador de security audit. Roda OWASP static review + supply-chain CVE/secret/IaC scanning + outdated check + supply-chain compromise detection em uma passada. Hard-gates comandos downstream quando há findings CRITICAL ou HIGH.
2
+ Você é o orquestrador de security audit — o **Security Gate**. Roda OWASP static review + SAST (Semgrep,
3
+ focado no diff do código gerado) + secret scan dedicado (gitleaks) + supply-chain CVE/secret/IaC (Trivy +
4
+ native lockfile audit) + supply-chain compromise detection + outdated check, em uma passada. Hard-gates
5
+ comandos downstream quando há qualquer finding bloqueante.
3
6
 
4
- Auto-invocado por `/dw-review` e `/dw-generate-pr` em projetos TS/Python/C#/Rust. Invocação standalone disponível pra audit manual.
7
+ É **auto-invocado por `/dw-review` e `/dw-generate-pr`** (em TS/Python/C#/Rust), roda como **fase explícita e
8
+ nomeada no `/dw-autopilot`** (após review + QA, antes de commit/PR), e é **executável standalone** a qualquer
9
+ momento. O `/dw-generate-pr` re-enforça o verdict como hard gate final. Os três continuam gating em segurança
10
+ — o comando standalone e a fase nomeada são aditivos, não substituem.
5
11
 
6
12
  ## Quando Usar
7
13
  - Auto-invocado: `/dw-review` e `/dw-generate-pr` em linguagens suportadas.
@@ -10,7 +16,7 @@ Auto-invocado por `/dw-review` e `/dw-generate-pr` em projetos TS/Python/C#/Rust
10
16
  - NÃO use como substituto pra review humano em código auth/payment de alto risco (use skill `security-review` JUNTO com este).
11
17
 
12
18
  ## Posição no Pipeline
13
- **Antecessor:** qualquer momento; auto-invocado por `/dw-review`, `/dw-generate-pr` | **Sucessor:** `/dw-bugfix` pra atacar findings, ou `/dw-commit` se APROVADO
19
+ **Antecessor:** `/dw-review` + `/dw-qa` (o Security Gate roda depois deles passarem) | **Sucessor:** `/dw-commit` / `/dw-generate-pr` se APROVADO, ou `/dw-bugfix` pra atacar findings. Também roda standalone a qualquer momento.
14
20
 
15
21
  ## Modos
16
22
 
@@ -33,10 +39,20 @@ Auto-invocado por `/dw-review` e `/dw-generate-pr` em projetos TS/Python/C#/Rust
33
39
 
34
40
  ## Dependências Necessárias
35
41
 
36
- - **Trivy** — deve estar instalado (via `npx @brunosps00/dev-workflow install-deps`).
42
+ - **Trivy** — SCA / secrets / IaC (via `npx @brunosps00/dev-workflow install-deps`).
43
+ - **Semgrep** — camada SAST (opcional mas recomendado). Se ausente, a camada SAST é pulada e anotada.
44
+ - **gitleaks** — secret scan dedicado (opcional mas recomendado). Se ausente, cai pro Trivy secrets.
37
45
  - **Context7 MCP** — pra best practices específicas de versão.
38
46
 
39
- ## Três Camadas de Detecção
47
+ Todos os scanners são opcionais individualmente: uma tool ausente degrada aquela camada e é **reportada**
48
+ no summary (o gate nunca quebra por falta de scanner), mas a cobertura faltante fica visível. Instale via
49
+ `npx @brunosps00/dev-workflow install-deps`.
50
+
51
+ ## Camadas de Detecção
52
+
53
+ **Foco no diff (código gerado):** camadas que suportam isso focam no diff contra a base do PR
54
+ (`git merge-base HEAD origin/main`), concentrando o gate no código recém-escrito, sem ruído do código
55
+ pré-existente. Um pass periódico `--full` varre a árvore inteira.
40
56
 
41
57
  ### Camada 1: OWASP Static Review (via skill `security-review`)
42
58
 
@@ -72,6 +88,23 @@ Cruza dependency tree contra:
72
88
 
73
89
  Output: `.dw/secure-audit/compromise-findings.md` por pacote afetado: COMPROMISED / suspicious / clean.
74
90
 
91
+ ### Camada 4: SAST — Semgrep (análise semântica do código gerado)
92
+
93
+ Análise estática semântica e determinística do **diff** (`--baseline-commit`), complementando a Camada 1.
94
+ Rulesets fixados: `p/security-audit`, `p/owasp-top-ten`, `p/secrets` (+ packs de linguagem detectados).
95
+ Mapeie Semgrep `ERROR`→HIGH (ou CRITICAL pra CWEs RCE/authn-bypass/SQLi), `WARNING`→MEDIUM, `INFO`→LOW.
96
+ Aplique a disciplina **fp-check** (reachability) antes de um finding bloquear (ver `security-review/references/sast.md`).
97
+
98
+ Output: `.dw/secure-audit/sast-findings.md`. Se `semgrep` ausente: pulado + anotado no summary.
99
+
100
+ ### Camada 5: Secret scan dedicado — gitleaks
101
+
102
+ `gitleaks protect --staged` (não-commitado) ou `gitleaks detect --log-opts <base>..HEAD` (branch), `--redact`.
103
+ Autoritativo no diff; complementa Trivy secrets (dedupe por `file:line`). **Qualquer hit = REPROVADO, sem
104
+ exceção de ADR** — secrets são removidos e rotacionados, não justificados. Ver `security-review/references/secrets.md`.
105
+
106
+ Output: `.dw/secure-audit/secret-findings.md`. Se `gitleaks` ausente: cai pro Trivy secrets + anotado.
107
+
75
108
  ### Plus: outdated check
76
109
 
77
110
  `npm outdated` / `pip list --outdated` / `dotnet list outdated` / `cargo outdated` pra identificar pacotes atrás em minor ou major.
@@ -84,9 +117,11 @@ Todos os findings são classificados num desses tiers em `.dw/secure-audit/audit
84
117
 
85
118
  | Tier | Critério | Bloqueia | Ação Sugerida |
86
119
  |------|----------|----------|---------------|
120
+ | **SECRET** | Credencial/chave/token hardcoded (gitleaks ou Trivy), sobrevive ao allowlist | SIM — **sem exceção de ADR** | Remover do código + histórico, rotacionar, mover pra secret store |
87
121
  | **COMPROMISED** | Pacote conhecido como malicioso nessa faixa de versão | SIM | Remover imediato / pin pra versão segura |
88
- | **CRITICAL** | CVE CVSS ≥9.0 OU exploit ativo OU auth bypass | SIM | Update ou substituir em 24h |
89
- | **HIGH** | CVE CVSS 7.0–8.9 OU exploitável no contexto atual | SIM | Update ou substituir em 1 semana |
122
+ | **CRITICAL** | CVE CVSS ≥9.0 OU exploit ativo OU auth bypass; OU SAST de CWE alto-impacto (SQLi/RCE/authn-bypass/SSRF/deserialization) alcançável | SIM | Update/substituir ou corrigir em 24h |
123
+ | **HIGH** | CVE CVSS 7.0–8.9 OU exploitável no contexto; OU Semgrep `ERROR` alcançável (fp-check passou) | SIM | Update/substituir ou corrigir em 1 semana |
124
+ | **MEDIUM / LOW** | CVE CVSS <7.0; OU Semgrep `WARNING`/`INFO`; OU finding inalcançável rebaixado pelo fp-check | NÃO (advisory) | Rastrear e corrigir rotineiramente |
90
125
  | **OUTDATED-MAJOR** | ≥1 major version atrás (ex: React 17 → 19) | NÃO | Planejar migração próximo trimestre |
91
126
  | **OUTDATED-MINOR** | Minor/patch atrás | NÃO | Update rotineiro |
92
127
  | **CLEAN** | Sem findings | NÃO | — |
@@ -94,19 +129,26 @@ Todos os findings são classificados num desses tiers em `.dw/secure-audit/audit
94
129
  ## Hard Gates
95
130
 
96
131
  Verdict é um de:
97
- - **APROVADO** — sem CRITICAL, HIGH ou COMPROMISED. Arquivo verdict `.dw/secure-audit/audit-summary.md` status: APROVADO.
98
- - **REPROVADO** — ≥1 CRITICAL, HIGH ou COMPROMISED sem ADR explícito ou remediação em andamento. Status: REPROVADO.
132
+ - **APROVADO** — sem SECRET, COMPROMISED, CRITICAL ou HIGH. Arquivo verdict `.dw/secure-audit/audit-summary.md` status: APROVADO. (MEDIUM/LOW/outdated podem aparecer como advisory.)
133
+ - **REPROVADO** — ≥1 SECRET (sempre), ou ≥1 COMPROMISED/CRITICAL/HIGH sem ADR explícito ou remediação em andamento. Status: REPROVADO.
134
+
135
+ **Limiar Rigoroso:** SECRET e COMPROMISED sempre bloqueiam. CRITICAL/HIGH (CVE ou SAST alcançável) bloqueiam salvo ADR justificando aceitação. MEDIUM/LOW e outdated são advisory. SECRET é o único tier **sem escape via ADR** — rotacione, não justifique.
99
136
 
100
137
  **`/dw-review` e `/dw-generate-pr` enforçam:** se linguagem do projeto é suportada E `.dw/secure-audit/audit-summary.md` mais recente está faltando OU REPROVADO, esses comandos retornam REPROVADO. Sem exceção. Sem flag bypass.
101
138
 
102
139
  ## Modo 1: Default (`/dw-secure-audit`)
103
140
 
104
141
  1. **Detectar stack**: checar package.json / requirements.txt / *.csproj / Cargo.toml.
105
- 2. **Rodar todas as três camadas em paralelo** (onde possível):
106
- - OWASP static (via skill `security-review`).
142
+ 2. **Rodar todas as camadas de detecção em paralelo** (onde possível):
143
+ - OWASP static (via skill `security-review`) — focado no diff.
144
+ - SAST — Semgrep no diff (`--baseline-commit`).
145
+ - Secret scan dedicado — gitleaks no diff.
107
146
  - Trivy + lockfile audit.
108
147
  - Supply-chain compromise check.
109
148
  3. **Rodar outdated check.**
149
+ **fp-check:** antes de finalizar, rode validação de reachability em cada finding SAST/OWASP bloqueante
150
+ (ver `security-review` SKILL.md); rebaixe os comprovadamente inalcançáveis pra advisory com razão logada.
151
+ Secrets são isentos de rebaixamento.
110
152
  4. **Agregar findings** por tier.
111
153
  5. **Escrever summary** em `.dw/secure-audit/audit-summary.md`:
112
154
 
@@ -118,14 +160,26 @@ Verdict é um de:
118
160
  ## Resumo por Tier
119
161
  | Tier | Contagem | Detalhe |
120
162
  |------|----------|---------|
163
+ | SECRET | N | <lista> |
121
164
  | COMPROMISED | N | <lista> |
122
165
  | CRITICAL | N | <lista> |
123
166
  | HIGH | N | <lista> |
167
+ | MEDIUM/LOW (advisory) | N | <lista> |
124
168
  | OUTDATED-MAJOR | N | <lista> |
125
169
  | OUTDATED-MINOR | N | <lista> |
126
170
 
171
+ ## Scanners
172
+ | Camada | Tool | Status |
173
+ |--------|------|--------|
174
+ | OWASP | security-review | rodou |
175
+ | SAST | semgrep | rodou / pulado (não instalado) |
176
+ | Secrets | gitleaks | rodou / fallback trivy (não instalado) |
177
+ | SCA/IaC | trivy | rodou |
178
+
127
179
  ## Relatórios das camadas
128
180
  - OWASP: `owasp-findings.md`
181
+ - SAST: `sast-findings.md`
182
+ - Secrets: `secret-findings.md`
129
183
  - Trivy: `trivy-findings.md`
130
184
  - Lockfile: `lockfile-findings.md`
131
185
  - Compromise: `compromise-findings.md`
@@ -164,7 +218,7 @@ Pra cada remediação aprovada:
164
218
  ## Modo 4: CI (`/dw-secure-audit --scan-only`)
165
219
 
166
220
  Output mínimo:
167
- - Roda todas as três camadas.
221
+ - Roda todas as camadas de detecção.
168
222
  - Escreve findings em disco.
169
223
  - Exit code 0 se APROVADO, 1 se REPROVADO.
170
224
  - Sem planejamento.
@@ -182,8 +236,9 @@ Pra gates pre-merge em CI.
182
236
  ## Constitution Gate
183
237
 
184
238
  <critical>
185
- - CRITICAL ou COMPROMISED finding sem ADR justificando aceitação explícita → verdict não pode ser APROVADO.
186
- - Violações de princípios de constitution security-related (P-009 server-side auth, P-010 secrets-in-repo) escalonam findings — violação de princípio `severity: info` surface aqui vira HIGH.
239
+ - Finding SECRET → verdict NUNCA pode ser APROVADO (sem escape via ADR). Rotacione e remova.
240
+ - Finding CRITICAL/HIGH/COMPROMISED (CVE ou SAST alcançável) sem ADR justificando aceitação explícita → verdict não pode ser APROVADO.
241
+ - Violações de princípios de constitution security-related (P-009 server-side auth, P-010 secrets-in-repo, P-011 sem SAST high-severity) escalonam findings — violação de princípio `severity: info` surface aqui vira HIGH.
187
242
  </critical>
188
243
 
189
244
  ## Anti-patterns
@@ -198,8 +253,10 @@ Pra gates pre-merge em CI.
198
253
 
199
254
  ```
200
255
  .dw/secure-audit/
201
- ├── audit-summary.md # verdict + resumo de tiers
256
+ ├── audit-summary.md # verdict + resumo de tiers + status dos scanners
202
257
  ├── owasp-findings.md # Camada 1
258
+ ├── sast-findings.md # Camada 4 (Semgrep)
259
+ ├── secret-findings.md # Camada 5 (gitleaks, redacted)
203
260
  ├── trivy-findings.md # Camada 2 (SCA + secrets + IaC)
204
261
  ├── lockfile-findings.md # Camada 2 (native auditor)
205
262
  ├── compromise-findings.md # Camada 3
@@ -80,6 +80,7 @@ O comando `update` sobrescreve arquivos gerenciados e PRESERVA:
80
80
  - `.dw/rules/` (rules do usuário)
81
81
  - `.dw/spec/` (PRDs e tasks em andamento)
82
82
  - `.dw/intel/` (índice de codebase do `/dw-intel --build`)
83
+ - docs derivados do projeto como `.dw/constitution.md`, `.dw/rules/concerns.md` e `DESIGN.md` em frontend
83
84
 
84
85
  O comando `update` também roda o passo de migração GSD automaticamente — se o projeto tem `.planning/` legado (de uso prévio do GSD), o conteúdo é migrado para `.dw/intel/`, `.dw/spec/active-session.md`, `.dw/spec/quick/`, etc., e `.planning/` é renomeado para `.planning.gsd-archive-<DATA>/` para inspeção. Os arquivos `.claude/commands/gsd/`, `.claude/agents/gsd-*.md`, `.claude/hooks/gsd-*.js` e `.claude/gsd-file-manifest.json` são removidos durante a migração.
85
86
 
@@ -97,9 +98,23 @@ Apresente ao usuário:
97
98
  - Idioma detectado (`DETECTED_LANG`)
98
99
  - Versão anterior → nova versão
99
100
  - Resumo do que o output do `update` mostrou (arquivos copiados, wrappers gerados, MCPs configurados)
101
+ - As `Post-update agent actions` impressas pelo CLI
100
102
  - Quaisquer avisos ou erros
101
103
 
102
- ### 6. Sugerir Próximo Passo
104
+ ### 6. Executar Ações de Agente Pós-Update (Obrigatório)
105
+
106
+ <critical>O CLI não consegue executar slash commands nem inspecionar sozinho a autoridade de design específica do produto. Quando o output do update imprimir `Post-update agent actions`, trate essa lista como trabalho para este agente executar agora, exceto se o usuário pediu explicitamente "só atualize os arquivos".</critical>
107
+
108
+ Rode os comandos listados nesta ordem:
109
+
110
+ 1. `/dw-analyze-project` quando listado — refresca docs derivados do projeto. Este é o comando que cria ou atualiza `.dw/rules/`, oferece `.dw/constitution.md`, escreve `.dw/rules/concerns.md` e, em projetos frontend, sintetiza `DESIGN.md` a partir dos tokens existentes quando não há autoridade de design.
111
+ 2. `/dw-intel --build` quando listado — reconstrói o índice queryable do codebase depois do refresh de rules/docs.
112
+ 3. `/dw-harness-audit` — valida commands, wrappers, agentes, MCPs e gates depois que arquivos gerenciados mudaram.
113
+ 4. `/dw-skill-health` — audita bundled skills e agentes atualizados por bloat, sobreposição e referências antigas.
114
+
115
+ Se `/dw-analyze-project` fizer perguntas de esclarecimento ou aprovação, faça-as e continue. Não fabrique `DESIGN.md`, `concerns.md` ou constitution diretamente dentro de `/dw-update`; delegue para o comando dono desses artefatos.
116
+
117
+ ### 7. Sugerir Próximo Passo
103
118
 
104
119
  Se comandos/skills foram atualizados, lembre o usuário:
105
120
  - Reinicie a sessão do agente (ou recarregue skills) para que as instruções novas tenham efeito — skills costumam ser carregadas no início da sessão
@@ -80,12 +80,17 @@ mode: defaults | custom
80
80
  **P-009 — Authorization server-side em todo endpoint que altera estado** (severity: info)
81
81
  **Regra:** Endpoint que cria, atualiza ou deleta dado deve verificar autorização do caller no servidor. Gating em UI (botões escondidos, formulários disabled) não é segurança.
82
82
  **Why:** Browsers são untrusted (ver `dw-testing-discipline/references/security-boundary.md`). Gating em UI é conveniência; só checks server-side protegem dado.
83
- **Enforcement:** `dw-code-review` e `dw-security-check` exigem check de auth explícito (decorator, middleware ou assertion in-handler) em rotas POST/PUT/PATCH/DELETE.
83
+ **Enforcement:** `dw-review` e o Security Gate `dw-secure-audit` exigem check de auth explícito (decorator, middleware ou assertion in-handler) em rotas POST/PUT/PATCH/DELETE.
84
84
 
85
- **P-010 — Secrets nunca entram no repositório** (severity: info)
85
+ **P-010 — Secrets nunca entram no repositório** (severity: high)
86
86
  **Regra:** Nenhuma API key, password, signing key, token ou endpoint de produção commitado em source. `.env.example` documenta forma apenas.
87
87
  **Why:** Histórico de repositório é permanente. Um secret commitado uma vez está vazado mesmo que revertido no commit seguinte.
88
- **Enforcement:** `dw-security-check` roda Trivy + secret scanners no diff.
88
+ **Enforcement:** o Security Gate `dw-secure-audit` roda gitleaks + Trivy no diff. Qualquer hit bloqueia — **sem exceção de ADR** (rotacione, não justifique).
89
+
90
+ **P-011 — Nenhum finding SAST high-severity passa sem revisão** (severity: high)
91
+ **Regra:** Código gerado/alterado deve passar na camada SAST (Semgrep) do Security Gate. Um finding high-severity alcançável (SQLi, command/code injection, SSRF, deserialization insegura, authn bypass) bloqueia salvo ADR justificando aceitação.
92
+ **Why:** Código gerado por IA reproduz padrões inseguros em escala; SAST determinístico no diff pega o que o review por leitura perde.
93
+ **Enforcement:** `dw-secure-audit` roda Semgrep no diff (`--baseline-commit`); findings sobrevivem a uma validação fp-check de reachability antes de bloquear.
89
94
 
90
95
  ---
91
96
 
@@ -0,0 +1,17 @@
1
+ // Módulo de fluxo do functional-doc. Executado por .dw/scripts/functional-doc/run-playwright-flow.mjs
2
+ // através da API Node do Playwright, para que o vídeo seja gravado com page.screencast (funciona tanto
3
+ // com browser lançado quanto via connectOverCDP). Não precisa de imports aqui — o runner injeta
4
+ // page, context, expect, baseURL e os helpers step()/shot().
5
+
6
+ export default async function flow({ page, expect, baseURL, step, shot }) {
7
+ await step("Abrir rota alvo", async () => {
8
+ await page.goto(`${baseURL}{{routePath}}`);
9
+ await expect(page).toHaveURL(new RegExp("{{routeRegex}}"));
10
+ });
11
+
12
+ {{testSteps}}
13
+
14
+ await step("Registrar contexto final", async () => {
15
+ await expect(page.locator("body")).toBeVisible();
16
+ });
17
+ }
@@ -0,0 +1,16 @@
1
+ [package]
2
+ name = "cdp-relay"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Tiny TCP forwarder so WSL can reach a Windows browser's CDP debug port in NAT mode. Built on Windows by dev-workflow; no runtime dependency."
6
+
7
+ [[bin]]
8
+ name = "cdp-relay"
9
+ path = "src/main.rs"
10
+
11
+ [profile.release]
12
+ opt-level = "z"
13
+ lto = true
14
+ strip = true
15
+ panic = "abort"
16
+ codegen-units = 1
@@ -0,0 +1,49 @@
1
+ //! cdp-relay — a tiny TCP forwarder.
2
+ //!
3
+ //! Listens on 0.0.0.0:<listen> and forwards every connection to 127.0.0.1:<target>.
4
+ //! dev-workflow runs this on the Windows side so WSL (NAT networking) can reach a Windows
5
+ //! browser's CDP remote-debugging port, which Chromium binds to loopback only. std-only:
6
+ //! no crates, no runtime — a single static .exe built with the user's Windows `cargo`.
7
+
8
+ use std::env;
9
+ use std::io;
10
+ use std::net::{Shutdown, TcpListener, TcpStream};
11
+ use std::thread;
12
+
13
+ fn pump(mut from: TcpStream, mut to: TcpStream) {
14
+ let _ = io::copy(&mut from, &mut to);
15
+ // Signal EOF to the peer so the other direction can finish cleanly.
16
+ let _ = to.shutdown(Shutdown::Write);
17
+ }
18
+
19
+ fn handle(client: TcpStream, target_port: u16) -> io::Result<()> {
20
+ client.set_nodelay(true).ok();
21
+ let upstream = TcpStream::connect(("127.0.0.1", target_port))?;
22
+ upstream.set_nodelay(true).ok();
23
+
24
+ let client_rx = client.try_clone()?;
25
+ let upstream_rx = upstream.try_clone()?;
26
+ // client -> upstream
27
+ thread::spawn(move || pump(client, upstream));
28
+ // upstream -> client
29
+ thread::spawn(move || pump(upstream_rx, client_rx));
30
+ Ok(())
31
+ }
32
+
33
+ fn main() {
34
+ let args: Vec<String> = env::args().collect();
35
+ if args.len() < 3 {
36
+ eprintln!("usage: cdp-relay <listen_port> <target_port>");
37
+ std::process::exit(2);
38
+ }
39
+ let listen_port: u16 = args[1].parse().expect("invalid listen port");
40
+ let target_port: u16 = args[2].parse().expect("invalid target port");
41
+
42
+ let listener = TcpListener::bind(("0.0.0.0", listen_port)).expect("bind failed");
43
+ for stream in listener.incoming() {
44
+ if let Ok(client) = stream {
45
+ // One connection failing must not take down the relay.
46
+ let _ = handle(client, target_port);
47
+ }
48
+ }
49
+ }
@@ -628,7 +628,7 @@ function toTitleCase(value) {
628
628
  function buildPlaywrightSteps(features, heading, s) {
629
629
  const safeHeading = heading.replace(/"/g, '\\"');
630
630
  const steps = [
631
- ` await test.step("${s.validateInitialLoad}", async () => {`,
631
+ ` await step("${s.validateInitialLoad}", async () => {`,
632
632
  ` await expect(page.getByText(/${escapeRegex(safeHeading)}/i).first()).toBeVisible();`,
633
633
  ` });`,
634
634
  ];
@@ -636,8 +636,8 @@ function buildPlaywrightSteps(features, heading, s) {
636
636
  const firstEdge = features.flatMap((feature) => feature.cases).find((item) => item.type === "edge-case");
637
637
  if (firstEdge) {
638
638
  steps.push(
639
- ` await test.step("${s.recordEdgeCase}", async () => {`,
640
- ` await page.screenshot({ path: "evidence-edge-case.png", fullPage: true });`,
639
+ ` await step("${s.recordEdgeCase}", async () => {`,
640
+ ` await shot("edge-case");`,
641
641
  ` });`,
642
642
  );
643
643
  }
@@ -645,7 +645,7 @@ function buildPlaywrightSteps(features, heading, s) {
645
645
  const firstError = features.flatMap((feature) => feature.cases).find((item) => item.type === "error");
646
646
  if (firstError) {
647
647
  steps.push(
648
- ` await test.step("${s.recordErrorCase}", async () => {`,
648
+ ` await step("${s.recordErrorCase}", async () => {`,
649
649
  ` await expect(page.locator("body")).toBeVisible();`,
650
650
  ` });`,
651
651
  );
@@ -715,7 +715,7 @@ function buildManifest({ projectRoot, target, targetType, routePath, baseUrl, ha
715
715
  features: path.join(outputRoot, "features.md"),
716
716
  caseMatrix: path.join(outputRoot, "case-matrix.md"),
717
717
  e2eRunbook: path.join(outputRoot, "e2e-runbook.md"),
718
- script: path.join(outputRoot, "scripts", `${slugify(routePath || target)}.spec.ts`),
718
+ script: path.join(outputRoot, "scripts", `${slugify(routePath || target)}.flow.mjs`),
719
719
  captions: path.join(outputRoot, "captions", `${slugify(routePath || target)}.srt`),
720
720
  },
721
721
  blockers: hasPlaywright ? [] : ["Playwright configuration not detected"],
@@ -786,10 +786,8 @@ function main() {
786
786
  );
787
787
 
788
788
  writeFile(
789
- path.join(outputRoot, "scripts", `${slugify(routePath || target)}.spec.ts`),
790
- renderTemplate(path.join(templatesRoot, "playwright.spec.ts.tpl"), {
791
- baseUrl,
792
- testTitle: s.flowTest(routePath || target),
789
+ path.join(outputRoot, "scripts", `${slugify(routePath || target)}.flow.mjs`),
790
+ renderTemplate(path.join(templatesRoot, "playwright.flow.mjs.tpl"), {
793
791
  routePath,
794
792
  routeRegex: escapeRegex(routePath || "/"),
795
793
  testSteps: buildPlaywrightSteps(features, heading, s),