Test Host Isolation — heavy / risky / graphical tests run on dedicated VMs, not the developer laptop

mandatory

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:

  1. *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.

  1. *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ósediçã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 (sistemaimagem `systemimages;android-NN;googleapis;x8664) 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 via koder-test-vm CLI é tracking futuro em dev/koder-tools.

*M ≠ servidor de produção.*Containers de *rodução*(s.khost1.id, s.khost1.flow, s.khost1.hub, etc., listados em infrastructure/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:

  1. *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-1 vs dev-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_minutes sem update

      do heartbeat, OU started há mais de 24h e nenhum heartbeat) → considerar lock órfão; arquivar (mover pra meta/context/notices/vm-active-archive/lock-<vm-id>-expired-<date>.md) com nota explicando, e prosseguir pra §R4.2

  1. *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).

  1. *se.*Executar os testes/builds no VM.
    • Para execuções >10min, atualizar heartbeat em intervalos

      <ttl_minutes/3 para evitar staleness.

  1. *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 ao koder-lock de componentes. Tracking em dev/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.khost1

    via 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:

  1. *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

  2. *FS export*do laptop pro s.khost1 (read-only)
  3. *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.md que 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 kasm rodando em ~/dev/koder/engines/lang/koda no laptop → REROUTE pra VM
  • flutter test --device-id chrome no laptop → REROUTE pra VM
  • emulator -avd ... no laptop → REROUTE pra VM
  • qemu-system-... no laptop pra Windows VM local → MIGRAR pra s.khost1.dev-win10
  • lb build (ISO Koder Linux) no laptop → REROUTE pra VM ou s.khost1 direto

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

  • *0260519*— 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.

Source: ../home/koder/dev/koder/meta/docs/stack/policies/test-host-isolation.kmd