Test Host Isolation — heavy / risky / graphical tests run on dedicated VMs, not the developer laptop
Toda execução de teste, build pesado, simulação ou cenário que possa travar/consumir excessivamente o host (CPU, RAM, swap, GPU, IO, kernel) acontece em uma **VM de teste dedicada** hospedada em `s.khost1`, **nunca diretamente na máquina do desenvolvedor** (laptop, workstation). As VMs são compartilhadas entre IAs e humanos — para evitar interferência cruzada, cada VM aceita **uma única sessão de execução por vez** (mecânica de lock per-VM, análoga ao `koder-lock` de componentes). VMs cobrem 3 famílias de targets (linux / windows / android) através de qualquer tecnologia de virtualização (LXC, Docker, QEMU/KVM, emulador Android, etc.).
Policy — Test Host Isolation
A máquina onde o desenvolvedor (humano + IA) escreve código *ão*é a mesma máquina onde os testes rodam. Esta separação tem duas razões:
- *stabilidade da sessão.*Builds e testes pesados (Koda self-host,
ISOs, fuzzers, emuladores, stress, soak) regularmente travam o laptop — OOM-killer mata a IDE, swap explode, kernel panics, GPU freezing. Cada incidente custa 10–30 min de reset + perda de contexto de sessão de IA.
- *solamento entre IAs.*O monorepo Koder é editado por múltiplas
sessões de IA simultâneas (Claude, Codex, Gemini, etc.). Quando duas sessões rodam o mesmo emulador / a mesma VM / o mesmo Wayland compositor headless ao mesmo tempo, os outputs colidem silenciosamente (eventos de input intercalados, telas misturadas, arquivos sobrescritos, ADB device 0 disputado). A mecânica de lock per-VM, descrita aqui, impede isso.
Rules
R1 — Toda execução de teste pesado roda em VM remoto
Roda em VM hospedada em s.khost1 (qualquer tecnologia de virtualização, ver §R2) *oda*execução que case com pelo menos um dos critérios abaixo:
| Critério | Exemplo |
|---|---|
make build / make build-safe / make kasm de Koda |
bootstrap stage1 ~7 min, ~2.7 GB RSS nasm + ~600 MB koder = swap pressure |
| Suíte de testes de qualquer componente em modo "full" | /k-test <módulo>, cargo test, go test ./..., flutter test --coverage, pytest -x |
| Test gráfico em modo headless (Chrome headless, Wayland sintético, Xvfb) | flutter test integration_test --device-id chrome, Playwright/Selenium headless |
| Test que sobe emulador Android | qualquer flutter test --device-id emulator-*, /k-reinstall <slug> --device android em fixture |
| Stress / soak / fuzz / chaos | /k-test-gen-{stress,soak,fuzz,chaos}, criterion benches, property-based tests com seeds grandes |
| Build de ISO / image / pacote pesado | lb build do infra/linux/distro, kpkg pack, .deb/.AppImage gigantes |
| Qualquer execução que historicamente já travou esta máquina | listada em meta/context/test-host-isolation/known-hangs.md (catálogo crescente) |
Exceções permitidas no laptop, *em*necessidade de VM:
- Edição de código (LSP, formatters, linters)
- Build incremental / single-file de componente leve (<10s wall, <500MB RSS)
- Teste unitário isolado em código puro (sem subprocess pesado)
- Smoke "hello world" pós
edição (roundtrip <30s, <500MB RSS) - Inspeçãoscripting via
grepfindgit loggh/incus list - Comandos que *onversam*com o VM remoto (
ssh khost1 ...,incus exec ...) mas não rodam carga aqui
R2 — Catálogo de VMs de teste em s.khost1
Toda VM de teste é provisionada em s.khost1 (177.93.107.211, EVEO Vivver LAN, ver meta/context/infrastructure/servers.md §s.khost1).
*onvenção de nomes:*s.khost1.<env>-<target>[-<slug>] onde <env> é um dos códigos canônicos da policies/environments.kmd (dev / stg / prd). Test VMs vivem em dev env por definição — outros envs (stg/prd) só fazem sentido pra cenários explícitos de testecontrastaging ou testedefumaça em produção. Containers de produção da Stack (s.khost1.flow, s.khost1.id, s.khost1.aivoice, …) *ão*levam env prefix porque já são prd por convenção pré-existente.
| Target | Tecnologia preferida | Nomenclatura | Notas |
|---|---|---|---|
| *inux x86_64* | LXC via incus |
s.khost1.dev-linux-<slug> |
Ubuntu 24.04 ou Debian 13 base; share /srv/koder-monorepo via virtiofs / 9p / NFS |
| *inux ARM64* | LXC via incus (com --type=virtual-machine se cross-arch) |
s.khost1.dev-linux-arm64-<slug> |
Pra Koder Linux ARM, kasm ARM, paridade variantes |
| *indows 10/11* | QEMU/KVM | s.khost1.dev-win10 / s.khost1.dev-win11 |
WinRM 5985 + RDP 3389; sucessor do ~/temp/win10-vm local que demora >10min pra subir |
| *ndroid* | Emulador AOSP (sistema) sob QEMU/KVM, ou container redroid` (Docker) |
s.khost1.dev-android-<api> (ex.: dev-android-34) |
ADB exposto via incus proxy na porta 5555; um emulador por VM (evita ADB device 0 disputa) |
| *ontainer leve (linux user-space only)* | Docker / Podman | s.khost1.dev-docker-<image> |
Pra testes que não precisam de kernel customizado |
*rovisioning canônico:*
meta/context/runbooks/test-vm-provisioning.md(a criar — primeira sessão que precisar do VM provisionа e registra o runbook). A criação automatizada viakoder-test-vmCLI é tracking futuro emdev/koder-tools.
*M ≠ servidor de produção.*Containers de *rodução*(
s.khost1.id,s.khost1.flow,s.khost1.hub, etc., listados eminfrastructure/servers.md §Containers ativos) *ão*são VMs de teste. Não rodar teste neles a menos que seja um teste de integração contra produção, declarado explicitamente.
R3 — Lock per-VM: uma sessão de IA por vez
Cada VM de teste aceita *ma única sessão de execução por vez* independentemente de qual IA (Claude, Codex, Gemini, etc.) ou humano está pilotando. O mecanismo de lock é análogo ao lock per-componente (koder-lock / meta/context/notices/active/lock-<slug>.md) mas em um diretório paralelo dedicado a VMs.
*iretório de locks de VM:*meta/context/notices/vm-active/ (separado do vm-active/ vs active/ para não confundir locks de componente com locks de VM — uma sessão pode ter os dois ao mesmo tempo: lock no componente sendo editado + lock no VM onde os testes rodam).
*rquivo de lock:*meta/context/notices/vm-active/lock-<vm-id>.md
Frontmatter obrigatório:
---
vm: s.khost1.dev-linux-<slug> # ID canônico da VM (env prefix canônico — ver policies/environments.kmd)
session: <ai-session-tag> # ex.: claude-2026-05-19-rfc019, codex-2026-05-19-id
ai: claude | codex | gemini | <other> | human
started: <iso-8601-utc> # ex.: 2026-05-19T22:30:00Z
reason: |
<uma frase: que teste/operação está rodando + ticket referenciado>
heartbeat: <iso-8601-utc> # opcional; updateado por sessões longas
ttl_minutes: 60 # opcional; default 120 se omitido
---O conteúdo do arquivo é livre — vale repetir comandos exatos que estão rodando, output snapshots, etc., para outras sessões inspecionarem.
R4 — Fluxo de aquisição / liberação de lock de VM
Antes de rodar qualquer carga numa VM listada em §R2:
- *heck.*Ler
meta/context/notices/vm-active/lock-<vm-id>.md.- *ão existe*→ pode prosseguir (ir pra §R4.2)
- *xiste + não é minha sessão*→ outra sessão de IA/humano está
usando essa VM. Não invadir. Opções:
- (a) Aguardar (se urgente: pollar a cada 30s o arquivo até sumir)
- (b) Usar outra VM (provisionar uma nova se a alvo for genérica
o suficiente — ex.:
dev-linux-koda-1vsdev-linux-koda-2) - (c) Sinalizar pro usuário e parar — explicitamente reportar
qual sessão está ocupando, há quanto tempo, e que ticket
- *xiste + é minha sessão*→ seguir adiante (lock já meu)
- *xiste + sessão expirada*(mais que
ttl_minutessem updatedo
heartbeat, OUstartedhá mais de 24h e nenhumheartbeat) → considerar lock órfão; arquivar (mover prameta/context/notices/vm-active-archive/lock-<vm-id>-expired-<date>.md) com nota explicando, e prosseguir pra §R4.2
- *lace.*Escrever o arquivo de lock com o frontmatter de §R3.
Commitar (pode ser commit independente ou junto com o trabalho subsequente — escolher o que reduzir churn).
- *se.*Executar os testes/builds no VM.
- Para execuções >10min, atualizar
heartbeatem intervalos<
ttl_minutes/3para evitar staleness.
- Para execuções >10min, atualizar
- *rchive.*Ao concluir, mover o arquivo pra
meta/context/notices/vm-active-archive/lock-<vm-id>-<YYYY-MM-DD>.md. Não deletar — histórico fica como audit trail.
*mplementação preferida:*comando
koder-lock vm <subcmd>análogo aokoder-lockde componentes. Tracking emdev/koder-tools— até shipar, o fluxo manual acima vale.
R5 — Testes gráficos em modo headless *ÃO são exceção*
Mesmo em modo headless, testes gráficos consomem recursos de GPU virtual / framebuffer / Wayland compositor / ADB / emulador. Roda em VM:
- Flutter integration tests headless
- Chrome headless + PlaywrightSeleniumPuppeteer
- Xvfb / Wayland compositor sintético (Weston/labwc) em test runners
- Android emulator (qualquer API level)
- iOS simulator (futuro — quando shippar, mesmo princípio em
s.khost1via UTM ou similar)
Para emuladores Android, a regra de §R3 (uma sessão por VM) é *specialmente*importante: ADB device 0 é serializado pelo emulador. Duas sessões emitindo adb shell ao mesmo emulador intercalam comandos.
R6 — Onde ficam os dados de teste
Os repositórios da Stack vivem em ~/dev/koder/ no laptop. Para os VMs lerem o código, há 3 estratégias por ordem de preferência:
- *ind mount via incus*(LXC compartilha o diretório
real-time):
incus config device add <vm> monorepo disk source=/home/koder/dev/koder path=/home/koder/dev/koder readonly=true - *FS export*do laptop pro
s.khost1(read-only) - *it fetch*dentro do VM (clone
https://flow.koder.dev/Koder/koder.git) — mais lento, mas elimina dependência de o laptop estar ligado
Para artefatos de build / outputs / logs, escrever em path local da VM (ex.: /tmp/, /srv/test-out/). Não escrever de volta no monorepo exceto via PR/commit consciente.
R7 — Conflito entre lock percomponente e lock perVM
Os dois locks são ortogonais. Uma sessão típica de IA que edita engines/lang/koda e roda testes em dev-linux-koda mantém:
meta/context/notices/active/lock-engines-lang-koda.md(componente)meta/context/notices/vm-active/lock-s.khost1.dev-linux-koda.md(VM)
Liberar na ordem *eversa*da aquisição (LIFO): VM primeiro, depois componente. Garante que se outra sessão pegou o componente entre liberação e fim, ela ainda vê os artefatos da build prévia consistentes.
R8 — Onde criar a primeira VM (bootstrap)
Para Koda especificamente — o primeiro consumidor desta policy — provisionar s.khost1.dev-linux-koda:
# Em s.khost1, root:
incus launch images:debian/13 dev-linux-koda
incus config device add dev-linux-koda monorepo disk \
source=/home/koder/dev/koder \
path=/home/koder/dev/koder \
readonly=false
incus exec dev-linux-koda -- apt update
incus exec dev-linux-koda -- apt install -y nasm gcc binutils make git
# Run from inside:
incus exec dev-linux-koda -- bash -c \
'cd /home/koder/dev/koder/engines/lang/koda && make build-safe'Detalhes finais (NFS vs bind, network, snapshots para reset rápido) ficam no runbook
meta/context/runbooks/test-vm-provisioning.mdque a primeira sessão que provisionar deve escrever.
Tests (validação da policy)
| ID | Verificação |
|---|---|
| T1 | Audit walk encontra commits recentes que rodaram make build / make kasm no laptop sem lock de VM ativo. *ASS*= zero ocorrências; *AIL*= listar PRs/commits violadores |
| T2 | Audit walk encontra arquivos em meta/context/notices/vm-active/ com started há > ttl_minutes e sem heartbeat — locks órfãos |
| T3 | Dois locks distintos em vm-active/ pra mesma VM — *ASS*= nunca |
| T4 | infrastructure/servers.md §Containers ativos lista as VMs declaradas em §R2 que já estão provisionadas — sincronia entre policy + registro |
| T5 | meta/context/runbooks/test-vm-provisioning.md existe e cobre os 4 targets (linux x86, linux arm, windows, android) |
Trigger phrase mapping
Padrões que indicam violação iminente da policy:
make build/make build-safe/make kasmrodando em~/dev/koder/engines/lang/kodano laptop → REROUTE pra VMflutter test --device-id chromeno laptop → REROUTE pra VMemulator -avd ...no laptop → REROUTE pra VMqemu-system-...no laptop pra Windows VM local → MIGRAR pras.khost1.dev-win10lb build(ISO Koder Linux) no laptop → REROUTE pra VM ous.khost1direto
Phase-current note
*ase atual — aceleração:*o catálogo de VMs (§R2) está em construção. A regra obriga que *ntes de rodar o teste* a IA provisione a VM se ainda não existir, escreva o runbook em
meta/context/runbooks/test-vm-provisioning.md, e só então rode. O custo de provisionar a primeira VM é amortizado por todas as sessões subsequentes.
Histórico
- *026
0519*— policy ratificada após sequência de incidentes(#770#773#774) onde builds de Koda travaram o laptop com OOM-killer + swap saturado + segfaults durante GC mark walker, custando 30+ minutos por incidente. Owner direcionou: "todos os testes da koda devem ser realizados em uma vm de testes que deve ser criada no s.khost1". Esta policy generaliza pra Stack-wide.