sdd-layer 0.26.0

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

## Rastreabilidade
- Orquestração: DDD Effect Rust Providers CLI
- Slug: ddd-effect-rust-providers-cli
- Stage: adr
- Estado atual: recorded
- Origem principal: `docs/ddd-effect-rust-providers-cli/06-execution.md`
- Artefatos relacionados: `03-techspec.md`, `04-tasks.md`, `05-refinement.md`, `07-review.md` quando existir.
- Decisão relacionada: SDD-DDD-001 a SDD-DDD-010.

## Status
- Estado: Aceito
- Data da decisão: 2026-06-06
- Dono técnico: mantenedor do `sdd-layer`
- Escopo: arquitetura interna do CLI Rust, runtime/redaction, provider registry offline, memória derivada e bridge Node+Effect.ts.

## Contexto
O `sdd-layer` concentra muita lógica em `src/main.rs`. A Tech Spec aprovada propôs DDD incremental, evitando uma migração imediata para workspace multi-crate. A execução SDD-DDD-001 precisava criar a primeira fronteira de domínio sem quebrar comandos existentes nem misturar uma grande reestruturação com a futura camada de providers. A execução SDD-DDD-002 precisava introduzir runtime/error/redaction antes de qualquer provider remoto, porque tokens e API keys são risco central da feature. As tasks restantes precisavam entregar provider selection, memória incremental auditável e spike Node+Effect.ts sem transformar o CLI offline em um cliente remoto obrigatório.

## Problema
Como iniciar DDD, provider selection, memória auditável e um caminho futuro para Effect.ts sem transformar o MVP em um refactor amplo, dependente de rede ou dependente de Node?

## Decisão
Iniciar por módulos internos dentro da mesma crate, começando por `src/domain/risk.rs`, `src/domain/providers.rs` e `src/runtime/*`. O `main.rs` permanece como composition root e mantém wrappers finos temporários para preservar call sites existentes. A base de runtime inclui `SddError`, `SddResult`, `Secret<T>`, `RedactedDisplay` e um scrubber central usado desde já no resumo de inputs dos hooks.

Provider selection fica offline e determinístico no MVP: config YAML versionada, registry builtin para `codex`, `claude`, `gemini` e `opencode`, comandos `sdd providers list/doctor` e flags `--provider`, `--model`, `--offline`. O CLI não chama providers remotos por padrão.

A memória incremental é um índice derivado e reconstruível em `.sdd/memory/learnings.jsonl`, com `source_artifact` e `source_hash` apontando para `docs/<slug>/`. A bridge Node+Effect.ts fica postergada: há configuração `runtime.node_effect_bridge.enabled`, mas com `false` o CLI não exige Node, pnpm ou Effect.ts.

## Alternativas consideradas
| Alternativa | Benefícios | Custos/Riscos | Motivo para aceitar ou rejeitar |
|---|---|---|---|
| Workspace multi-crate imediatamente | Fronteiras fortes desde o início | Alto churn em `Cargo.toml`, packaging, build.rs e testes | Rejeitada para o MVP 1 por risco desnecessário |
| Manter tudo em `main.rs` até providers | Menor diff imediato | Aumenta acoplamento e dificulta provider registry/redaction | Rejeitada porque não cumpre DDD incremental |
| Módulos internos na mesma crate | Baixo risco, fronteira real e testes preservados | Wrappers temporários ainda ficam no `main.rs` | Aceita como primeiro passo |
| Introduzir providers antes de redaction | Valor visível mais cedo | Risco de vazar tokens em eventos, docs e erros | Rejeitada; redaction deve anteceder chamadas remotas |
| Chamar providers remotamente no MVP | Demonstração completa de IA externa | Rede, credenciais, rate limit e segurança antes da base estar madura | Rejeitada; MVP entrega introspecção e seleção offline |
| Usar Node+Effect.ts como runtime obrigatório | Aproximação forte com Effect.ts | Quebra o harness Rust determinístico e exige Node em todos os projetos | Rejeitada; bridge fica desligada/postergada |
| Memory em banco/embedding remoto | Busca semântica mais poderosa | Custo operacional, privacidade e reconstrução difícil | Rejeitada; índice local derivado é suficiente para o MVP |

