# Tasks - Orquestrador Autônomo de Demandas SDD

## Rastreabilidade
- Orquestração: Orquestrador Autônomo de Demandas SDD
- Slug: orquestrador-autônomo-de-demandas-sdd
- Stage: tasks
- Estado atual: recorded
- Origem: Tech Spec v2 aprovada (`03-techspec.md`) + PRD (`02-prd.md`).
- Próximo artefato: Refinamento em `05-refinement.md` (obrigatório por risco high).
- Decomposição auto-avançável (sem checkpoint duro); conferência por amostragem antes do Refinamento.

## Backlog
IDs `SDD-OAD-NNN`. Camadas: `prereq` (refactor habilitante), `data`, `engine`, `security`, `cli`, `adapters`, `tui/qa`.

| ID | Título | Camada | Depende de | Paralelizável |
|---|---|---|---|---|
| SDD-OAD-001 / T-01 | Helpers reusáveis in-process (map serde, required_sections, validate_state, timeouts/retries) | prereq | — | sim (lane A) |
| SDD-OAD-002 | Extrair geração `tui::runner` → `runtime::stage_runner` (sink genérico) | prereq | — | sim (lane B) |
| SDD-OAD-003 | Modelo `Demand` + slug validado + fila `.sdd/queue/` (enqueue/dedup/list) | data | — | sim (lane C) |
| SDD-OAD-004 | Estado do motor `.sdd/state/<slug>.json` (atômico, lock, schema_version, mapeamento Opção A) | data | 003 | — |
| SDD-OAD-005 | `engine::tick` — ingestão (demanda → orquestração → idea) com `FakeRunner` | engine | 001,003,004 | — |
| SDD-OAD-006 | `engine::tick` — avanço de planejamento via `can_navigate` até gate | engine | 002,005 | — |
| SDD-OAD-007 | `gate` — pausa em PRD/TechSpec/Refinamento (awaiting no `.sdd/state`) + notify escapado | engine | 006 | — |
| SDD-OAD-008 | `approval` — Decision, allowlist `approvers`, default-deny, sem auto-aprovação, retomada idempotente | engine/security | 004,007 | — |
| SDD-OAD-009 | Plugar `stage_runner` real no engine + escrita canônica + validação de seções no caminho | engine | 001,002,006 | — |
| SDD-OAD-010 | Redação ampliada (Slack/GitHub/GitLab/JWT/AWS/PEM/URL-cred/e-mail) em todos os writers | security | 004,009 | sim (após 009) |
| SDD-OAD-011 | Entrada não confiável (dado delimitado + system prompt restritivo + deny-check) + path-allowlisting | security | 009 | — |
| SDD-OAD-012 | CLI `sdd demand enqueue/list/status/approve/reject` (+`--json`) | cli | 003,008 | sim (após 008) |
| SDD-OAD-013 | `sdd auto tick/run` (watch/intervalo/concorrência) + `.sdd/orchestrator.jsonl` redigido | cli | 006,012 | — |
| SDD-OAD-014 | Adapters: traits + `LocalAdapter` default + `SlackAdapter` (assinatura) + Jira/Confluence stubs | adapters | 007,008 | sim (após 008) |
| SDD-OAD-015 | TUI esteira (fila + destaque awaiting) + não regressão e evidências finais | tui/qa | 005–014 | — |

## Dependências
- **Lanes paralelas iniciais**: A=`001`, B=`002`, C=`003` (independentes).
- **Caminho crítico**: `003 → 004 → 005 → 006 → 007 → 008` e `006 → 009 → 011`.
- `010` aplica-se após existirem writers (`004`) e a saída do runner (`009`).
- `012` precisa de `approve` (`008`); `013` precisa do tick (`006`) e da CLI (`012`).
- `014` (Slack) precisa de gate (`007`) + approval (`008`).
- `015` fecha após todo o resto (gate de não regressão).

## Critérios de aceite
Cenários transversais (os por-task estão inline em `## Prompts Agent`; os de produto estão no PRD `02-prd.md`):

