🏗️ C4 · Stack · corte 27 ago 2026 · v2.2

C4 · Stack — especificação de inception

Especificação de inception

9,092palavras
14seções

Camada C4 do estudo WebTestPilot · v2.2 · corte 27 ago 2026 Público: quem vai implementar. Critério de qualidade: um engenheiro consegue começar amanhã — e o documento diz o que ele vai descobrir na primeira semana. Fecha decisões (ADR-01 a ADR-16) e separa o que está decidido do que só se resolve medindo (§ 12). Alvo: AWS, com escalabilidade. Aplicação de referência: Vendure + nextjs-starter-vendure; troca-se por app real pelo checklist do § 9.


0 · Como ler


1 · Quatro princípios

  1. O veredito é código; o agente descobre e propõe; o LLM tria depois. Não é diferencial deste estudo — é consenso operacional em 2026 (Octomind 2025; WebTestPilot FSE 2026). O que distingue a família D é a origem do predicado: relação entre execuções, não spec inferida. É isso que o piloto mede (E9c — com MR validada em E2E web, que fração das violações é bug?). A regra "o veredito é código" está no C2 § 7.3. [protocolo · A20]
  2. A relação é fixada antes de executar e falsificada por mutante. Operador, direção, observáveis, guardas e exclusões ficam em arquivo (anchoredOn) antes da primeira execução; mudam só com revisionNotes; e cada MR tem ao menos um mutante que a viola (falsifiedBy), executado por ferramenta de mutação onde há código. MRs são propriedades necessárias, não suficientes; o oráculo que não pode falhar é o que edita a relação depois do diff. (C2 § 4 regra 2, § 6.5.)
  3. O par fonte/seguidor nunca se divide. Dois browser contexts no mesmo processo, um único test(), um único trace, o mesmo clock, o mesmo banco. Orquestração faz fan-out de MRs; nunca parte uma MR ao meio. [protocolo · A27]
  4. Falha barulhenta é melhor que replanejamento silencioso. Nada na stack "conserta" um passo em tempo de execução: nem healer, nem cache de agente, nem Runtime Recovery. Falha de infraestrutura é ruído no passo 0 — com limiar calibrado, não chutado — e nunca entra como violação.

2 · A stack, camada a camada — decisões fechadas

Camada Decisão ADR O que ficou de fora
1 · Driver Playwright 1.62.1, biblioteca em processo; imagem própria só-Chromium derivada de mcr.microsoft.com/playwright:v1.62.1-noble, no ECR privado com índice SOCI v2 ADR-10 Cypress; Selenium; latest
1 · Infra de navegador Dentro da conta: ECS Fargate arm64 para K1/K2 (com a app sob teste dentro da task), Lambda para K0 ADR-01, 06 Browserbase/Steel/Anchor (sessão sai da VPC); browserless (SSPL); grids por slot; Device Farm
1 · Rede Subnets privadas + VPC endpoints (ecr.api, ecr.dkr, logs, secretsmanager) + S3 gateway; sem NAT; SG sem inbound ADR-15 NAT (US$ 20/rodada de egress de imagem); IP público no runner
2 · Determinismo page.clock (install antes de qualquer chamada de clock) · routeFromHAR só para assets/terceiros, nunca para a API da app · serviceWorkers: 'block' declarado · storageState por sujeito, credentials para passkey · clipboard e viewport fixos ADR-11 Gravar a API no HAR
2 · Estado K1 Postgres sidecar na mesma task, PGDATA explícito fora do VOLUME, listen_addresses=localhost, ownership assada no build ADR-03 Aurora clone por par; Neon; Testcontainers; bind mount de task; trust em all
2 · Estado K2 e shrinking Um database por par via CREATE DATABASE … TEMPLATE num cluster Aurora Serverless v2 PG 17.x (≥ 17.4), min ACU 4 na rodada e 0 fora, criado por init container antes da app subir ADR-04 PG 18 no Serverless v2 (não documentado); sidecar para K2; auto-pause na rodada; IAM DB auth
3 · Motor fast-check 4.9 para sequências e fixtures (entityGraph), scheduledModelRun para MR-B2, shrinking offline; a relação é código próprio. Bombadil avaliado como motor das MRs K0 de sessão única (P ∩ D) ADR-12 Chamar fast-check de "motor de relação"; shrinking no CI; gemtest
4 · Geração Molde travado → LLM instancia com saída Zod → baselinefalsificação por mutante → promoção. A contribuição é a automação do filtro, não o filtro ADR-13 (geração e agente) Família F; R(·) escolhida após execução
5 · Projeção Locator tipado como padrão; ARIA snapshot com filtro; rede só como cross-check; varredura implícita em toda execução; herda a literatura de abstração de estado de GUI ADR-14 Screenshot como R(·); extract; resposta de API como projeção primária
6 · Execução Playwright runner; par = um test() com test.step() e anotação mr:<id>; retryStrategy: 'isolated'; trace: 'retain-on-failure'; contrato de exit code ADR-08 Fonte e seguidor como testes separados; trace: 'on'
6 · Orquestração Step Functions Standard + Distributed Map (filhos Standard), ponteiro S3, Retry em States.TaskFailed/AmazonECS.Unknown, TimeoutSeconds, semáforo de rodadas ADR-05 Express; Map inline; dividir o par; rodadas concorrentes sem semáforo
6 · Monitoramento CloudWatch Synthetics para K0 em produção; K0 em Lambda no pré-deploy na versão do runtime, adaptador runPair com dois entrypoints, job de acompanhamento de runtime com dono ADR-06 Checkly no alvo AWS
6 · Observabilidade Histórico determinístico por MR antes de LLM; controle de ruído fonte×fonte na mesma task; tags de custo por run-id/owner/mr-id ADR-09 Quarentena automática; Container Insights por padrão
Agente Test Agents planner + generator (dev-time, transporte MCP), healer removido; Stagehand só para descoberta em runtime ADR-13 (geração e agente) Healer; extract em R(·)
Segurança e IAM Segredos via Secrets Manager no container definition; 4 roles mínimas; bucket de traces criptografado com retenção e acesso restrito (trace + storageState = sessão reproduzível) ADR-16 trust em rede; traces públicos na conta

3 · O laço de julgamento

0. SANIDADE        exit code do browser · crash de renderer · sidecar HEALTHY · app HEALTHY
                   · delta de latência fonte×seguidor acima do LIMIAR CALIBRADO (§ 12)
                                                                          → RUÍDO, sai
1. SELEÇÃO         classe de custo (K0/K1/K2) × módulo tocado (owner) × orçamento
                   (Reporter.preprocess)                                   → manifesto S3
2a. DETERMINISMO   in-process, microssegundos: clock.install(T0) · routeFromHAR(assets)
                   · serviceWorkers:'block' · storageState(sujeito) · viewport
2b. ESTADO         a única parte cara — antes de a app subir:
                   (K1) sidecar pg_isready · (K2) init container: CREATE DATABASE … TEMPLATE
