AUR-465 — O site gerado nao anuncia branch mutavel nem promessa vencida
Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.
O que este card entrega
Um guardrail deterministico ao redor do texto que a LLM gera para o topo do
index.md publicado (internal/documentation/welcome/generator.go,
Generator.Generate). O modelo continua livre para escrever a prosa; o que
este card garante e que o resultado NUNCA carrega uma referencia de Action
mutavel nem um link interno inventado, e que o requisito de asset declarado
(_config.yml, logo:) tem uma verificacao reutilizavel com dentes.
O achado e o porque de nao editar site/scaffold.go
O index.md publicado hoje instrui uses: Mpaape/AurumCode@main — um
branch mutavel, o anti-padrao que os workflows de exemplo deste repo ja
corrigiram para a tag v1 (AUR-440). A origem desse texto e a prosa que a
LLM escreve a partir do README.md do consumidor (welcome/generator.go
substitui {{README_CONTENT}} no prompt e manda para o provider); o
internal/documentation/site — dono do _config.yml deterministico e da
listagem de paginas — nunca declara logo: nem escreve um link assim.
internal/documentation/site e read_path deste card, nao path: o card
nao o edita. O que este card pode e deve fazer, dentro de
internal/documentation/welcome, e nunca confiar que o modelo obedece a
instrucao do prompt sozinho — a mesma logica que corrige a instrucao de
templates/welcome-page.md tambem sanitiza o texto que efetivamente sai do
provider, depois da chamada, antes de virar front matter.
Os tres guardrails (internal/documentation/welcome/sanitize.go)
SanitizeActionRef(content) (string, bool)— toda ocorrencia deMpaape/AurumCode@<ref>cujorefnao e a tag semver publicada (v1,v1.0,v1.0.1) nem um SHA completo de 40 hex e reescrita para@v1. Isso cobre@mainexplicitamente e qualquer outro nome de branch por construcao: o teste e "parece tag ou SHA imutavel?", nao uma lista fixa de nomes proibidos. Um SHA completo permanece intocado — e o pin imutavel que os workflows de exemplo ja oferecem a quem tem politica mais estrita.SanitizeInternalLinks(content) (string, bool)— todo link markdown cujo alvo e um caminho relativo local que nao e o guia (docs/getting-started.md) e neutralizado para texto simples, mantendo o rotulo. URLs externas,mailto:e ancoras (#...) nunca sao tocadas. Isto e o Non-goal do card em codigo: o index nao vira guia, ele aponta para o guia, e nenhum outro link inventado sobrevive. A comparacao contra o guia e IGUALDADE EXATA do alvo normalizado (normalizeLinkTarget: remove./inicial e#fragmentofinal, sem resolver..), nuncaContains/HasPrefix/HasSuffix— uma revisao adversarial confirmou queContainsdeixavaevil/docs/getting-started.md/../../etc/passwdenotes/docs/getting-started.md-old.mdsobreviverem intocados (o alvo real nao e o arquivo que o gerador produziu, so contem a substring). O mesmo buraco existiria comHasPrefix(docs/getting-started.md.bakpassaria) ouHasSuffix(internal/attacker/docs/getting-started.mdpassaria); os quatro casos tem teste dedicado emtests/unit/AUR-465.go, provado vermelho contra a versaoContainsantes do conserto.DeclaredAssetPath(configYML) (string, bool)+AssetExists(path, siteRoot) bool— extrai ologo:de um_config.yml(com ou sem aspas) e resolve o caminho contra a raiz do site publicado. Ausencia delogo:e conforme por definicao (a regra e "todo caminho declarado existe, ou nao e declarado"); umlogo:declarado que nao resolve para um arquivo real e o defeito que AC-002 e o mutante MUT-002 verificam.
Generator.Generate chama SanitizeActionRef e depois SanitizeInternalLinks
sobre a resposta da LLM, antes de addFrontMatter — o texto que sai de
Generate (e que a integracao com internal/documentation/site prende acima
da listagem gerada) ja chega saneado.
Non-goals cumpridos
- O index nao virou guia:
templates/welcome-page.mdagora instrui a LLM a nao inventar links de secao e a linkar apenas paradocs/getting-started.md;SanitizeInternalLinkse o cinto de seguranca caso a instrucao seja ignorada. - Nenhum exemplo foi pinado a SHA: a tag
v1continua sendo o alvo do reescrever; um SHA completo ja presente e preservado, nunca forcado.
Validacao deste card
- Unit:
tests/unit/AUR-465.go::TestAUR465— os tres guardrails testados em isolamento, casos de branch/tag/SHA paraSanitizeActionRef, link legitimo vs. inventado vs. externo vs. ancora paraSanitizeInternalLinks, presenca/ausencia de asset paraDeclaredAssetPath/AssetExists. - Integration:
tests/integration/AUR-465.go::IntegrationAUR465— chamaGenerator.Generatede ponta a ponta com umllm.Providermock cuja resposta reproduz o defeito medido (@main+ link inventado), prova que a saida real do entrypoint quecmd/regenerate-docsusa ja sai saneada; e compoeDeclaredAssetPath+AssetExistscontra uma arvore de arquivos real. - E2E:
tests/e2e/AUR-465.sh::E2EAUR465— compila e executa um binario Go standalone (naogo test) contra uma copia de modulo que carrega somentesanitize.go(sem a dependencia deinternal/llm), provando que os guardrails funcionam sozinhos, do jeito que uma ferramenta de consumidor os importaria. - Accept:
./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-465.
Mutantes
- MUT-001 (AC-001): neutralizar
SanitizeActionRefnuma copia de scratch (substituicao vira identidade) reprova a lane unit e a lane e2e, ambas reportandoAUR-465/AC-001/MUT-001. - MUT-002 (AC-002): uma fixture de
_config.ymlque declara um logo que a arvore publicada nunca recebeu reprova a lane e2e, reportandoAUR-465/AC-001/MUT-002, sem tocar em nenhum arquivo rastreado.
Achado nao-bloqueante registrado, fora deste ciclo
pinnedTagPattern (^v[0-9]+(\.[0-9]+){0,2}$) aceita qualquer ref com FORMA
de semver — v2, v10, v1.99 — como se ja fosse a tag publicada, sem
conferir contra o conjunto real (v1, v1.0.0, v1.0.1). Hoje nao e
explorável: nenhum branch com esses nomes existe. Um branch v2 criado
durante o trabalho da proxima major passaria sem reescrita. Fica registrado
para um card separado; nao foi corrigido nesta rodada por prazo.