sdd-layer 0.13.3

Spec-Driven Development CLI and agent harness
# Execution - DDD Effect Rust Providers CLI

## Rastreabilidade
- Orquestração: DDD Effect Rust Providers CLI
- Slug: ddd-effect-rust-providers-cli
- Stage: execution
- Estado atual: recorded
- Origem: Refinement aprovado em `05-refinement.md`.
- Task executada: SDD-DDD-006 - Melhorar risk classifier para provider/secrets.
- ADR: não criada nesta task; alteração é calibração de regra determinística do classificador, sem decisão arquitetural nova.
- Próximo artefato: Review em `07-review.md`.

## Tarefa
Implementar a primeira tarefa do MVP 1: corrigir a subestimação de risco quando a descrição menciona providers externos, tokens, API keys ou credenciais.

Critério original:

```gherkin
Cenário: feature com tokens e provider é classificada como risco alto
  Dado a descrição "usar provider externo com tokens"
  Quando eu executo `sdd risk`
  Então a classificação inclui fatores de segurança ou dependência externa
  E exige Refinement
```

## Resumo da implementação
- Ampliei os termos de `security_sensitive` no risk classifier para reconhecer `tokens`, `access token`, `api key`, `api-key`, `apikey`, `secrets`, `credencial`, `credenciais`, `credential` e `credentials`.
- Ampliei os termos de `external_dependency` para reconhecer `provider`, `providers`, `provedor`, `provedores`, `openai`, `anthropic`, `claude`, `gemini`, `opencode` e `codex`.
- Adicionei teste unitário de regressão para a frase `usar provider externo com tokens`, garantindo nível `high`, fatores `security_sensitive` e `external_dependency`, e checkpoint `Refinement`.
- Verifiquei via CLI que `sdd risk "usar provider externo com tokens" --json` retorna `risk_level: high`.

## Arquivos alterados
- `src/main.rs`: tabela de termos do risk classifier e teste unitário `risk_classifier_treats_provider_tokens_as_high_risk`.
- `docs/ddd-effect-rust-providers-cli/`: artefatos SDD desta orquestração e registro desta execução.

## Testes e evidências
- `cargo fmt --check`: passou.
- `cargo test risk_classifier`: passou, 2 testes.
- `cargo run --quiet -- risk "usar provider externo com tokens" --json`: retornou `risk_level: high`, fatores `security_sensitive` e `external_dependency`, checkpoints `PRD`, `Tech Spec`, `Merge`, `Refinement`, `Deploy`.
- `cargo test`: passou, 7 testes unitários e 6 testes de integração.
- `cargo clippy -- -D warnings`: passou.

## Riscos e pendências
- O worktree já continha muitas alterações fora desta task antes da execução; esta implementação tocou apenas o bloco de termos/teste em `src/main.rs` e os artefatos SDD desta orquestração.
- Ainda falta Review antes de merge.
- As próximas tasks do MVP 1 são SDD-DDD-001 e SDD-DDD-002; elas devem ser executadas em diffs separados para manter o refactor controlado.

---

## Rastreabilidade
- Orquestração: DDD Effect Rust Providers CLI
- Slug: ddd-effect-rust-providers-cli
- Stage: execution
- Estado atual: recorded
- Origem: comando humano `/sdd execution` após review da SDD-DDD-006.
- Task executada: SDD-DDD-001 - Preparar módulos de domínio sem alterar comportamento.
- ADR: `06-adr.md`, decisão de iniciar DDD por módulos internos na mesma crate.
- Próximo artefato: Review atualizado em `07-review.md`.

## Tarefa
Executar a segunda task do MVP 1: criar a primeira estrutura de domínio e mover lógica pura de risco para módulo próprio sem alterar o comportamento público do CLI.

Critério original:

```gherkin
Cenário: comandos existentes preservados após a separação inicial
  Dado o CLI atual do `sdd-layer`
  Quando eu executo `cargo test`
  Então todos os testes existentes continuam passando
  E os comandos públicos documentados continuam disponíveis
```