2c. APP            vendure-server + vendure-worker + storefront sobem apontando para o DB
3. EXECUÇÃO        context A = FONTE, context B = SEGUIDOR, mesmo processo, um test()
3b. ASSENTAMENTO   após mutação (server action, POST): esperar a resposta da ação e
                   projetar até duas leituras consecutivas coincidirem (settle: projeta,
                   1 s, reload, projeta; OracleSetupError após 6) — a app tem dois
                   estados por ~1–2 s (cache de RSC/`updateTag`); a MR lê R(·) uma vez,
                   a asserção convencional esconde isso com auto-wait. Medido na app de
                   referência (§ 10.3): 4/4 falsos positivos em MR-B1 sem este passo,
                   0/5 com ele.
4. PROJEÇÃO        R(fonte), R(seguidor) por locator tipado / ARIA filtrado;
                   OracleSetupError se contador≠lista, paginação>guarda, observável vazio,
                   ou projeção não assentou
5. RELAÇÃO         função pura sobre as duas projeções · veredito determinístico
6. REGISTRO        mr.evaluated += 1 (EMF) · verdict.json → S3 (sempre) · se violar:
                   seed + replayPath + trace.zip → S3 · exit 0 ("o harness rodou")
                   NÃO encolhe aqui
7. TRIAGEM         (a′) rerun automático com settle e reload — barato, e na primeira
                       semana não há histórico
                   (a) histórico determinístico da MR (flake? primeira vez?)
                   (b) controle de ruído: divergência fonte×fonte da mesma build/task
                       (não detecta estado não assentado — fonte vazia é sempre igual)
                   (c) só então LLM: {bug, MR mal especificada, ruído}, com evidência
—. SHRINKING       offline, sob demanda, com orçamento, relendo seed+replayPath;
                   exige estado reconstruível → database por par (ADR-04); leitura:
                   Jiang, Tian & Chen (2026), delta debugging através de MT

Contrato de exit code (ADR-08): 0 = o harness rodou e o veredito está em S3 (inclusive violação); ≠ 0 = ruído de infra → Retry no filho. Sem isso o .sync do Step Functions só sabe dizer States.TaskFailed.


4 · Formato de MR

Uma MR é um arquivo versionado, revisável em PR, sem LLM no laço depois de promovida. Nove campos obrigatórios: id, owner, costClass, anchoredOn, projection, source, follower(s), relation, falsifiedBy. O resto é opcional — o revisor lê o que importa — e o linter só exige revisionNotes quando projection/relation mudam após execução; relationSampled só em K2. excludes e locators pertencem à projeção compartilhada, não à MR — os motivos são sempre os mesmos ("CDN", "moeda", "ordem"), e o que quebra com o redesign é a projeção.

// mr/A4-particao-categorias.mr.ts
export default defineMR({
  id: 'A4-particao-categorias',
  molde: { mrip: 'partition-attribute', mrop: 'complete', tree: 'Sets/CompletePartitions' },
  owner: 'catalogo',                         // quem responde pela quebra; seleção por módulo (ADR-09)
  costClass: 'K2',                           // K0 | K1 | K2
  target: {                                  // R(·) envelhece com a app tanto quanto N4 com a data
    app: 'vendure', core: '3.7.2', storefront: 'vendurehq/nextjs-starter-vendure@7d06ae0',
    seed: 'products.csv@<sha>',
  },
  anchoredOn: [                              // requisito de origem — obrigatório (princípio 2)
    'packages/core/mock-data/data-sources/products.csv',
    'initial-data.ts:89-165',
  ],                                         // ou 'observed', com peso menor (SaaS de terceiro, K0 em produção)
  preconditions: [
    { kind: 'seed-invariant', check: 'cada produto tem exatamente 1 faceta category de topo' },
    { kind: 'no-promotion-active' },
  ],
  projection: R_grid,                        // referência nomeada + versão; enumera até a última página;
                                             // excludes e locators (com risco) vivem em R_grid, não aqui
  source:    { flow: 'search', params: {} },
  followers: { fanOut: 'top-level-collections', sample: { k: 2, rotate: 'nightly' } },   // k+1 rotativo
  relation:  (f, segs) => union(segs).equals(f) && sum(segs, s => s.size) === f.size,
  relationSampled: (f, segs) => union(segs).subsetOf(f) && sum(segs, s => s.size) <= f.size,
  falsifiedBy: [                             // análise de mutação — EXECUTADA na promoção; listar não basta
    { mutant: 'descartar a última página na enumeração', tool: 'runner-flag', violated: true },
    { mutant: 'produto sem faceta de topo no seed', tool: 'seed-patch', violated: true },
    // ATENÇÃO: "{or: id} item a item" é mutante EQUIVALENTE no Vendure (§ 10.3);
    // o mutante real é facetValueFilters: [{ or: facetValueIds }]
  ],
  setupErrors: ['contador ≠ lista', 'paginação > 50 páginas'],   // o que NÃO é violação
  exercised: { sometimes: true },            // registro de exercício: MR morta não passa em silêncio
  revisionNotes: [],                         // obrigatório preencher se projection/relation/excludes mudar após execução
});

Regras do formato:


5 · Projeção R(·) — as regras

A decisão de engenharia mais cara do projeto (estimativa: ~35–40% do esforço do harness; medido por MR no § 10.3). Extraído de doze instâncias e da literatura de abstração de estado de GUI (Genie, Odin, Yandrapally 2020, Liu 2026), que fez isso para inferência de modelo.

5.1 O que entra. Chave de identidade estável por item (slug, SKU, código), lida de atributo; cardinalidade explícita, separada do conjunto; grandezas de negócio na forma canônica (número, não string; subtotal antes de desconto quando a MR é de ordem); o estado dos controles que definem o resultado (facetas, sort, variante).

5.2 O que sai. Preço com moeda/formatação, imagens e srcSet, contadores "(n)" de faceta, ordem (salvo quando a MR é sobre ordem), timestamps, ids de sessão e de pedido em AddingItems, blocos de recomendação, texto de estoque antes de fixar variante, breadcrumb quando reflete caminho. Tudo que a app tem permissão de requisito para variar entre fonte e seguidor sai; na dúvida, sai até a doc dizer o contrário — e a exclusão leva motivo.

5.3 Enumeração completa. Projeção de conjunto enumera até a última página com condição de parada por locator; guarda de laço e contador ≠ itens são OracleSetupError, nunca violação. Custo real de K2 é em page loads (MR-A4 no Vendure: 4 navegações, 11 loads). Virtualização sem critério de parada observável torna a MR ❌.

5.4 Locator tipado × ARIA snapshot × rede.

Via Quando Risco
getByRole / getByLabel / getByTestId conjunto enumerável com chave em atributo; grandezas; estado de controle. Padrão. renomeação de rótulo — referenciar a chave i18n
ariaSnapshot() + poda explícita (roles permitidos, atributos removidos, { boxes } se geometria importa) estrutura pequena e regular sem filtro vira "DOM inteiro" com outro nome
Resposta de API interceptada só cross-check mesmo caminho de renderização dos dois lados: bug no resolver move fonte e seguidor juntos

