sdd-layer 0.25.3

Spec-Driven Development CLI and agent harness
# ARTIFACT-GENERATION.md — Contrato de geração de artefatos SDD

Documentação técnica do pipeline unificado de geração de artefatos introduzido
pelas tasks T-01 a T-15 da orquestração
`geracao-mais-robusta-e-organizada-redesenhar-pipeline-de-geracao-de-artefatos-do-sd-7baa90d82a5a`.

---

## Visão geral

Antes do refactor (baseline T-01), o SDD tinha três funções paralelas de
renderização em `src/main.rs`:

- `render_stage_artifact_body` — renderizava o corpo Markdown de stages.
- `render_stage_artifact` — ponto de entrada do CLI para stages.
- `render_discovery_artifact_body` / `render_discovery_html_artifact` — discovery.

Cada uma duplicava lógica de rastreabilidade, telemetria e ordenação de seções.
O pipeline unificado centraliza esse contrato em `src/domain/artifact.rs` e
`src/domain/html.rs` (feature `html-gen`).

---

## Trait `ArtifactGenerator`

**Arquivo:** `src/domain/artifact.rs` (linha 98)

```rust
pub trait ArtifactGenerator {
    fn generate_markdown(&self, ctx: &GenerationContext<'_>) -> Result<String>;

    #[cfg(feature = "html-gen")]
    fn generate_html(&self, ctx: &GenerationContext<'_>) -> Result<String>;
}
```

Contrato compartilhado por CLI e TUI (RF-12, RF-13). Qualquer gerador concreto
implementa `generate_markdown` (sempre disponível) e, quando a feature `html-gen`
está ativada, `generate_html`.

---

## `GenerationContext<'a>`

**Arquivo:** `src/domain/artifact.rs` (linha 69)

```rust
pub struct GenerationContext<'a> {
    pub stage: &'a str,          // ex: "idea", "prd", "tasks", "project-discovery"
    pub name: &'a str,           // nome da orquestração
    pub input: &'a str,          // entrada do usuário para o stage
    pub root: &'a Path,          // raiz do projeto
    pub provider: &'a ProviderSelection,
    pub format: ArtifactFormat,  // Markdown (padrão) ou Html
    pub real_telemetry: Option<Telemetry>, // telemetria real do provider; None = scaffolding local
    pub no_telemetry: bool,      // flag --no-telemetry
}
```

É o único objeto que os geradores recebem. Substitui os parâmetros avulsos que
cada função de renderização recebia antes do refactor.

---

## Quando telemetria real é emitida — `should_emit_telemetry`

**Arquivo:** `src/domain/artifact.rs` (linha 85)

```rust
pub fn should_emit_telemetry(&self) -> bool {
    self.real_telemetry.is_some() && !self.no_telemetry
}
```

Regra única (RF-03, RN-01):

| `real_telemetry` | `no_telemetry` | emite bloco? |
|---|---|---|
| `None` | qualquer | **não** — scaffolding local, sem provider |
| `Some(_)` | `false` | **sim** — provider real, sem flag de supressão |
| `Some(_)` | `true` | **não** — provider real + `--no-telemetry` |

Telemetria nunca é estimada localmente. O bloco só aparece quando variáveis
`SDD_USAGE_*` e `SDD_GENERATION_*` foram populadas pelo provider real (lidas
em `render_stage_artifact_with_ctx` — `src/main.rs:12543`).

---

## `DeterministicGenerator` vs `ProviderGenerator`

**Arquivo:** `src/domain/artifact.rs` (linhas 115–153)

| Tipo | Quando usado | Telemetria |
|---|---|---|
| `DeterministicGenerator` | `real_telemetry = None` — scaffolding local | nunca emite |
| `ProviderGenerator` | `real_telemetry = Some(_)` — chamada real ao provider | emite quando `should_emit_telemetry() = true` |

Ambos delegam para `render_stage_artifact_with_ctx` (`src/main.rs:12543`), que
centraliza a leitura de env e a decisão de emissão. A diferença entre os dois
geradores é semântica (documenta a intenção no tipo) — a lógica de emissão está
em `should_emit_telemetry`, não duplicada nos dois.