## Resumo da implementação
- Criei `src/domain/mod.rs` como raiz inicial de domínio.
- Criei `src/domain/risk.rs` com `RiskResult`, termos determinísticos de risco, `classify` e `term_matches`.
- Removi o `RiskResult` local de `src/main.rs` e passei a importar `domain::risk::RiskResult`.
- Mantive wrappers finos `classify` e `term_matches` em `src/main.rs` para preservar call sites existentes, hooks e comportamento público.
- Mantive a calibração de risco da SDD-DDD-006 dentro do novo módulo de domínio.

## Arquivos alterados
- `src/main.rs`: declaração/import do módulo de domínio e wrappers para risco.
- `src/domain/mod.rs`: novo módulo de domínio.
- `src/domain/risk.rs`: domínio de classificação de risco.
- `docs/ddd-effect-rust-providers-cli/`: atualização de execução/ADR/review desta orquestração.

## Testes e evidências
- `cargo fmt --check`: passou.
- `cargo test risk_classifier`: passou.
- `cargo run --quiet -- risk "usar provider externo com tokens" --json`: manteve `risk_level: high`, fatores `security_sensitive` e `external_dependency`.
- `cargo test`: passou, 7 testes unitários e 6 testes de integração.
- `cargo clippy -- -D warnings`: passou.

## Riscos e pendências
- O escopo foi intencionalmente pequeno: apenas o domínio de risco foi extraído. `artifact`, `stage`, `orchestration` e demais bounded contexts seguem no `main.rs`.
- Os wrappers em `main.rs` são uma ponte temporária para evitar um refactor amplo; devem ser removidos quando os demais call sites forem movidos para application/runtime.
- O worktree continua contendo alterações pré-existentes fora deste escopo; não foram revertidas.

---

## Rastreabilidade
- Orquestração: DDD Effect Rust Providers CLI
- Slug: ddd-effect-rust-providers-cli
- Stage: execution
- Estado atual: recorded
- Origem: comando humano `/sdd execution` após aprovação da review das tasks SDD-DDD-006 e SDD-DDD-001.
- Task executada: SDD-DDD-002 - Introduzir runtime/error/redaction base.
- ADR: `06-adr.md`, decisão de iniciar runtime/redaction como módulo interno antes de provider remoto.
- Próximo artefato: Review atualizado em `07-review.md`.

## Tarefa
Executar a terceira task do MVP 1: adicionar base de runtime inspirada em Effect.ts para Rust, com erro tipado, wrapper de segredo e scrubber central, sem introduzir chamadas remotas a providers.

Critério original:

```gherkin
Cenário: segredo não aparece em saída formatada
  Dado um valor secreto carregado em `Secret<String>`
  Quando o valor é usado em erro, debug seguro ou evento estruturado
  Então a saída contém apenas marcador redigido
  E não contém o valor original
```

## Resumo da implementação
- Criei `src/runtime/mod.rs` como raiz inicial do runtime.
- Criei `src/runtime/errors.rs` com `SddError`, `SddResult` e `Display` redigido via scrubber central.
- Criei `src/runtime/redaction.rs` com `Secret<T>`, `RedactedDisplay`, marcador `[REDACTED]` e `redact_text`.
- O scrubber cobre valores em chaves sensíveis como `OPENAI_API_KEY`, `token`, `secret`, `password`, `credential`, headers `Authorization: Bearer ...` e formatos conhecidos como `sk-*`, `sk-ant-*` e `AIza*`.
- Conectei `redact_text` ao `compact_tool_input` para que eventos JSONL de hooks já recebam o alvo redigido quando comandos incluem secrets.
- Adicionei testes unitários para `Secret<T>` em `Display`, `Debug` e evento estruturado, erro tipado com segredo e caminho real de hook.