5.5 O que "fixado antes" significa. O que fica fixado fora do sistema é a relação: operador e direção vêm do molde, não do modelo; a lista de observáveis, as guardas e as exclusões ficam em anchoredOn antes da primeira execução (o argumento — MR necessária, não suficiente — está no C2 § 6.5). Regra operacional: três guardas baratas — observável obrigatório não vazio (rejeitar []/"" como pass); not_equal só com valor esperado declarado; observáveis invariantes separados dos transformados, com normalização (caixa, 7.1M) antes de comparar — e a guarda cara, que é a que vale: um mutante por MR.

5.6 Projeções compartilhadas (R_grid, R_cart, R_pdp, R_orders) são módulos com versão, lista de dependentes, excludes com motivo e locators com risco. É onde o custo da MR está e onde ele se amortiza: medido na app de referência (§ 10.3), ~40 min para ler o starter e escrever R_grid, depois 2 min por MR que a reutiliza; a asserção convencional custa 20–30 min cada e não reutiliza nada.

5.7 Dois estados por dois segundos. Depois de uma server action o storefront serve o estado antigo por ~1–2 s; R(·) lê o estado assentado — é o passo 3b do § 3.


6 · Geração do critério (camada 4)

O princípio: o modelo reconhece, não inventa; nada entra no catálogo sem passar por execução e por mutante. O que se copia e o que não se copia dos artefatos existentes está marcado. A linha de base sem LLM é o MRG (Segura et al., 2022): dada uma spec de parâmetros de consulta, instanciar MRPs é barato — o valor do LLM está em descobrir a spec da tela, não em instanciar.

# Passo Decisão De onde vem
1 Biblioteca de moldes — os 24 do C2 § 4 como dado versionado: MRIP, MROP, direção do operador, pré-condições, exemplo Molde é dado, não código metamorph: família travada no prompt (copiar)
2 Inventário da telaariaSnapshot({ mode: 'ai', boxes: true }) filtrado por roles promovíveis, com ref e caixa no viewport, rotulado E1…En; screenshot anotado só como apoio O modelo recebe lista de itens, não árvore metamorph: Set-of-Marks, element_id só do inventário (copiar)
3 Instanciação restrita — para cada molde aplicável, pergunta de reconhecimento: quais controles são filtros, qual região é o conjunto, qual locator é o total. One-shot por família, saída Zod O modelo preenche lacunas; não escreve a relação metamorph (copiar); MRG como referência
4 Especificação de R(·) antes de executar — observáveis com binding tipado a partir do inventário do estado inicial e do molde, não do estado final da fonte; direção do operador do molde Divergência deliberada do metamorph não copiar
5 Baseline — a candidata roda contra a versão sabidamente correta com o seed padrão. Violou → descartada. Passou com observável vazio → descartada Protocolo-padrão de avaliação por mutação (Zahid o faz à mão) ausente no metamorph
6 FalsificaçãofalsifiedBy executado com ferramenta de mutação onde há código (Stryker para o starter TS; patch de seed; flag no core); ao menos um mutante tem de violar Resposta à MR que não pode falhar Zahid (falhas manuais em E2E; Stryker só na API dele); Xiong 2026 (refinar por feedback)
7 Registro de exercício — contador por MR avaliada (EMF) always/sometimes do Bombadil/Antithesis parcial no metamorph
8 Promoção.mr.ts no formato do § 4, PR revisado. Sem LLM daqui em diante hitl por padrão; auto só com 5 e 6 verdes metamorph: mr_version + hash de bundle (copiar)

O que o LLM não faria. Descobrir que Computers ⊂ Electronics torna a instância vácua; que {or: id} item a item é equivalente; que o carrinho tem 2 s de cache. Na app de referência (§ 10.3) essas três coisas custaram 80% do tempo de autoria e só saíram de execução + código. A Fase 5 fica menor: o LLM instancia MRs sobre projeções compartilhadas já escritas à mão — economiza os 2 minutos do arquivo, não os 40 da ancoragem. O time escreve R(·).

Juiz de progresso de exploração. O único uso de modelo sobre tela que a stack aceita é decidir se continuar explorando (metamorph explore-verify) — nunca o veredito.

Custo variável de LLM. Só aqui. Ordem de grandeza (N2): ≈ 34 chamadas, US$ 0,05 e 10 min de parede por MR compilada; 63% de compilação; MetaFOE indica que metade das instâncias pode cair mesmo com verificação por execução — orçar o dobro. Transporte da descoberta: MCP com os Test Agents oficiais.


7 · Arquitetura AWS

7.1 As dezesseis ADRs

A tabela do § 2 é o índice. Cada ADR abaixo tem três parágrafos: Decisão, Rejeitadas, Evidência. Onde a evidência é doc oficial da AWS, a citação literal e a URL estão nos registros apontados no § 0; onde é medição, o número está no § 10.3.

ADR-01 · A task do par — com a app dentro

Decisão. O sidecar Postgres escuta em localhost; logo a app que fala com ele roda na mesma task. Vendure é server + worker; o storefront Next.js faz SSR. Task de K1/K2 = runner + Chromium + Postgres + vendure-server + vendure-worker + storefront (5–6 dos 10 containers permitidos), dependsOn postgres → (init K2) → server → storefront → runner, com health checks. Forma inicial 4 vCPU / 8 GB arm64 (≈ US$ 0,0092/par); 2 vCPU/4 GB é irrealista com SSR + API + 2 contexts. Imagem do runner só-Chromium no ECR com SOCI v2 (o digest muda após o índice — a task definition aponta para o digest pós-soci convert).

Rejeitadas. Device Farm; ECS/EC2 (só vence acima de ~65% de ocupação); Managed Instances (sem ipcMode nem tmpfs); Lambda para K1/K2; app fora da task (reintroduz control plane e latência assimétrica).

Evidência. Doc do ECS (containers por task, localhost entre containers em awsvpc, combinações vCPU/memória do Fargate, SOCI v2 e mudança de digest); doc de deploy do Vendure (server e worker na mesma imagem). A forma da task é estimativa até o experimento do § 12.

ADR-02 · Memória compartilhada — Chromium e Postgres

Decisão. Fargate não dá ipcMode/sharedMemorySize/privileged. --disable-dev-shm-usage já é default do Playwright. linuxParameters.tmpfs em /dev/shm dos dois containers — suportado em Fargate desde 06/01/2026 (anúncio + API ref; o guia está atrasado); o Postgres com workers paralelos falha sem isso ("could not resize shared memory segment"); fallback dynamic_shared_memory_type=mmap. tmpfs conta contra a memória da task. Crash de renderer = ruído (passo 0).

Rejeitadas. --ipc=host via EC2; ignorar o modo de falha.

Evidência. Anúncio da AWS de 06/01/2026 e referência da API (LinuxParameters); guia do desenvolvedor ainda com a restrição em 27/08/2026; chromiumSwitches.ts do Playwright; página oficial do Postgres no Docker Hub (shm_size). Validação em Fargate e via CDK: § 12.

ADR-03 · Estado K1 — sidecar

Decisão. postgres:18 com PGDATA explícito fora de /var/lib/postgresql (o VOLUME de 18 mudou e o PGDATA padrão fica dentro dele); ownership e 0700 assados no build (o entrypoint faz chown recursivo a cada start — segundos em 500 MB); initdb pulado por PG_VERSION; listen_addresses=localhost e SG da task sem inbound — o entrypoint oficial escreve host all all all trust, que é qualquer host, não localhost; shared_buffers 128 MB; buildar a imagem arm64 em Graviton, não por QEMU (PGDATA é gerado na arquitetura alvo).