`generate_html` (quando disponível) chama `generate_markdown` internamente e
converte o Markdown resultante via `StageHtmlGenerator` (`src/domain/html.rs:771`).
Isso garante paridade byte-semântica entre as saídas Markdown e HTML.

---

## `StageGenerator` — dispatch estático

**Arquivo:** `src/domain/artifact.rs` (linha 163)

```rust
pub enum StageGenerator {
    Deterministic(DeterministicGenerator),
    Provider(ProviderGenerator),
}

impl StageGenerator {
    pub fn select(ctx: &GenerationContext<'_>) -> Self {
        match ctx.real_telemetry {
            Some(_) => StageGenerator::Provider(ProviderGenerator),
            None   => StageGenerator::Deterministic(DeterministicGenerator),
        }
    }
}
```

Evita `Box<dyn ArtifactGenerator>`. O dispatch é estático (monomorphization),
sem alocação de heap. CLI e TUI chamam `StageGenerator::select(&ctx)` e invocam
`generate_markdown` ou `generate_html` no resultado.

---

## Ordem canônica de seções (RN-07, RF-01)

A ordem dos blocos `## Section` em qualquer artefato gerado pelo pipeline:

```
# Título - Nome da orquestração

## Resumo
## [seções de conteúdo do stage — ex: Objetivos, Contexto, Critérios…]
## Diagramas
## Rastreabilidade          ← penúltima (antes era a primeira — mudança de T-03)
## Histórico de revisão
```

**Exceção — ADR:** a seção `## Rastreabilidade` aparece no rodapé, como nas
demais etapas. Não há exceção estrutural para ADR na ordem canônica.

A ordem é definida em `schemas/sdd-contract.yaml` e validada por
`src/contract.rs::required_sections()`. O arquivo `schemas/artifact-sections.json`
espelha o contrato para validação externa (`sdd validate-artifact`).

---

## Flags de CLI

### `--format`

**Arquivo:** `src/main.rs` (enum `OutputFormat`, uso em `StageArgs`)

```
sdd idea --format html "minha feature"
sdd prd  --format md   "minha feature"   # padrão
```

Valores aceitos: `html`, `md` (default), `json` (aceito pelo parser mas não
implementado para artefatos de stage — retorna Markdown).

Internamente, `OutputFormat` converte para `ArtifactFormat` via `impl From`:

```rust
// src/main.rs:848
impl From<OutputFormat> for crate::domain::artifact::ArtifactFormat {
    fn from(f: OutputFormat) -> Self {
        match f {
            OutputFormat::Html => ArtifactFormat::Html,
            OutputFormat::Md | OutputFormat::Json => ArtifactFormat::Markdown,
        }
    }
}
```

### `--no-telemetry`

**Arquivo:** `src/main.rs` (campo `no_telemetry` em `StageArgs`)

```
sdd idea --no-telemetry "minha feature"
```

Suprime o bloco de telemetria mesmo quando `SDD_USAGE_*` estão definidas.
Propagado para `GenerationContext.no_telemetry`; a decisão final é de
`should_emit_telemetry()`.

---

## Feature `html-gen`

**Arquivo:** `Cargo.toml`

```toml
[features]
html-gen = ["askama"]

[dependencies]
askama = { version = "0.12", optional = true }
```

Ativa:
- `ArtifactGenerator::generate_html` no trait e nas implementações.
- O módulo `src/domain/html.rs` completo (templates Askama, parser Markdown→HTML).
- O subcomando `sdd artifact render`.

Sem a feature, o binário é menor e nenhum símbolo HTML é compilado.

**Dependências transitivas introduzidas** (resultado de `cargo tree --features html-gen | grep askama`):

```
├── askama v0.12.1
│   ├── askama_derive v0.12.5 (proc-macro)
│   │   ├── askama_parser v0.2.1
│   ├── askama_escape v0.10.3
```