```gherkin
Funcionalidade: Planejamento autônomo ponta a ponta (FakeRunner)
  Cenário: Da fila ao gate do PRD sem humano
    Dado o modo autônomo ligado e a fila com a demanda "DEM-1"
    Quando o motor roda ticks sucessivos
    Então a Idea é gerada e o PRD chega ao estado "awaiting_approval"
    E nenhuma etapa posterior é executada até a aprovação

  Cenário: Retomada idempotente após restart
    Dado "DEM-1" em "awaiting_approval (prd)"
    E o processo reiniciado
    Quando chega uma aprovação humana válida de PRD
    Então o motor retoma na Tech Spec sem reexecutar o PRD

Funcionalidade: Compatibilidade com a TUI existente
  Cenário: Estados ricos do motor não quebram a navegação
    Dado que o motor gravou "awaiting_approval" no .sdd/state
    Quando a TUI lê o traceability-map
    Então o campo de estado por-artefato continua em valor conhecido por StageStatus::from_yaml
    E can_navigate/current_stage não retornam Unknown

Funcionalidade: Não vazamento e não auto-aprovação
  Cenário: Segredo plantado é redigido em todos os writers
    Dado valores sensíveis (e-mail, xoxb-, ghp_, JWT, AKIA, URL com user:pass@) no contexto
    Quando o motor grava queue/state/orchestrator.jsonl/map
    Então todos aparecem como [REDACTED]
  Cenário: O motor nunca aprova um gate
    Dado "DEM-1" em "awaiting_approval"
    Quando o motor roda N ticks
    Então nenhum registro de aprovação é criado pelo motor
```

## Definition of Done
Para toda task:
- `cargo test` (incluindo os novos testes da task), `cargo clippy -- -D warnings` e `cargo check` verdes.
- Sem regressão nos testes existentes de `stage.rs`/`orchestration.rs`/`persist.rs`/TUI.
- Nenhum segredo em código, artefato ou log; redação aplicada em writers novos.
- Funções de domínio puras/testáveis sem rede nem terminal; efeitos isolados em `runtime`.
- Comportamento coberto por critério Gherkin/EARS da task.
- Rastreabilidade: a task cita PRD/Tech Spec de origem; evidências registradas em `06-execution.md` na execução.
- Mudança contida no escopo da task (sem tocar fluxo manual existente além do declarado).

## Estratégia de testes
- **Unitário/puro** (maioria): `queue`, `state`, `engine::tick`, `approval`, `slug`, mapeamento de estados, redação por formato.
- **Integração com `FakeRunner`**: ciclo completo de planejamento + restart no meio (sem provider real).
- **Equivalência de refactor** (`002`): saída de geração da TUI antes/depois da extração é equivalente.
- **Segurança**: injeção de prompt, não vazamento por formato, anti-spoof Slack, path traversal, não auto-aprovação.
- **Provider real**: teste `#[ignore]` gated por disponibilidade do CLI.
- **Não regressão**: suíte completa + gates de CI (`quality.require_tests=true`).

## Ordem sugerida
1. `001`, `002`, `003` (paralelos) → 2. `004` → 3. `005` → 4. `006` → 5. `009` → 6. `007` → 7. `008` → 8. `010`, `011` (segurança) → 9. `012` → 10. `013` → 11. `014` → 12. `015` (fechamento).

## Prompts Agent
Cada prompt é standalone (executável sem memória de conversa). Sempre: ler origem, respeitar contratos da Tech Spec v2, rodar `cargo test`/`clippy`/`check`, não tocar fluxo manual fora do escopo, reportar evidências.

- Task ID: T-01. Alias executável para `SDD-OAD-001`; aplicar o prompt completo da seção `SDD-OAD-001 / T-01 — Helpers reusáveis in-process`.

### SDD-OAD-001 / T-01 — Helpers reusáveis in-process
Objetivo: habilitar reuso in-process antes do motor. (1) Extrair `update_stage_state` (`src/main.rs:~1199`) para função reusável com round-trip `serde_yaml` (não string-replace) preservando o formato do `traceability-map.yaml`; (2) tornar `required_sections()` (`src/main.rs:~1406`) acessível a outros módulos; (3) estender `validate_state` (`src/main.rs:~1326`) para aceitar `in_progress`; (4) expor `timeouts`/`retries` em `ProviderSelection` (`src/domain/providers.rs:174`) a partir de `ProviderDefinition`.
Escopo IN: refactors + testes de round-trip e de não regressão. Escopo OUT: motor, fila, runner.
Arquivos prováveis: `src/main.rs`, `src/domain/providers.rs`.
Validação: `cargo test`, `clippy`, `check`; teste que grava e relê o map sem perder campos; teste que `--state in_progress` é aceito.
AC (EARS): Quando o motor gravar estado no map, o sistema DEVE preservar todos os campos existentes e desconhecidos. DoD: seção comum. Relatório: arquivos tocados, novas funções públicas, testes.