Rejeitadas. Aurora clone por par (15 CoW / 40 clusters / dois passos / acorda o writer); Neon; Testcontainers (sem daemon); bind mount de task (root:root); trust em all.

Evidência. docker-entrypoint.sh oficial (host all all all trust, chown recursivo, pulo do initdb por PG_VERSION); Docker Hub (VOLUME e PGDATA da imagem 18); doc do Aurora (limites de clone e writer acordado). Custo do PGDATA no pull e do chown no start: § 12.

ADR-04 · Estado K2 e shrinking — database por par

Decisão. CREATE DATABASE … TEMPLATE (WAL_LOG) num cluster Aurora Serverless v2 PostgreSQL 17.x (≥ 17.4) — 18.x existe no Aurora mas não consta na lista do Serverless v2; confirmar com aws rds describe-orderable-db-instance-options --engine aurora-postgresql --db-instance-class db.serverless. Min ACU 4 durante a rodada, 0 fora (mudar o mínimo não exige reboot; auto-pause fora evita ≈ US$ 44/mês ociosos); máx fixado uma vez (mudar exige reboot para max_connections). Com min 0/0,5 o max_connections é capado em 2.000. Criado por init container com dependsOn: SUCCESS antes de o server subir; DROP DATABASE WITH (FORCE) ao fim; Lambda reaper para pair_% com mais de 1 h. Credenciais via Secrets Manager. Standard × I/O-Optimized decidido por VolumeWriteIOPs medido (WAL_LOG de 500 MB ≈ 128k blocos ≈ US$ 0,026/par de I/O no Standard — 30× a ACU).

Rejeitadas. Sidecar para K2; auto-pause na rodada; PG 18 sem verificação; IAM DB auth (300–1000 MiB extra, inviável em ACU baixo); RDS Proxy (impede auto-pause).

Evidência. Página de versões suportadas do Serverless v2 (17.4+, sem coluna 18); doc de capacidade (max_connections capado em 2.000 com min 0/0,5; reboot ao mudar o máx); doc do PostgreSQL 18 para CREATE DATABASE (WAL_LOG bloco a bloco; template bloqueado por conexão); preço de I/O do Aurora. A conta de I/O é estimativa — VolumeWriteIOPs sob 20 criações concorrentes é experimento do § 12.

ADR-05 · Orquestração

Decisão. Step Functions Standard + Distributed Map, filhos Standard, entrada por ponteiro S3 (256 KiB por estado), ResultWriter para S3. O par nunca cruza um estado. Contrato: ecs:runTask.sync falha sempre como States.TaskFailed; exit ≠ 0 = ruído → Retry (1–2×, backoff, jitter); RunTask sob cota vem como AmazonECS.UnknownRetry; ToleratedFailurePercentage explícito (padrão é zero); TimeoutSeconds ≈ 3× o par; idempotência por chave S3 (runId, mrId, attempt). Semáforo de rodadas (fila SQS ou MaxConcurrencyPath lido de contador) — 20 rodadas/dia se sobrepõem e duas Map Runs a cota/2 estouram a cota. MaxConcurrency ≤ 100 enquanto o burst launch rate for 100. Custo: US$ 0,000025/transição ≈ US$ 0,0003/par.

Rejeitadas. Express (5 min); Map inline (40); dividir o par; rodadas sem semáforo. Batch em Fargate fica como alternativa condicional: enfileira sob cota e faz retry por exit code de graça, mas não dá tmpfs — se ADR-02 for necessário, Batch cai; Batch sobre EC2/Spot para o noturno K2.

Evidência. Cotas do Step Functions (256 KiB; 100 execuções/s no Distributed Map); doc da integração com ECS (FailuresAmazonECS.Unknown; cancelamento best-effort do .sync); ToleratedFailurePercentage padrão zero; burst launch rate 100 do Fargate. Que o erro do .sync é sempre States.TaskFailed é N2 (README de terceiros) — o Cause sob cota estourada é experimento do § 12.

ADR-06 · K0 e monitoramento

Decisão. K0 em Lambda no pré-deploy (imagem até 10 GB, 15 min, EphemeralStorage 2 GB, HOME/XDG_* em /tmp) e em CloudWatch Synthetics em produção (3.008 MB, 840 s, EphemeralStorage 1–10 GB). Adaptador runPair(browser, mr) puro com dois entrypoints — o runtime do Synthetics envolve o Playwright em @aws/synthetics-playwright, então "mesma versão" ≠ "mesmo código". K0 é workspace separado fixado na versão do runtime (syn-nodejs-playwright-8.0 = 1.61.1 hoje); job semanal com donoDescribeRuntimeVersions e abre PR de bump — a cadência é ~um runtime por mês e sem dono o pin apodrece.

Rejeitadas. Checkly (~10× o Synthetics — números no C3 § 3.6; dados fora da conta); Fargate para K0 (frio de 30–60 s para 60 s de trabalho).

Evidência. Doc do runtime Synthetics (syn-nodejs-playwright-8.0 = Playwright 1.61.1; cadência 5.0 → 8.0 em ~8 meses); CanaryRunConfigInput (3.008 MB, 840 s, EphemeralStorage); limites do Lambda (10 GB, 900 s, /tmp 512 MB–10 GB). Preço arm64 do Lambda e US$ 0,0012/run do Synthetics são N2 (Price List API, não a página de preço). Se DescribeRuntimeVersions expõe a versão do Playwright: § 12.

ADR-07 · Capacidade e cotas

Decisão. Sem Fargate Spot para pares (aviso de 2 min, stopTimeout ≤ 120); Spot arm64 só para shrinking offline e noturno em Batch/EC2. Todos os tickets no dia 1 em conta nova: L-3032A538 (Fargate On-Demand vCPU, inicial 6 = 3 tasks de 2 vCPU; ≥ 400 para 100 tasks de 4 vCPU), Step Functions ("reduced state transition quotas"), Lambda ("reduced concurrency and memory quotas"), ENIs/IPs (100 tasks = 100 ENIs → subnets /24 no mínimo), RDS 40 clusters/instâncias.

Rejeitadas. Spot para tudo; esperar a cota subir sozinha.

Evidência. Cotas do ECS/Fargate (L-3032A538 = 6 em conta nova; burst 100/s, sustained 20/s); doc de capacity providers (aviso de 2 min como SIGTERM, stopTimeout máx 120 s); notas de cota reduzida em conta nova para Step Functions e Lambda; cotas do RDS (40 clusters, 40 instâncias). Spot arm64 no Fargate existe desde 09/2024.

ADR-08 · Artefatos e contrato do runner

Decisão. S3, trace: 'retain-on-failure', Expiration 30 dias, sem transição; layout runs/<runId>/<mrId>/<attempt>/{verdict.json,projections/,traces/}; verdict.json sempre, trace e replayPath só em violação; exit 0 = rodou; bucket criptografado, acesso restrito — trace + storageState é sessão reproduzível. Trace típico 1–10 MB.