## Arquivos alterados
- `src/main.rs`: declaração do módulo `runtime`, aplicação de `redact_text` em `compact_tool_input` e teste `compact_tool_input_redacts_secret_values`.
- `src/runtime/mod.rs`: novo módulo de runtime.
- `src/runtime/errors.rs`: `SddError`, `SddResult` e redaction no `Display`.
- `src/runtime/redaction.rs`: `Secret<T>`, `RedactedDisplay` e scrubbers centrais.
- `docs/ddd-effect-rust-providers-cli/`: atualização de execução/ADR/review desta orquestração.

## Testes e evidências
- `cargo fmt --check`: passou.
- `cargo test runtime`: passou, 4 testes.
- `cargo test redacts`: passou, 3 testes.
- `cargo test compact_tool_input_redacts_secret_values`: passou, 1 teste.
- `cargo test`: passou, 12 testes unitários e 6 testes de integração.
- `cargo clippy -- -D warnings`: passou.

## Riscos e pendências
- `SddError` ainda não substitui `anyhow` nos comandos existentes; esta task cria a base para migração gradual.
- `Secret<T>` evita vazamento por `Display`/`Debug`, mas futuros adapters devem evitar `expose` ou payloads crus e sempre usar scrubber antes de logs/docs.
- O scrubber inicial é conservador e cobre padrões centrais; provider registry e doctor devem adicionar testes negativos com env vars reais sintéticas antes de qualquer chamada remota.
- Providers remotos, async runtime, retries, timeout e Node bridge permanecem fora desta fatia.

---

## Rastreabilidade
- Orquestração: DDD Effect Rust Providers CLI
- Slug: ddd-effect-rust-providers-cli
- Stage: execution
- Estado atual: recorded
- Origem: comando humano para executar o ciclo completo após a review da SDD-DDD-002.
- Tasks executadas: SDD-DDD-003, SDD-DDD-004, SDD-DDD-005, SDD-DDD-008, SDD-DDD-007, SDD-DDD-009 e SDD-DDD-010.
- ADR: `06-adr.md`, decisão de manter provider registry offline e postergar a bridge Node+Effect.ts ampla.
- Próximo artefato: Review final em `07-review.md` e Memory em `08-memory.md`.

## Tarefa
Executar o restante do backlog aprovado: schema/parser de provider config, introspecção offline de providers, flags de provider/model/offline nos stages, preservação de config em update, memory index derivado, documentação e spike controlado da bridge Node+Effect.ts.

Critérios originais cobertos:

```gherkin
Cenário: flags sobrescrevem config
  Dado um `sdd.config.yaml` com provider default `codex`
  Quando eu executo `sdd orchestration --provider claude --model test-model --dry-run "x"`
  Então o provider resolvido é `claude`
  E o modelo resolvido é `test-model`

Cenário: doctor mostra env presente sem vazar segredo
  Dado a variável `OPENAI_API_KEY` definida com um valor sensível sintético
  Quando eu executo `sdd providers doctor --json`
  Então o JSON contém `present: true`
  E não contém o valor original

Cenário: artifact registra provider e modelo sem token
  Dado um comando `sdd prd --provider custom --model local-test --name ciclo "entrada"`
  Quando o artefato é gerado
  Então a seção de rastreabilidade contém provider `custom` e modelo `local-test`
  E não contém valores de variáveis de ambiente sensíveis

Cenário: learning aponta para fonte auditável
  Dado uma orquestração com PRD aprovado e Review registrada
  Quando eu executo `sdd memory learn --name "<ciclo>"`
  Então cada learning gerado contém `source_artifact` e `source_hash`
  E pode ser reconstruído a partir de `docs/<slug>/`

Cenário: update preserva provider customizado
  Dado um projeto instalado com provider `custom` em `sdd.config.yaml`
  Quando eu executo `sdd update`
  Então o bloco do provider customizado permanece no arquivo
  E os arquivos gerenciados pelo pacote são atualizados

Cenário: usuário encontra a precedência de configuração
  Dado a documentação do projeto
  Quando eu procuro por provider selection
  Então encontro a ordem flags, env, config e defaults
  E encontro a política de não registrar secrets

Cenário: CLI não depende de Node quando bridge está desligada
  Dado que `runtime.node_effect_bridge.enabled` é `false`
  Quando eu executo `sdd doctor` e `sdd orchestration --dry-run`
  Então nenhum comando exige `node`, `pnpm` ou `Effect.ts`
```