### SDD-OAD-002 — Extrair `runtime::stage_runner`
Objetivo: mover o núcleo de geração headless de `src/tui/runner.rs` para `src/runtime/stage_runner.rs`, substituindo `Sender<StageEvent>` por um *event sink* genérico (trait ou `FnMut`), de modo que TUI e (futuro) engine usem o mesmo código (resolve arch-C1). Preservar `DirectAdapter`/`build_provider_prompt`/`collect_artifact_context`/`ensure_adapter_ready`.
Escopo IN: extração + adaptação da TUI para consumir o módulo + testes de equivalência. Escopo OUT: engine.
Arquivos prováveis: `src/runtime/stage_runner.rs` (novo), `src/runtime/mod.rs`, `src/tui/runner.rs`.
Validação: TUI compila e funciona; teste de equivalência da saída de geração antes/depois; `clippy`/`test`/`check`.
AC: Dado a TUI gerando um artefato, Quando a geração roda pelo módulo extraído, Então o resultado é equivalente ao anterior. Relatório: diff de fronteira, sink usado, riscos de regressão.

### SDD-OAD-003 — `Demand` + fila `.sdd/queue/`
Objetivo: criar `src/domain/orchestrator/demand.rs` (struct `Demand` com `schema_version:1`, id, type, title, description, source, priority, created_at, status, `slug`) e `queue.rs` (enqueue, dedup por `id`, list). `slug` derivado do título e **validado contra path traversal** (sem `..`, separador, symlink, abs) e contra colisão de hint.
Escopo IN: modelo + fila + testes. Escopo OUT: engine/tick.
Arquivos prováveis: `src/domain/orchestrator/{mod,demand,queue}.rs`.
Validação: testes de dedup (mesmo id não duplica), de slug inválido rejeitado, de `schema_version` divergente → erro.
AC: Dado uma demanda nova, Quando enfileirada, Então persiste 1 arquivo; Dado o mesmo id de novo, Então não duplica. Relatório: schema final, regras de slug, testes.

### SDD-OAD-004 — Estado do motor `.sdd/state/<slug>.json`
Objetivo: `src/domain/orchestrator/state.rs` com `schema_version`, cursor, status (`queued|orchestrating|awaiting_approval|ready_for_exec|paused|error|manual:*`), lock, approvals, attempts. Escrita **atômica** (write-temp+rename), reconstrução na carga, rejeição de `schema_version` divergente. **Mapeamento Opção A**: os estados ricos vivem só aqui; nunca escrever `awaiting_approval`/`ready_for_exec` no campo por-artefato do map.
Escopo IN: persistência + lock + testes. Escopo OUT: lógica de avanço.
Arquivos prováveis: `src/domain/orchestrator/state.rs`.
Validação: teste de escrita atômica, lock impede 2º writer, reconstrução pós “restart” (reler arquivo), `schema_version` divergente → erro.
AC: Dado um estado salvo, Quando o processo recarrega, Então o estado é idêntico; Dado dois ticks concorrentes, Então só um adquire o lock. Relatório: contrato do arquivo, política de divergência state↔map.

### SDD-OAD-005 — `engine::tick` ingestão
Objetivo: `src/domain/orchestrator/engine.rs` — `tick()` que detecta demanda nova, faz dedup, cria a orquestração (`docs/<slug>/`, reusando a parte de scaffold de `run_orchestration` in-process) e marca estado `idea`. Usar `FakeRunner` (stub determinístico) para a geração nesta task.
Escopo IN: ingestão + FakeRunner + testes. Escopo OUT: avanço multi-etapa, gates.
Arquivos prováveis: `src/domain/orchestrator/engine.rs`, helper de scaffold reusável.
Validação: teste ingestão cria dir + estado idea; demanda duplicada ignorada; nada escrito fora de `docs/<slug>`/`.sdd`.
AC (PRD): "Demanda nova inicia a orquestração automaticamente" e "Demanda duplicada não cria segunda orquestração". Relatório: o que foi reusado de `run_orchestration`, FakeRunner.

### SDD-OAD-006 — `engine::tick` avanço até gate
Objetivo: estender `tick()` para avançar o planejamento (Idea→PRD→TechSpec→Tasks→Refinamento) usando `tui::stage::{current_stage, can_navigate}` e o `StageRunner` (FakeRunner), parando ANTES de executar a etapa seguinte quando a concluída é um gate.
Escopo IN: avanço + integração com StageRunner trait + testes. Escopo OUT: pausa/notify (007), aprovação (008), runner real (009).
Arquivos prováveis: `src/domain/orchestrator/engine.rs`.
Validação: com FakeRunner, da Idea ao gate do PRD; ordem respeitada; idempotência (tick repetido não reexecuta etapa cumprida).
AC: Dado uma demanda em `idea`, Quando ticks rodam, Então avança até o primeiro gate e para. Relatório: contrato `StageRunner`, pontos de idempotência.