Rejeitadas. trace: 'on'; Glacier IR; traces legíveis por qualquer role.

Evidência. O contrato de exit code decorre do comportamento do .sync (ADR-05); o tamanho de trace é ordem de grandeza — a medida nas MRs do Vendure é experimento do § 12.

ADR-09 · Seleção, ruído e triagem

Decisão. K0 sempre; K1 por módulo tocado (owner); K2 noturno com k+1 rotativo. Antes de qualquer LLM: retryStrategy: 'isolated', histórico da MR (par como unidade), e controle de ruído fonte×fonte na mesma task — +1 execução por MR por build; violação só quando fonte×seguidor excede fonte×fonte. O limiar do passo 0 sai dessa medição, não de chute. Tags de custo (run-id, cost-class, owner, mr-id) com propagateTags; Budgets por tag; EMF por rodada.

Rejeitadas. Rodar tudo em todo PR; quarentena automática; LLM como primeiro classificador; Container Insights por padrão.

Evidência. Na app de referência (§ 10.3): ruído fonte×fonte 0/7, e o que derrubou MR-B1 não era ruído — era estado não assentado, que o controle fonte×fonte não detecta (daí o passo 3b do § 3). propagateTags e enableECSManagedTags na referência do RunTask. O limiar do passo 0 é experimento do § 12.

ADR-10 · Versões

Decisão. Playwright fixado por tag de imagem e digest; não subir sem medir memória (regressão em 1.57, parcialmente resolvida em 1.58). Task definitions são imutáveis (rollback = revisão anterior); state machine com versions/aliases; template do Aurora nomeado tpl_v<sha>; imagem do sidecar e template mudam juntos quando products.csv muda.

Rejeitadas. latest.

Evidência. Releases do Playwright (1.62.1 é a última em 27/08/2026; regressão de memória em 1.57); doc do Step Functions (versions/aliases) e do ECS (revisões imutáveis de task definition).

ADR-11 · Determinismo

Decisão. page.clock.install(T0) antes de qualquer chamada de clock, nos dois contexts; routeFromHAR só para assets e terceiros, nunca para a API da app; serviceWorkers: 'block' declarado na MR (muda a app sob teste, e o service worker derrota o HAR em silêncio); storageState por sujeito (A, B, anônimo) e credentials para passkey; clipboard e viewport fixos. Tudo in-process, em microssegundos — é o passo 2a do § 3; a parte cara do determinismo é o estado (ADR-03, ADR-04).

Rejeitadas. Gravar a API no HAR (a MR passaria a comparar HAR com HAR, e a app sob teste sairia do laço); waitForTimeout como espera por estado (o assentamento é o passo 3b, com duas leituras iguais como critério observável); resolver determinismo por infraestrutura o que se resolve no processo.

Evidência. § 3 (passo 2a) e § 7.4 item 3; capacidades do Playwright 1.62 (clock, routeFromHAR, serviceWorkers, storageState) em C3 § 3.1; na app de referência (§ 10.3), o falso positivo não veio de clock nem de rede, mas de estado não assentado.

ADR-12 · Motor de relação

Decisão. fast-check 4.9 gera sequências de ações e fixtures (entityGraph) e, para MR-B2, scheduledModelRun; o shrinking roda offline, sob demanda, com orçamento, relendo seed + replayPath (§ 3); a relação é código próprio — função pura sobre duas projeções, com o contrato do § 8. Bombadil é avaliado como motor das MRs K0 de sessão única (P ∩ D; C2 § 7.1) — o experimento é da Fase 2 (§ 12).

Rejeitadas. Chamar fast-check de "motor de relação" (é gerador; a relação não vem dele — C3 § 3.3); shrinking no CI (exige estado reconstruível e orçamento próprio — ADR-04); gemtest (C3 § 3.3, § 13).

Evidência. § 3 (o registro não encolhe; shrinking offline) e § 8 (projeção → relação); C2 § 7.1 (Bombadil expressa toda MR de sessão única, mas não faz par em contexts separados, baseline nem falsificação).

ADR-13 · Geração e agente

Decisão. O pipeline do § 6: molde travado → LLM instancia com saída Zod → baseline → falsificação por mutante executado → promoção, hitl por padrão. O LLM instancia MRs sobre projeções compartilhadas escritas à mão; o único uso de modelo em runtime é o juiz de progresso de exploração. Agente: Test Agents oficiais planner + generator em dev-time, transporte MCP; healer removido; Stagehand só para descoberta em runtime.

Rejeitadas. Família F (LLM como oráculo); R(·) escolhida após execução (o oráculo que não pode falhar); healer, Runtime Recovery e cache de agente (princípio 4); extract em R(·).

Evidência. § 6 (passos 1–8; o que se copia e o que não se copia do metamorph); na app de referência (§ 10.3), o que custou 80% da autoria só saiu de execução + código; transporte MCP verificado no código dos Test Agents (C3 § 4); a regra "o veredito é código" (C2 § 7.3).

ADR-14 · Projeção

Decisão. Locator tipado (getByRole/getByLabel/getByTestId) como padrão; ARIA snapshot só com filtro explícito (roles permitidos, atributos removidos); rede só como cross-check; varredura implícita (console, 5xx) em toda execução; projeções compartilhadas versionadas, com excludes com motivo e locators com risco (§ 5.6); herda a literatura de abstração de estado de GUI (§ 5).

Rejeitadas. Screenshot como R(·) (compara pixels, não estado); extract de agente (julgamento por modelo dentro da projeção); resposta de API como projeção primária (mesmo caminho de renderização dos dois lados — bug no resolver move fonte e seguidor juntos — e muda o objeto testado).

Evidência. § 5.1–5.4 (regras extraídas de doze instâncias); R_grid e R_cart executadas contra demo e instância local (§ 9.2, § 10.3); C2 § 7.2 (evidência para locator tipado).

ADR-15 · Rede

Decisão. Subnets privadas + VPC endpoints ecr.api, ecr.dkr, logs, secretsmanager (US$ 0,01/h por endpoint por AZ + US$ 0,01/GB) + S3 gateway endpoint (obrigatório para as camadas do ECR, sem custo de dados) ≈ US$ 44–58/mês fixos; SG de egress restrito aos endpoints e à app; sem NAT. Se a app precisar de internet, terceiros já vão por HAR.

Rejeitadas. NAT Gateway (905 MiB × 475 tasks = 451 GB/rodada × US$ 0,045 = US$ 20/rodada ≈ US$ 8.900/mês, 4–9× o compute); IP público no runner (ECR grátis na região, mas sessão autenticada num ENI público).

Evidência. Preços de VPC, PrivateLink e ECR lidos em 27/08/2026; doc do ECR sobre o S3 gateway obrigatório. Bytes reais por rota, ±SOCI: § 12.

ADR-16 · Segurança, IAM, contas

Decisão. Quatro roles: state machine (ecs:RunTask na task definition específica, StopTask/DescribeTasks, events:PutRule, iam:PassRole, states:StartExecution própria, S3 do manifesto/ResultWriter), task execution (ECR, logs, secrets), task role (S3 artefatos, EMF, secrets), Lambda de triagem (S3 read, LLM). ECR scan on push e tag immutability; logs com retenção por ambiente. Contas: CI (Fargate + Aurora de teste + ECR) em tooling; K0 pré-deploy em staging; Synthetics em produção (artefatos ficam lá); replicação de ECR entre contas. IaC: CDK (ASL tipado, task definitions, soci no pipeline, Synthetics Runtime como construct).