Quatro crates: `askama`, `askama_derive`, `askama_parser`, `askama_escape`.
Nenhuma dependência de runtime adicional além das já presentes no projeto.

---

## Templates Askama

**Diretório:** `templates/html/`

| Arquivo | Descrição |
|---|---|
| `templates/html/base.html` | Template base: `<head>`, nav lateral, área de conteúdo, scripts Mermaid |
| `templates/html/discovery.html` | Estende `base.html`; injeita `content_html` com `\|safe` |
| `templates/html/stage.html` | Estende `base.html`; idem para stages genéricos |
| `templates/html/partials/toc.html` | Índice lateral (lista de `TocEntry`) |
| `templates/html/partials/traceability.html` | Bloco de rastreabilidade (lista de `TraceabilityItem`) |
| `templates/html/partials/mermaid_fallback.html` | `<pre class="mermaid-fallback">` emitido junto ao `<div class="mermaid">` |

O template base carrega o script Mermaid via CDN com fallback de `<noscript>`.
RF-11 exige que ambos os elementos (`<div class="mermaid">` e
`<pre class="mermaid-fallback">`) sejam emitidos para cada bloco de diagrama.

---

## Parser Markdown → HTML

**Arquivo:** `src/domain/html.rs` (função `md_to_html`, linha ~485)

Parser line-by-line em Rust puro, sem dependências externas. Implementa:

- Headings `#` a `######``<h2>` a `<h6>` com id de âncora (função `heading_id`).
- Listas `- item``<ul><li>`.
- Tabelas pipe → `<table><thead><tbody>`.
- Blocos mermaid (` ```mermaid `) → `<div class="mermaid">` + `<pre class="mermaid-fallback">`.
- Blocos de código genérico → `<pre><code class="language-X">`.
- Inline: `**bold**`, `*italic*`, `` `code` ``, `[text](url)`.

Limitações documentadas (não implementado): aninhamento de listas, blockquotes,
HTML inline, imagens, referências de link. Suficiente para os artefatos SDD
atuais. Se necessário, substituir por `pulldown-cmark` sem quebrar a API pública
(`render_markdown_file_to_html`, `md_to_html`).

---

## Subcomando `sdd artifact render`

**Arquivo:** `src/main.rs` (enum `ArtifactCommand::Render`, linha ~4531;
handler em `run_artifact`, linha ~28133)

Disponível apenas com a feature `html-gen`.

```
sdd artifact render --format html --stage prd --slug minha-feature [--output out.html]
```

| Parâmetro | Descrição |
|---|---|
| `--format html` | Único formato suportado; outros retornam erro. |
| `--stage` | Stage do artefato (ex: `prd`, `idea`, `tasks`). Mapeado via `contract::stage_file`. |
| `--slug` | Slug da orquestração (determina o diretório `docs/<slug>/`). |
| `--output` | Caminho de saída; default: mesmo caminho com extensão `.html`. |

Internamente chama `render_markdown_file_to_html` (`src/domain/html.rs:1129`),
que lê o arquivo Markdown e delega para `StageHtmlGenerator`.

---

## Paridade CLI / TUI

A TUI (`src/tui/runner.rs`, função `spawn_stage`) usa o mesmo
`GenerationContext` que o CLI. A saída Markdown é byte-idêntica entre CLI e TUI
para o mesmo conjunto de variáveis de ambiente e flags (RF-12, RF-13).
Validado pelo teste de integração `tui_and_cli_produce_identical_stage_output`
em `tests/cli.rs`.

---

## Rastreabilidade

- Origem: `docs/geracao-mais-robusta-e-organizada-redesenhar-pipeline-de-geracao-de-artefatos-do-sd-7baa90d82a5a/04-tasks.md` (T-14)
- Tech Spec: `03-techspec.md` §Contratos, §RF-01 a RF-13, §RN-01/07
- Estado: concluído (T-14 entregue junto com T-15)
- Evidências: `cargo clippy --all-targets --all-features -- -D warnings` → sem issues; `cargo test --features html-gen` → 577 unit + 8 characterization passando