## Resumo da implementação
- Adicionei parser estruturado de `sdd.config.yaml` com `serde_yaml` e tipos de domínio em `src/domain/providers.rs`.
- Adicionei `schemas/provider-config.schema.json` e espelho em `.sdd/schemas/provider-config.schema.json`.
- Adicionei blocos `providers`, `runtime.node_effect_bridge` e `memory` em `sdd.config.yaml`, `sdd.config.example.yaml` e presets.
- Adicionei `sdd providers list` e `sdd providers doctor`, com saída humana e `--json`, sem imprimir valores de env vars.
- Adicionei flags `--provider`, `--model`, `--offline` e resolução por precedência flags/env/config/defaults nos stages e em `sdd orchestration`.
- Passei a registrar provider/model/offline na rastreabilidade de artefatos gerados por stages.
- Implementei `sdd memory learn --name` e `sdd memory status --json`, gerando `.sdd/memory/learnings.jsonl` derivado de artefatos e com `source_artifact`/`source_hash`.
- Mantive `sdd update` preservando `sdd.config.yaml` por padrão e acrescentei teste de preservação de provider customizado.
- Documentei provider selection, segurança, memória derivada e bridge Node+Effect.ts desligada por padrão em README, USAGE, CLIENTS e PROJECT-ONBOARDING.
- O spike Node+Effect.ts foi decidido como postergado: existe envelope/config de runtime, mas o CLI não exige Node quando a bridge está desligada.

## Arquivos alterados
- `Cargo.toml` e `Cargo.lock`: `serde`, `serde_yaml` e `sha2`.
- `src/main.rs`: comandos `providers`, flags de provider/model/offline nos stages, resolução de provider, memory learn/status, doctor da bridge Node.
- `src/domain/providers.rs`: config, provider registry builtin e tipos de seleção.
- `schemas/provider-config.schema.json` e `.sdd/schemas/provider-config.schema.json`: contrato do bloco de providers/runtime/memory.
- `sdd.config.yaml`, `sdd.config.example.yaml` e `presets/*.yaml`: defaults seguros de providers/runtime/memory.
- `README.md`, `docs/USAGE.md`, `docs/CLIENTS.md`, `docs/PROJECT-ONBOARDING.md`: documentação de provider selection, segurança e memória.
- `tests/cli.rs`: testes de providers, flags, memory index, doctor sem vazamento e update preservando provider customizado.
- `docs/ddd-effect-rust-providers-cli/`: atualização de execution/ADR/review/memory desta orquestração.

## Testes e evidências
- `cargo fmt --check`: passou.
- `cargo test providers_memory`: passou.
- `cargo test update_refreshes`: passou.
- `cargo test`: passou, 12 testes unitários e 7 testes de integração.
- `cargo clippy -- -D warnings`: passou.
- `OPENAI_API_KEY=[REDACTED] cargo run --quiet -- providers doctor --json`: retornou `present: true` para `codex` sem imprimir o valor original.
- `cargo run --quiet -- orchestration --provider claude --model test-model --dry-run "x"`: retornou `Provider: claude`, `Model: test-model`, `Offline: true`.
- `cargo run --quiet -- doctor`: passou.
- `cargo run --quiet -- memory status --json`: retornou índice `.sdd/memory/learnings.jsonl` sem exigir serviço externo.

## Riscos e pendências
- Providers seguem offline; ainda não há chamada real a OpenAI/Claude/Gemini/opencode.
- A bridge Node+Effect.ts foi mantida como configuração/spike desligado, não como runtime obrigatório.
- `serde_yaml` está marcado como deprecated upstream, mas foi adotado como parser pragmático para o MVP conforme recomendação do Refinement; pode ser substituído futuramente se o ecossistema do projeto exigir.
- `SddError` ainda é base nova e não migrou todos os comandos existentes de `anyhow`.