Rejeitadas. trust alcançável na VPC; uma conta para tudo; IaC ad hoc.

Evidência. Templates de política na doc do Step Functions (integração ECS, Distributed Map — StopTask com Resource: "*" porque o TaskId só existe depois do submit); recomendação de tags imutáveis na doc do ECS. O risco "trace + storageState = sessão reproduzível" é raciocínio próprio (§ 11).

7.2 Fluxo de uma rodada

  EventBridge / webhook de PR
        │
        ▼
  Semáforo de rodadas (SQS) ── uma Map Run por vez, ou MaxConcurrency dinâmico
        │
        ▼
  Step Functions (Standard) — pai
        ├─ 1. Seleciona MRs: classe × owner × orçamento     → manifesto em S3
        ├─ 2. Aquecimento: min ACU → 4 no cluster Aurora (K2); confirma cota
        ├─ 3. Distributed Map (filhos Standard, MaxConcurrency ≤ min(100, cota/4),
        │      entrada = ponteiro S3, TimeoutSeconds ≈ 3×par, Retry em TaskFailed/ECS.Unknown,
        │      ToleratedFailurePercentage explícito)
        │       ▼
        │   ecs:runTask.sync → UMA task Fargate arm64 4 vCPU/8 GB por MR
        │       ├── init (K2): CREATE DATABASE pair_<id> TEMPLATE tpl_v<sha>  → SUCCESS
        │       ├── postgres:18 (K1): PGDATA semeado, tmpfs em /dev/shm, localhost  → HEALTHY
        │       ├── vendure-server · vendure-worker  (DB_HOST=localhost | cluster)  → HEALTHY
        │       ├── storefront Next.js                                        → HEALTHY
        │       └── runner Playwright (tmpfs em /dev/shm)
        │             ├─ clock.install(T0) · routeFromHAR(assets) · serviceWorkers:'block' · storageState
        │             ├─ context 1 = FONTE ─┐ mesmo processo, mesmo clock, mesmo banco,
        │             ├─ context 2 = SEGUIDOR ┘ um test(), test.step() por fase
        │             ├─ (por build) fonte×fonte na mesma task → piso de ruído
        │             ├─ R(fonte), R(seguidor) — OracleSetupError separado de violação
        │             ├─ passo 0: crash? delta > limiar calibrado? → RUÍDO, exit ≠ 0
        │             ├─ relação, determinística · EMF mr.evaluated += 1
        │             └─ verdict.json → S3; violação: + seed + replayPath + trace.zip; exit 0
        ├─ 4. ResultWriter → S3 · DROP DATABASE dos pares K2 (reaper cobre o resto)
        └─ 5. Violações: histórico → ruído fonte×fonte → Lambda com LLM {bug, MR, ruído}

  Fora do laço:  K0 em Lambda (pré-deploy) e Synthetics (produção), adaptador runPair,
                 versão do runtime · shrinking sob demanda relendo seed + replayPath

7.3 Custo — ordem de grandeza (us-east-1, 27/08/2026, aritmética mostrada)

Premissas: par = 60 s de frio + 150 s de trabalho = 210 s; task 4 vCPU / 8 GB arm64 (app dentro); ~12 transições; 2% de violação; trace 5 MB; rede por endpoints.

Item Por par Conta
Fargate arm64 4 vCPU / 8 GB, 210 s US$ 0,0092 (0,0000089944×4 + 0,0000009889×8) × 210
Fargate arm64 2 vCPU / 4 GB (só o runner — referência) US$ 0,0046 (0,0000089944×2 + 0,0000009889×4) × 210
Step Functions, ~12 transições US$ 0,0003 12 × 0,000025
S3 com retain-on-failure ≈ US$ 0,00002 2 PUT + 5 MB × 2% × 30 d
Rede (endpoints, rateado) ≈ US$ 0,0002 US$ 50/mês ÷ (475 × 440 rodadas)
Par K1, total ≈ US$ 0,0097
Par K2: + database no Aurora (4 ACU compartilhados por 100 pares/10 min) + ≈ US$ 0,0008 de ACU + até US$ 0,026 de I/O no Standard (500 MB WAL_LOG) — medir; I/O-Optimized zera 0,48/6/100
Par K0 em Lambda arm64, 3 GB, 60 s US$ 0,0024 60 × 2,94 × 0,0000133334 (Price List API)
Canário K0 no Synthetics, 60 s US$ 0,0041 0,0012 (Price List API) + Lambda x86
Escala Custo Wall clock
Rodada de 475 execuções, Fargate arm64 4 vCPU ≈ US$ 4,6 com cota de 6 vCPU (1 task de 4): 475 × 210 s = 27,7 h · com 400 vCPU (100 tasks): 475 × 210 / 100 ≈ 17 min
20 rodadas/dia × 22 dias ≈ US$ 2.000/mês de compute + US$ 50 de rede
Mesma carga via NAT em vez de endpoints + US$ 8.900/mês
Aurora ocioso com min 0,5 ACU (se não desligar fora da rodada) + US$ 44/mês
20 MRs K0 a cada 15 min em produção, Synthetics ≈ US$ 238/mês Checkly: ~10× (C3 § 3.6)
Artefatos, retain-on-failure < US$ 1,50/mês sem: US$ 13–65/mês

A conclusão de custo: o compute é barato; o que decide o orçamento é a rede (uma ADR) e a cota (tempo) — e, para K2, o I/O do CREATE DATABASE, que só a medição fecha.

7.4 Armadilhas documentadas

  1. Cota inicial de 6 vCPUs = uma task de 4 vCPU. Todos os tickets no dia 1.
  2. Fargate não cacheia imagem: 905 MiB (runner) + Postgres + app puxados por task. SOCI v2 corta ~50% (AWS); imagem só-Chromium corta mais; a rota dos bytes decide o custo (ADR-15).
  3. Service worker da app derrota routeFromHAR em silêncio. serviceWorkers: 'block' é obrigatório e muda a app sob teste — declarado na MR.
  4. Auto-pause do Aurora: resume de 15–30 s assimétrico. Min ACU 4 na rodada.
  5. Spot no meio de um par = meio par.
  6. VOLUME da imagem Postgres, chown recursivo no start, trust em all, listen_addresses='*' — quatro armadilhas na imagem oficial (ADR-03).
  7. Template bloqueado: CREATE DATABASE falha se houver conexão ao template. O template nunca recebe a app. Databases órfãos sem reaper degradam o catálogo.
  8. O passo 0 mede um ruído que o próprio harness fabrica quando Chromium, SSR e API competem na mesma task — o limiar vem de fonte×fonte na mesma task, nunca de chute.
  9. Chromium 1.57+ teve regressão de memória; 706 MB de pico por contexto (pré-1.57) pode estar subestimado.
  10. Rodadas concorrentes disputam a cota e derrubam Map Runs com tolerância zero.

8 · Interfaces entre camadas

