AUR-534: Toolchain Go suportado e atualizado por automacao
Documento historico do board. Para uso atual, consulte o guia do produto.
second-reader-go-v1 / go-git-offline-v1: legado congelado, nao um bloqueio atual
.board/bin/second-reader usa .board/locks/oci/second-reader-go-v1.lock.json
(golang@sha256:2414035b..., go1.21.13, GOTOOLCHAIN=local forcado) e
go-git-offline-v1 pina golang@sha256:4746d264... (tambem go1.21.13).
Nenhum dos dois foi trocado por este card -- a instrucao reserva essa troca
ao coordenador, e ambos pinam uma imagem golang pura, nao a
aurum-bootstrap-go-bash deste card. Confirmado por execucao direta: um
go build contra um go.mod com go 1.27 dentro de qualquer uma das duas
falha com go: go.mod requires go >= 1.27 (running go 1.21.13;
GOTOOLCHAIN=local).
Essa execucao so importa, porem, onde esses dois locks sao de fato
chamados: .board/validate.sh (unico lugar que invoca
bin/second-reader) e .board/bin/second-reader em si. Por
.board/README.md ("## Operating model", 2026-08): "O processo atual nunca
invoca .board/validate.sh; esse script e sua ceremonia de second-reader
sao evidencia legada congelada, nao um portao de sessao." Nenhum card em
backlog/ready/doing/review/validating/blocked-on-owner declara
go-git-offline-v1, e o second-reader so roda via validate.sh, que o
processo atual nao chama. Nao e, portanto, um bloqueador de integracao
para o fluxo leve atual -- e um gap real para o dia em que
validate.sh/second-reader seja reativado (ou para uma evidencia done
anterior re-verificada com --verify), e por isso fica registrado aqui,
mas nao impede este card de integrar nem bloqueia revisao de nenhum card
Go corrente.
O que mudou
go.mod passa de go 1.21 para go 1.27. Go 1.21 saiu de suporte em 2024;
uma ferramenta de compliance nao pode rodar sobre uma toolchain sem correcoes
de seguranca. Go 1.27 e a versao estavel atual na familia Go 1.2x (1.26 perde
suporte antes da proxima janela de manutencao deste board).
Nenhuma dependencia muda. go.sum e byte-idêntico antes e depois do salto
(sha256:b9142883d8f8e46998f705fbcd9964d6f386e17381ddf95a0c3173c832ce25e8);
so a diretiva go em go.mod muda.
Imagem do toolchain
Nova imagem, de Dockerfile versionado no repositorio em
.board/oci/images/go-bash-1.27/Dockerfile:
FROM golang:1.27.1-alpine3.24@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414
RUN apk add --no-cache bash
WORKDIR /opt/aurum-a006
COPY go.mod go.sum ./
RUN GOTOOLCHAIN=local GOPROXY=https://proxy.golang.org GOSUMDB=sum.golang.org go mod download
COPY .board/oci/images/go-bash-1.27/Dockerfile /opt/aurum-a006/toolchain.Dockerfile
ENV GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off GONOSUMDB=*
Mesmo padrao de bootstrap-readonly-v1/Dockerfile: base pinada por digest,
go.mod/go.sum reais do repositorio copiados no build (modulo cache
assado na imagem, ja que a sealed run roda offline). Duas diferencas
deliberadas:
- Sem
ENTRYPOINT ["/bin/bash"].go-sealed,go-sharedego-livechamam a imagem com um comando explicito (go .../sleep infinity/...) sem--entrypoint=; herdar o entrypoint de bash dobootstrap-readonly-v1faria cada chamada virar/bin/bash go ...e morrer.oci-runzera o entrypoint por conta propria, entao nao precisa de nenhum dos dois. - A propria Dockerfile e copiada para dentro da imagem
(
/opt/aurum-a006/toolchain.Dockerfile), junto com ogo.mod/go.sumassados. E a base da prova de AC-004: a suite roda dentro da imagem sem Docker disponivel, entao o unico jeito de provar "esta imagem nasceu deste Dockerfile exato, com estego.mod/go.sumexatos" de dentro do sandbox e comparar byte a byte o que foi assado contra o que foi materializado.
Build (fora de qualquer card, para reconstruir a imagem apos uma mudanca):
docker build -f .board/oci/images/go-bash-1.27/Dockerfile \
-t aurum-bootstrap-go-bash:1.27.1-<data> .
docker image inspect -f '{{.Id}}' aurum-bootstrap-go-bash:1.27.1-<data>
O .Id resultante (neste Docker 29, com o containerd image store, igual ao
unico RepoDigests da imagem recem-construida) e o digest que entra em cada
lock como aurum-bootstrap-go-bash@sha256:<id>. Confirmado para esta troca:
sha256:99bbed33cc0ac22f6fe947f589b82930dff841c35e6f8817f9c9fbae2adedb40
Esse .Id nao e bit-reproduzivel entre rebuilds. apk add --no-cache
bash resolve a versao mais recente de bash (e das libs Alpine que ela
traz) no momento do build, sem pin de versao -- um rebuild dias depois, com
um pacote bash novo no repositorio Alpine, produz um .Id diferente
mesmo com o mesmo Dockerfile e o mesmo go.mod/go.sum. O que e estavel
entre rebuilds e o CONTEUDO funcional (base Go pinada por digest, bash
presente, modulo cache baixado dos mesmos go.mod/go.sum), nao o
byte-a-byte da imagem. Por isso o digest do lock precisa ser recalculado a
cada rebuild (a cadeia de digest acima existe exatamente para isso), e por
isso nenhuma automacao pode assumir que o .Id de hoje continua valido
amanha so porque o Dockerfile nao mudou.
Smoke test (replica o probe do oci-run):
docker run --rm --pull=never --network=none --read-only \
--user 65534:65534 --cap-drop=ALL \
--tmpfs /tmp:rw,nosuid,nodev,size=128m --entrypoint= \
aurum-bootstrap-go-bash@sha256:99bbed33cc0ac22f6fe947f589b82930dff841c35e6f8817f9c9fbae2adedb40 \
bash -c 'command -v bash && go version'
Cadeia de digest
oci-run nunca le registry.v1.json; o unico caminho que vale e
.board/oci/profiles/<nome>.json + .board/locks/oci/<nome>.lock.json. A
troca:
imageem cada lock*.lock.jsonpassa aaurum-bootstrap-go-bash@sha256:99bbed33cc0ac22f6fe947f589b82930dff841c35e6f8817f9c9fbae2adedb40.lock_digestem cada profile.json(o campo queoci-runde fato valida contrasha256sumdo arquivo de lock) e recalculado do lock novo..board/oci/profiles/registry.v1.json(registro secundario, nao lido por nenhum script encontrado neste pass -- so documental) temlock_digesteimage_set_digestrecalculados por entrada, usando o campolockde cada entrada, nao o nome da chave. Isso importa porque a entradabootstrap-readonly-v1desse arquivo apontalockpararegistry-v1.lock.json, nao parabootstrap-readonly-v1.lock.json-- uma inconsistencia que ja existia antes deste card e que este card nao corrige, so preserva corretamente (o lock_digest de uma entrada tem que bater com o arquivo que a propria entrada nomeia, nao com o arquivo cujo nome parece corresponder).image_set_digest=sha256(printf '%s' "aurum-bootstrap-go-bash@sha256:<digest>"), sem newline final -- formula confirmada reproduzindo o valor atual (c12ef435...) contra a imagem antiga antes de escrever qualquer valor novo.
Locks trocados (10, todos os que pinavam
aurum-bootstrap-go-bash@sha256:e8521445a6c1bc7c017258e9c71caf8eb78ee254862582c5d57d8a79d9413084):
bootstrap-readonly-v1, docs-tool-offline-v1, fake-provider-v1,
fake-scm-offline-v1, go-unit-offline-v1, oci-conformance-v1,
parser-worker-v1, polyglot-toolchain-v1, registry-v1,
sqlite-offline-v1. (O briefing inicial deste card citava 12; a contagem
real na arvore e 10 -- recomputada, nao assumida.)
Locks nao trocados, por pinarem uma imagem golang diferente, nao a
aurum-bootstrap-go-bash: go-git-offline-v1
(golang@sha256:4746d26432a9117a5f58e95cb9f954ddf0de128e9d5816886514199316e4a2fb)
e second-reader-go-v1
(golang@sha256:2414035b086e3c42b99654c8b26e6f5b1b1598080d65fd03c7f499552ff4dc94).
Nenhum card em backlog/ready/doing/review/validating/blocked-on-owner
declara esses dois profiles hoje (so done/cancelled), entao a troca nao
bloqueia nenhum trabalho em andamento.
.board/bootstrap/locks/go.yml e .board/bootstrap/locks.yml
go_version passa a 1.27; go_mod_sha256 e recalculado do go.mod novo
(go_sum_sha256 fica igual, porque go.sum nao mudou). O identity em
locks.yml e recalculado pela formula exata de .board/bootstrap/verify.sh
(sha256 das linhas AUR-xxx sha256:<digest> de cada um dos 6 filhos, em
ordem lexicografica, com newline final) -- reproduzida primeiro contra o
valor atual antes de ser confiada, e depois confirmada rodando
bash .board/bootstrap/verify.sh .board/bootstrap, que retorna ok com o
identity novo.
Automacao (AC-003)
.github/dependabot.yml cobre tres ecossistemas, todos semanais: gomod
(raiz), docker (raiz e .board/oci/images/go-bash-1.27, a nova
imagem) e github-actions. Limite conhecido: o Dependabot nao interpreta
imagem Docker dentro de uma linha run: de workflow, nem dentro de um
Makefile, nem uma variavel (GO_IMAGE ?= ...) -- ele so atualiza um
FROM em Dockerfile e a referencia de uma uses: action. As duas linhas
docker run ... golang:1.27.1-... em .github/workflows/ci.yml e as tres
referencias de imagem em Makefile (GO_IMAGE default e o golang:1.27.1-
bookworm fixo do target test-race) continuam precisando de edicao manual
quando a proxima versao sair. Isso nao bloqueia AC-003 (que pede cobertura
dos tres ecossistemas, nao automacao total de toda referencia de imagem),
mas e um limite real, nao "CI totalmente automatizado".
Terceiro limite: tests/acceptance/AUR-534.sh tambem fixa 1.27.1 e os
dois digests de base (alpine3.24, bookworm) como literais no proprio texto
do script (AC-001 e AC-004). Um bump futuro para 1.27.2 (ou 1.28.x)
precisa editar esses literais tambem -- nao e so go.mod + Dockerfile +
Makefile + ci.yml + os locks, e mais um lugar na lista de "o que esta
automacao nao fecha sozinha".
Segundo limite, mais importante: mesmo o FROM que o Dependabot consegue
editar (.board/oci/images/go-bash-1.27/Dockerfile) nao fecha o ciclo por
si so. Um PR do Dependabot muda o digest base; ele nao builda a imagem, nao
calcula o novo aurum-bootstrap-go-bash@sha256:..., e nao atualiza os 10
locks, os 10 lock_digest de profile, nem o registry.v1.json. Esse e
exatamente o trabalho manual que a secao "Cadeia de digest" acima descreve
-- docker build + docker image inspect -f '{{.Id}}' + reescrever cada
lock/profile/registry com o digest novo. "Atualizado por automacao" (o
titulo deste card) vale para a DETECCAO de uma versao nova disponivel, nao
para o fechamento de toda a cadeia de digest sem intervencao humana.
Referencias historicas fora do alcance deste card (nao editadas)
Achadas por grep dos digests antigos fora de .board/locks/oci,
.board/oci/profiles e .board/bootstrap (os unicos caminhos que este card
possui). Nao sao paths deste card; ficam estaticas, como prova historica
do que rodou no passado, nao como afirmacao do estado atual:
.board/evidence/**(muitos cardsdone): evidencia congelada de execucoes passadas contra a imagem antiga.forbidden_pathsdeste card proibe tocar.board/evidence; correto nao editar..board/research/legacy-files.tsv: inventario historico do AUR-001, registra o digest antigo dego.mode dogo.ymlantigo.docs/specs/AUR-457.md,docs/specs/AUR-360.md: specs de cardsdoneque citam o digest antigo da imagem/go.mod como registro do que foi executado na epoca.tests/bootstrap/locks/AUR-360/**(fixtures ecases.tsv): fixtures autocontidas do validador de lock do AUR-360, com copias proprias dego.ymle seus proprios digests esperados -- nao derivam dogo.ymlreal e continuam internamente consistentes sem mudanca.tests/e2e/AUR-486.sh,tests/acceptance/AUR-457.sh: usamgo 1.21como conteudo de scratch (umgo.modsintetico descartavel criado dentro do proprio teste), nao como declaracao da toolchain do projeto.
Rollback
Reverter go.mod e as imagens (Dockerfile raiz, Dockerfile da nova imagem,
os 10 locks, os profiles, registry.v1.json, go.yml, locks.yml) para o
estado anterior a este commit. Nenhum dado de usuario envolvido.
Validacao
go vet ./...,go build ./...ego test -count=1 ./...rodados viago-sealedcomGO_SEALED_IMAGE=aurum-bootstrap-go-bash:1.27.1-2026-10-02eGO_SEALED_MODCACHE=/nonexistent(para nao herdar o modcache do host e provar que o modcache assado na imagem basta):go vetlimpo,go testpassa nos 35 pacotes da arvore completa, exit 0.go mod tidy -diff, no mesmo ambiente, falha por dois motivos pre-existentes e fora do escopo deste card:tests/contracts/AUR-309.go(atras de//go:build aur309_characterization, inativa por padrao) importa um pacote que nunca existiu (tests/characterization/legacy/pipeline); egopkg.in/check.v1(usado so pelos testes internos dogopkg.in/yaml.v3, nunca pelo codigo deste repositorio) nao esta no modulo cache assado eGOPROXY=offimpede buscar. Os dois ja existiam identicos emmainantes deste card (confirmado viagit show main:...); nenhum dos dois e causado pelo salto de versao, e corrigi-los excede "o que o salto exige" (Non-goals)..board/tests/go-profile-regression.sh(prova real do AUR-508 para o profilego-unit-offline-v1, independente deste card) roda contra o profile e o lock ja trocados:real Go PASS, mutated Go RED, lock/privilege/runtime rejected, exit 0.tests/acceptance/AUR-534.sh all, rodado diretamente contra a imagem nova com as MESMAS flags de container que ooci-runusa, mas com a worktree inteira montada (nao so ospaths/read_pathsdeste card):AUR-534/all/pass, exit 0. Cada selector roda isolado com exit 0, AC-002 inclusive (go test ./...passa nos 35 pacotes); a mutacao adversarial de AC-001-MUT-001 testa DUAS mutacoes independentes sobre uma copia limpa de cada entrada quecheck_ac001le -- reintroduzir1.21so emci.yml(Makefile limpo) e, separadamente, so emMakefile(ci.yml limpo) -- e exige quecheck_ac001, a funcao real do gate, caia nas duas, nao so que o helper interno de scan "funcione" em isolamento. Isso prova queMakefile(adicionado aosscan_targetse aos checks de pin degolang:1.27.1-alpine3.24/bookwormpor digest nesta revisao) esta realmente coberto por AC-001, nao so citado.- O
acceptreal do card --./.board/bin/oci-run --profile go-unit-offline-v1 --card AUR-534-- retorna exit 0. A versao 4 do card (mesclada demainneste pass) ampliouread_pathspara[cmd, internal, pkg, action.yml, scripts, .board/bin, tests, demo, standards, docs/specs, .board/schemas, .board/research, .board/decisions], cobrindotests/fixtures/repos/git-demo/repo.git,tests/integratione.board/schemas/task-spec.schema.json-- os tres caminhos externos que a suite completa precisa e que a v3 do card nao possuia nem lia. Com isso, dentro do proprio sandbox dooci-run, os 35 pacotes dego test ./...passam de fato (nao um subconjunto), AC-002 inclusive.check_ac002nao hardcoda mais esses tres caminhos como guarda de infra: generaliza para qualquer falha de resolucao de pacote emgo build ./...(mensagem de "cannot find package" ou equivalente) virarinfra()em vez de vermelho comportamental, sem depender de uma lista fixa que pode ficar obsoleta a proxima vez queread_pathsmudar.