## Consequências
- Positivas: cria bounded contexts iniciais, preserva comportamento, entrega provider selection offline, cria proteção de secrets antes de provider remoto e torna memória auditável/reconstruível.
- Negativas: o `main.rs` ainda contém composition root grande; `SddError` ainda não substitui `anyhow` nos comandos existentes; providers remotos e bridge Node ficam para etapa futura.
- Trade-offs aceitos: fronteira menos forte que multi-crate em troca de menor risco; provider registry offline antes de adapter remoto real.
- Dívidas técnicas assumidas: remover wrappers, mover demais domínios quando application/runtime forem criados, migrar erros públicos para `SddError` gradualmente e decidir futura substituição de `serde_yaml` se necessário.

## Impactos e riscos
- Código e arquitetura: `src/domain/risk.rs` passa a ser fonte da lógica de risco; `src/domain/providers.rs` passa a ser fonte dos contratos de config/provider.
- Dados, contratos ou APIs: novo schema `schemas/provider-config.schema.json`, novos comandos `sdd providers list/doctor`, flags provider/model/offline nos stages e índice `.sdd/memory/learnings.jsonl`.
- Segurança e privacidade: preserva a calibração de tokens/providers como alto risco, redige secrets em `Display`/`Debug` de `Secret<T>`, `SddError` e resumo de inputs de hooks, e `providers doctor` só mostra presença de env var.
- Performance e capacidade: sem impacto relevante.
- Observabilidade e suporte: eventos JSONL de hooks passam a receber alvo compacto com scrubber central; memory status expõe contagens e fontes indexadas sem serviço externo.

## Plano de adoção
- Manter `domain::risk` como referência para futuras mudanças no risk classifier.
- Próximas extrações devem seguir o mesmo padrão: mover domínio puro, manter wrapper se necessário, testar comportamento público.
- Usar `runtime::redaction::redact_text` antes de novos logs, artifacts ou payloads de provider que possam conter segredo.
- Migrar novos adapters para `SddResult`/`SddError`; não introduzir provider remoto sem testes negativos de redaction.
- Evoluir providers remotos apenas depois de `providers doctor`, redaction e tests negativos estarem consolidados.
- Manter Node+Effect.ts atrás de `runtime.node_effect_bridge.enabled`; não exigir Node no caminho padrão.
- Regenerar memory index com `sdd memory learn --name` ao fim de cada ciclo.
- Atualizar review e memory com a decisão.

## Plano de reversão
- Mover `RiskResult`, `classify` e `term_matches` de volta para `src/main.rs`.
- Remover `mod domain` e os arquivos `src/domain/*`.
- Remover `pub mod runtime`, `src/runtime/*` e a chamada a `redact_text` em `compact_tool_input`.
- Remover `Command::Providers`, flags provider/model/offline, schema de provider config e blocos providers/runtime/memory dos presets.
- Remover `sdd memory learn/status` e `.sdd/memory/learnings.jsonl`, pois o índice é derivado.
- Rodar `cargo test` e `cargo clippy -- -D warnings`.

## Evidências
- Execução: `docs/ddd-effect-rust-providers-cli/06-execution.md`
- Testes/CI: `cargo fmt --check`, `cargo test risk_classifier`, `cargo test runtime`, `cargo test redacts`, `cargo test compact_tool_input_redacts_secret_values`, `cargo test providers_memory`, `cargo test update_refreshes`, `cargo test`, `cargo clippy -- -D warnings`, `sdd doctor`.
- PR/commits/diff: worktree local ainda não commitado.
- Review: `docs/ddd-effect-rust-providers-cli/07-review.md` quando atualizado.

## Histórico de revisão
| Data | Autor | Alteração | Motivo |
|---|---|---|---|
| 2026-06-06 | Codex | Criação da ADR | Pós-execução da SDD-DDD-001 |
| 2026-06-06 | Codex | Ampliação da ADR para runtime/redaction | Pós-execução da SDD-DDD-002 |
| 2026-06-06 | Codex | Ampliação para provider registry offline, memória derivada e bridge Node desligada | Pós-execução da SDD-DDD-003 a SDD-DDD-010 |