Contratos mínimos para duas pessoas — harness e plataforma — trabalharem em paralelo.

De → para Contrato
Molde → gerador JSON: { id, mrip, mrop, operatorDirection, preconditions[], example }
Gerador → validação MRCandidate = defineMR sem revisionNotes, com anchoredOn e falsifiedBy; rejeitada se vazios
Validação → catálogo MRCandidate + { baselineRun: pass, falsificationRuns: [{mutant, violated: true}], exercised: n }.mr.ts
Seleção → orquestração manifesto S3 versionado: [{ mrId, costClass, owner, taskShape, followers[], dbMode }]; schema de entrada da state machine e do ItemReader
Orquestração → runner env: MR_ID, MANIFEST_S3, RUN_ID, ATTEMPT, DB_MODE=sidecar\|template, DB_HOST/PORT/NAME, DB_SECRET_ARN, APP_URL, T0, STORAGE_STATE_S3[], ARTIFACT_BUCKET, EMF_NAMESPACE
Task dependsOn e health checks: postgres HEALTHY → init SUCCESS (K2) → server HEALTHY → storefront HEALTHY → runner; quem cria o database é o init container
Runner → projeção Page + ProjectionSpecProjection (JSON, com cardinality) ou OracleSetupError
Projeção → relação (Projection, Projection[]) → { verdict: pass\|fail, observables: [...] } — função pura
Runner → artefatos exit 0 = rodou; runs/<runId>/<mrId>/<attempt>/verdict.json sempre; projections/, traces/, replayPath em violação; nunca payload em Step Functions
Runner (por build) → ruído runs/<runId>/<mrId>/noise.json = divergência fonte×fonte
Artefatos → triagem histórico por mrId → controle de ruído → LLMTriage({ mr, projections, traces, noise }) → { class, rationale }
Reaper Lambda horária: DROP DATABASE WITH (FORCE) de pair_% com datcreated < now() − 1h
K0 runPair(browser, mr) puro; entrypoints lambda.ts e synthetics.ts; workspace k0/ fixado no runtime

9 · Aplicação de referência

9.1 Perfil (checklist de aceite para qualquer app-alvo)

Funcional: RF1 busca por texto acionável por URL · RF2 filtros facetados com semântica declarada · RF3 ordenação ≥ 2 critérios em URL · RF4 paginação enumerável com sinal de última página · RF5 alternância de apresentação (ou substituto) · RF6 categorias que particionam, verificável no seed · RF7 carrinho com subtotal e total distintos · RF8 cupom com regra de reaplicação documentada · RF9 ≥ 2 papéis e ≥ 1 recurso só do dono · RF10 rotas diretas estáveis · RF11 estado por sessão · RF12 variantes com estoque do seed.

Infra: I1 Docker · I2 Postgres · I3 seed determinístico · I4 partição provável no seed · I5 API administrativa scriptável · I6 licença · I7 sem service worker obrigatório · I8 API versionada · I9 demo pública · I10 locators estáveis · I11 a app cabe numa task (processos, memória, boot) · I12 controles alcançáveis por getByRole + click() sem overlay — header fixed z-50 sobre um radio custou dezenas de cliques falhos na primeira execução; o clique no label resolveu · sem modal bloqueante no fluxo de busca. I4, I5, I11 e I12 são obrigatórios.

9.2 Decisão

Principal: Vendure core 3.7.2 (GPL-3.0/VCL) + vendurehq/nextjs-starter-vendure@7d06ae0 (MIT).

Por que.

Como sobe. npx @vendure/create <nome> --ci --with-storefront --db postgres — 7m49s medidos, Postgres em Docker incluso. Imagem própria só para Fargate. No harness, o storefront roda com next build && next start — em dev o primeiro hit de cada rota leva 2–3 s e contamina o passo 0. Três processos (server, worker, storefront) dentro da task — I11 a medir.

Ressalvas medidas.

Contras. Core GPL (uso interno ok); starter jovem.

Reserva: Saleor (BSD-3 + storefront FSL-1.1-ALv2). Sandbox de validação do harness: Toolshop (with-bugs = mutantes gratuitos; licença proprietária, MariaDB — não é alvo).


10 · Plano de piloto

10.1 Fases de adoção

Fase Entrega Infra Responde
Fase 1 Oráculo implícito sobre os fluxos Playwright existentes — inclui MR-C9 com q=<script> nenhuma
Fase 2 MRs K0 (MR-A5 só caixa, MR-A7, MR-B3, MR-C10; MR-A3 e MR-C7 só em SPA com estado acumulado na UI) em Lambda no pré-deploy e Synthetics em produção, com R(·) que exclua ranking/personalização/promoção; avaliar Bombadil como motor destas Lambda + Synthetics E9b (replay sem camada 2) para K0; divergência de versão declarada
Fase 3 Camada 2 em CI → grupo A em Fargate com a app na task; MR-A4/MR-A8 amostradas VPC + endpoints, ECR + SOCI, task de 5–6 containers, Step Functions, IAM, cotas frio real, tmpfs, forma da task, limiar do passo 0
Fase 4 Sidecar → grupo B e MR-C1–C6, MR-C8; database por par para K2 e shrinking + Aurora Serverless v2, template pipeline, reaper, secrets E10 (custo de R(·)) completo; E9c com mutantes
Fase 5 Geração assistida: moldes → LLM → baseline → falsificação → promoção + Lambda de triagem, Langfuse E9a (que fração das MRs geradas sobrevive à validação?); E12 (§ 10.3)

10.2 Três pilotos

Três pilotos, do menor para o maior:

Piloto Etapas Horas
MVP de 1 semanao que convence um QA lead @vendure/create --ci + next build + Postgres em Docker (0,5 h) · 5 MRs à mão: MR-A1 (Computers ∧ Photo), MR-A7, MR-B1, MR-B3, MR-C6 · três projeções (R_grid, R_cart, R_pdp) · runner com settle e ruído fonte×fonte · falsifiedBy executado (sed no storefront) · entregável: tabela MR × mutante × pegou? — sem AWS, sem K2, sem LLM ~2 dias
Harness (local) — contém o MVP subir app + pré-condições via Admin API (1–2 h) · camada 2: storageState, HAR de assets, sidecar (6–8 h) · 4 projeções + 12 R(·) + indícios não confirmados (PISTAs) restantes (16–24 h — MR-B2/MR-B6/grupo C exigem promoções, dois storageState e leitura do core) · harness com OracleSetupError, exit code, verdict.json, seleção por owner (4–6 h) 27–40 h
Plataforma (Fases 3–4) ADR-15 rede + endpoints · ECR + pipeline SOCI · task definition de 5–6 containers com dependsOn e init · 4 roles IAM · ASL com Retry/Timeout/semáforo · Aurora + template + reaper + secrets · CDK · tickets de cota 24–40 h de infra + 16–24 h de experimentos (§ 12) = 40–64 h
Total o MVP está contido no harness; total = harness + plataforma 67–104 ≈ 70–100 h