### SDD-OAD-007 — `gate` (pausa + notify)
Objetivo: `src/domain/orchestrator/gate.rs` — ao concluir PRD/TechSpec/Refinamento, gravar `awaiting_approval` no `.sdd/state` (não no map por-artefato), montar `GateRequest` (o quê + origem + link local + riscos) e emitir via `hook notify` **escapado** (sem interpolar dado externo em AppleScript/shell).
Escopo IN: gate + request + notify seguro + testes. Escopo OUT: decisão (008), Slack real (014).
Arquivos prováveis: `src/domain/orchestrator/gate.rs`, ajuste no caminho de `notify`.
Validação: teste de pausa em cada gate; teste de escaping (título com `"`/`$()` não injeta).
AC: Dado PRD concluído, Quando alcança o gate, Então estado `awaiting_approval` e pedido emitido, sem avançar. Relatório: formato do GateRequest, prova de escaping.

### SDD-OAD-008 — `approval` (decisão humana segura)
Objetivo: `src/domain/orchestrator/approval.rs` — registrar `Decision{slug,gate,decision,author,channel,ts,reason}`; novo bloco `approvers` em `sdd.config.yaml` (cli/slack); autor CLI derivado de `git config user.email` checado contra allowlist; **default-deny** (allowlist vazia ⇒ gate não aprovável); **o motor não tem caminho que escreva `approve`**. Retomada idempotente após aprovação.
Escopo IN: decisão + allowlist + retomada + testes. Escopo OUT: comandos CLI (012), Slack (014).
Arquivos prováveis: `src/domain/orchestrator/approval.rs`, `src/domain/providers.rs` (config), `sdd.config.yaml`.
Validação: aprovação de não-autorizado rejeitada; allowlist vazia ⇒ nega; tick não cria aprovação; retomada não reexecuta etapa.
AC (PRD): "Retomada sobrevive a reinício"; "Reprovação devolve a etapa". Relatório: schema `approvers`, fonte do autor, prova de não auto-aprovação.

### SDD-OAD-009 — Plugar geração real
Objetivo: o engine consome `runtime::stage_runner` (em vez do FakeRunner) para gerar cada etapa; grava o artefato **direto no nome canônico**; valida seções com `required_sections()` no caminho antes de persistir; atualiza o map in-process (`recorded`).
Escopo IN: wiring + validação + testes com runner real gated. Escopo OUT: redação ampliada (010), deny-check (011).
Arquivos prováveis: `src/domain/orchestrator/engine.rs`, `src/runtime/stage_runner.rs`.
Validação: teste gated com provider; teste de que artefato inválido (faltando seção) não é persistido; nome canônico sempre.
AC: Dado uma etapa elegível, Quando o runner gera, Então o artefato válido é salvo no nome canônico; inválido é rejeitado. Relatório: integração, tratamento de `RunnerError::NotHeadless` → `manual:*`.

### SDD-OAD-010 — Redação ampliada em todos os writers
Objetivo: expandir `src/runtime/redaction.rs` para Slack (`xoxb-`/`xoxp-`), GitHub (`ghp_`/`gho_`/`github_pat_`), GitLab (`glpat-`), JWT (`eyJ`), AWS (`AKIA`), PEM, URL com credencial e e-mail/PII; aplicar `redact_text` em **todos** os novos writers (queue json, state json, orchestrator.jsonl, map, transcript).
Escopo IN: regex + aplicação + teste por formato. Escopo OUT: lógica de motor.
Arquivos prováveis: `src/runtime/redaction.rs`, writers em `orchestrator/*` e `runtime/*`.
Validação: teste plantando 1 valor por formato e afirmando `[REDACTED]` em cada writer.
AC: Dado um segredo de qualquer formato suportado, Quando gravado, Então aparece redigido. Relatório: formatos cobertos, writers cobertos, limitação (defesa-em-profundidade).

### SDD-OAD-011 — Entrada não confiável + path-allowlisting
Objetivo: tratar a demanda como dado não confiável: compor o prompt com os campos da demanda em **bloco delimitado/rotulado** e uma instrução de sistema fixa que proíbe o modelo emitir comandos shell/segredos/release; limite de tamanho/sanitização; **deny-check de conteúdo** no gate de artefato (blocos `git push|merge|deploy`, `curl|sh`, padrões de segredo); **path-allowlisting** confinando escrita do motor a `root/docs/<slug>/` e `root/.sdd/`.
Escopo IN: composição segura + deny-check + confinamento + testes. Escopo OUT: outros writers.
Arquivos prováveis: `src/runtime/stage_runner.rs`, `src/domain/orchestrator/{engine,gate}.rs`, validação de path util.
Validação: payload adversarial não vira instrução; artefato com bloco perigoso é barrado; escrita com `../` é rejeitada.
AC: Dado uma demanda adversarial, Quando o artefato é gerado, Então não contém comando de release/segredo e o deny-check bloqueia o perigoso. Relatório: estratégia anti-injeção, paths confinados.

### SDD-OAD-012 — CLI `sdd demand ...`
Objetivo: subcomandos `enqueue`, `list`, `status [<id>]`, `approve <id> --gate --by [--note]`, `reject <id> --gate --reason`, todos com `--json`. Reusa `queue`/`approval`. Não colide com comandos existentes.
Escopo IN: parsing clap + saída determinística + testes. Escopo OUT: loop `auto` (013).
Arquivos prováveis: `src/main.rs` (dispatch), `src/domain/orchestrator/*`.
Validação: testes de cada comando (enqueue→list mostra; approve não-autorizado falha; `--json` estável).
AC: Dado `enqueue`, Quando `list`, Então a demanda aparece; `approve` por autorizado avança. Relatório: superfície CLI, formato `--json`.

### SDD-OAD-013 — `sdd auto tick/run` + auditoria
Objetivo: `sdd auto tick [--max]` (um passo) e `sdd auto run [--watch] [--interval] [--max-concurrency]` (laço fino sobre tick; evento de filesystem ou intervalo; sem busy-poll; concorrência limitada). Auditoria append-only redigida em `.sdd/orchestrator.jsonl`.
Escopo IN: comandos + observer + jsonl + testes. Escopo OUT: adapters externos.
Arquivos prováveis: `src/main.rs`, `src/domain/orchestrator/engine.rs`, util de watch.
Validação: `auto tick` idempotente; `run` respeita concorrência; jsonl redigido; sem busy-poll (intervalo respeitado).
AC: Dado a fila, Quando `auto run`, Então processa respeitando concorrência e registra eventos. Relatório: modelo do observer, campos do jsonl.

### SDD-OAD-014 — Adapters (local default + Slack assinado)
Objetivo: traits `WorkItemAdapter/DocAdapter/ChatAdapter/RepoAdapter` em `src/runtime/adapters/`; `LocalAdapter` default (docs/map/jsonl); `SlackAdapter` com `post_gate` e `verify_and_parse` (HMAC `X-Slack-Signature`, janela ≤5 min, anti-replay por event_id, mapeamento user-id→approver); `Jira`/`Confluence` stubs habilitados por config que falham claro se sem credencial. Nenhuma credencial lida quando `local`.
Escopo IN: traits + local + slack(assinatura) + stubs + testes. Escopo OUT: wiring de produção real.
Arquivos prováveis: `src/runtime/adapters/{mod,local,slack,jira,confluence}.rs`, `sdd.config.yaml` (seleção).
Validação: callback Slack sem assinatura/expirado/replay é rejeitado; default local não exige credencial.
AC: Dado adapter `local`, Então tudo funciona sem rede; Dado Slack, Quando callback inválido, Então rejeitado. Relatório: contrato dos adapters, verificação de assinatura.

### SDD-OAD-015 — TUI esteira + fechamento
Objetivo: estender a TUI (`scan_orchestrations`) para listar demandas em fila e destacar `awaiting_approval` com cor de alerta; rodar a suíte completa de não regressão e produzir evidências (restart, não vazamento, não auto-aprovação, anti-spoof). Confirmar fluxo manual intacto.
Escopo IN: TUI + QA final + evidências. Escopo OUT: novas features.
Arquivos prováveis: `src/tui/{app,ui,orchestration}.rs`, testes.
Validação: `cargo test` completo verde; TUI mostra fila e gate; checklist de evidências do PRD/risco preenchido.
AC: Dado a esteira, Quando há item em gate, Então é destacado; suíte completa verde. Relatório: screenshots/saída da TUI (texto), matriz de evidências final.