O que medir primeiro, nesta ordem: (0) os mutantes — é a única coisa que diz se a MR pode falhar, e a app de referência entregou um equivalente e uma instância vácua no primeiro dia (§ 10.3); (1) a task com a app dentro sobe? memória/CPU por container, tempo até HEALTHY; (2) bytes e tempo de pull por caminho de rede, ±SOCI; (3) fonte×fonte na mesma task para calibrar o limiar do passo 0.

10.3 O experimento que só o piloto responde — E9c e E12, com os primeiros pontos

E9c — com MR validada em E2E web, que fração das violações é bug? E12 — que bug a família D acha que Meticulous (C) e Bombadil (P) não acham, e quanto vale?

E9c — medido em miniatura na app de referência (local, sed + HMR, revertido):

Mutante MR Asserção convencional
{or: id} item a item (o mutante óbvio) passa — mutante equivalente passa
[{ or: facetValueIds }] (OR real) MR-A1 (Electronics ∧ Computers): não detecta — 20 ⊆ 20, instância vácua · MR-A1 (Computers ∧ Photo): viola, 11 → 20 '11 products': detecta
removeFromCart sem mutate MR-B1: viola detecta

Os outros falsifiedBy (última página descartada; produto órfão; cupom duplicado; orderByCode sem dono) ficam para o piloto, com Stryker no starter e a variante with-bugs do Toolshop. Medir: mutantes que cada MR mata; violações sem mutante; passes vácuos. É o primeiro ponto com mutantes semeados antes das MRs — o de Zahid (10/10, C2 § 2) tem viés de seleção declarado.

E12 — primeiro ponto, medido na app de referência (amostra de 2, N5 na extrapolação): por cenário, MR ≈ 2× a asserção (45–60 min × 20–30 min para um QA sênior do zero); 80–90% do custo da MR é R(·) + ancoragem; o custo marginal da k-ésima MR sobre projeção pronta é ~2 min, e a asserção não amortiza. Ruído fonte×fonte 0/7; falsos positivos por estado não assentado 4/4 sem settle (§ 3 passo 3b). A resposta honesta a um QA lead com suíte Playwright existente — onde a MR ganha por família de tela, onde a asserção com valor esperado é mais curta e mais forte, e por que a MR soma à suíte em vez de substituí-la — está no C2 § 4, "Quem escreve".

O que o piloto ainda deve a E12: para os mesmos mutantes, o que Meticulous (replay diferencial) e Bombadil (propriedade sobre trace) pegam; e o custo marginal na k-ésima MR, não "10 MRs × asserção".


11 · Riscos e modos de falha

Risco Efeito Mitigação
App dentro da task fabrica o ruído que o passo 0 mede falso positivo de infra limiar calibrado por fonte×fonte na mesma task; forma 4 vCPU/8 GB
Egress de imagem via NAT US$ 8.900/mês ADR-15
Rodadas concorrentes disputando cota Map Runs falham com tolerância zero semáforo; Retry em AmazonECS.Unknown; ToleratedFailurePercentage
Chromium/Postgres sem shm crash lido como violação tmpfs nos dois; passo 0
Auto-pause / resume assimétrico fonte quente, seguidor frio min ACU 4 na rodada
Service worker derrota o HAR em silêncio estados não equivalentes serviceWorkers: 'block', declarado
Relação ajustada até passar oráculo que não pode falhar revisionNotes; falsifiedBy executado com Stryker
Estado não assentado após mutação lido como violação falsos positivos em MR-B1 (§ 10.3) § 3 passo 3b; rerun 7a′
Mutante equivalente ou instância vácua por inclusão falsifiedBy que não falsifica executar na promoção; pré-condição B ⊄ A
Locator estrutural (ícone sem nome) quebra sem requisito para reancorar declarar com risco; PR de aria-label
Um time de QA cortaria 15 de 24 MRs catálogo maior que a adoção começar pelas 9 que um QA escreve (C2 § 4, "Quem escreve")
Healer / Runtime Recovery / cache de agente julgamento silencioso removidos (ADR-13)
Versão do Synthetics atrás do CI divergência CI↔produção workspace K0 no runtime; job com dono
Trace + storageState em S3 credencial reproduzível por 30 dias bucket criptografado, acesso restrito, retenção
Postgres trust alcançável banco sem senha na VPC listen_addresses=localhost, SG sem inbound
PG 18 no Serverless v2 cluster não provisiona 17.x até verificação
Metade das instâncias cai na validação (MetaFOE) custo de descoberta dobra orçar 2× na Fase 5
Licenças: Vendure GPL; Saleor FSL; metamorph sem licença; Diffy NC-ND redistribuição uso interno; ler, não copiar

12 · Aberto — só se resolve medindo

Pergunta Experimento Sai como
A task com a app dentro sobe? Forma real 20 tasks; memória/CPU por container; tempo até HEALTHY ADR-01
Frio real, ±SOCI, x86/arm64, por caminho de rede (endpoints × NAT × IP público) 20 tasks × combinações; describe-tasks (pullStartedAt/pullStoppedAt/startedAt); bytes por rota ADR-01, ADR-15
Limiar do passo 0 fonte×fonte na mesma task, 20 pares ADR-09
tmpfs valida em Fargate (e via CDK/CloudFormation)? ajuda Chromium e Postgres? /dev/shm padrão RegisterTaskDefinition + 20 pares ±tmpfs; workers paralelos no Postgres ADR-02
Sidecar: pg_isready, custo do PGDATA no pull, chown no start imagem com 50/500 MB; time + pullStoppedAt ADR-03
Versão do Aurora Serverless v2 (18.x?) describe-orderable-db-instance-options --db-instance-class db.serverless ADR-04
CREATE DATABASE TEMPLATE sob 20 criações concorrentes, min ACU 0,5 e 4; VolumeWriteIOPs psql paralelo ADR-04 (Standard × I/O-Optimized)
Failures do RunTask sob cota estourada; o que chega ao Cause do .sync provocar cota ADR-05 (Retry)
Latência de clone Aurora (caso por PR) clone em dois passos (restore + instância), tempo do control plane referência
Tamanho de trace.zip nas MRs do Vendure --trace on em 10 MRs ADR-08
Lambda + imagem Playwright arm64: 2 contexts sem --single-process? função 4 GB com aws-lambda-ric ADR-06
DescribeRuntimeVersions expõe a versão do Playwright? chamada ADR-06
Os 4 falsifiedBy restantes (última página, órfão, cupom, orderByCode) Stryker + patches E9c
Bombadil expressa as MRs K0 do catálogo com menos código que o harness? reescrever MR-A5, MR-A7, MR-C10 em Bombadil Fase 2, ADR-12
Taxa de violação→bug com MR validada e mutantes § 10.3 E9c
Que bug a família D acha que Meticulous (C) e Bombadil (P) não acham § 10.3 E12

13 · O que não usaríamos, e por quê


Evidência: pesquisa/exploracao/X4-aws-plataforma.md, X2-mstwi-metamorph.md, X3-mercado-n4.md; asserções rastreadas A19, A26–A32 em pesquisa/protocolo.md § 4.

Registro: revisões R3 (plataforma) e R6 (execução) em pesquisa/revisoes/v2/; código de referência das MRs executadas em R6-qa-lead.md § 5; projeções em pesquisa/exploracao/X5-app-referencia-projecao.md.