prova — um clipe ininterrupto da jornada alterada
Um revisor deve poder assistir ao que um usuário veria, em velocidade normal, do início ao fim. O clipe é a evidência; uma captura de tela, uma folha de contatos ou uma gravação acelerada não são.
Fonte da verdade para o que cada nível exige: docs/process/PROOF-POLICY.yml (T0 nada; T1 testes focados + typecheck;
T2 adiciona um clipe da jornada alterada, desktop, mais telefone se o layout mudou, em um mundo com mente falsa; T3/T4 adicionam mais).
Obtenha o nível do diff atual com node tools/proof/tier.mjs --base origin/main.
Regras
- Ininterrupto e em velocidade real. Um contexto de navegador, uma tomada. Nunca corte, emende, acelere ou desacelere. Se a jornada
for maior que
max_secondsda política, grave uma jornada mais curta que ainda mostre a alteração, não uma mais rápida. - A tomada começa na chegada (o padrão): a gravação começa quando o mundo está na tela e o cartão de chegada
desapareceu, então o clipe abre no mundo, não no carregamento. A partir daí é uma tomada ininterrupta e em velocidade real da jornada,
que é o que a política pede. Quando o carregamento ou a chegada é o que a alteração trata (o cartão de chegada, uma
tela de carregamento, primeiro paint), ou a jornada não está em uma página de mundo (a lista de mundos,
login), passe
--from-load: a tomada então começa na criação da página. Ostartdo sidecar (arrivalouload) indica qual foi usado, eupload.mjs --proofo copia paraproof.json media[]. - Durante o dia. Em uma URL de mundo local, o gravador define o mundo para o meio-dia com seu relógio pausado antes de abrir a página
(
--time-of-day 12, o padrão; a rota god env como o desenvolvedor proprietário). Mundos de desenvolvimento iniciam às 06:00 e executam um dia de mundo por hora real, então sem isso um mundo ativo por 40 minutos estará à noite. Configurá-lo é uma entradaworldno histórico god do mundo, e o relógio permanece pausado depois. Passe outra hora, ou--time-of-day keepquando o relógio, a luz ou o histórico god é o que a alteração trata. OworldTimedo sidecar indica o que foi definido. - Entrada real. Dirija com
page.mouse,page.keyboard,page.touchscreenou cliques de localizador, como um usuário faria. Não chame internos do aplicativo depage.evaluatepara fazer as coisas acontecerem na câmera. - Mentes falsas, dados isolados. Apenas mundos locais,
BRAIN_MODE=fake, um diretório de dados novo. Nunca grave contra um mundo de produção a menos que o mantenedor peça, e nunca com um perfil de navegador pessoal conectado. - Nunca faça commit de vídeo (ou quadros) no repositório. Trabalhe em
.e2e/(ignorado pelo git). - O clipe deve mostrar a alteração. Assista-o (ou pelo menos extraia quadros, abaixo) antes de anexá-lo.
1. Inicie um mundo com mente falsa
Precisa de um cliente web compilado (pnpm install --frozen-lockfile && pnpm build; pule se
apps/web/dist/index.html existir e estiver atualizado para sua alteração). Em um clone novo, a verificação pré-construção da build falha
até que as mídias web estejam no lugar: node tools/assets/pull.mjs --only 'apps/web/**' (alguns arquivos) é suficiente para isso.
node tools/god-skill/dev-world.mjs proof 4710 --genesis worlds/templates/hello-hamlet.json --fresh --data .local/proof
# imprime http://127.0.0.1:4710/w/proof?dev=1 (pid N, log .lane/proof.log); o servidor continua executando em segundo plano
A página inicial (lista de mundos, Novo mundo) é servida pelo lançador de mundos, não por um único mundo. Para uma jornada que começa lá, execute um lançador (seus mundos sempre recebem mentes falsas) e crie um mundo para a lista através de sua API:
node tools/world-launcher/launcher.mjs --port 4720 --range 4721-4723 --data "$PWD/.local/proof-launcher" --eager 0 &
curl -s -X POST -H 'content-type: application/json' -H 'authorization: Bearer dev:proof' \
-d '{"name":"Proof Garden","template":"hello-hamlet"}' http://127.0.0.1:4720/__sim/worlds
# grave contra http://127.0.0.1:4720/ ; pare o lançador pelo seu pid (ele para seus mundos)
Escolha o template que contém a superfície alterada (worlds/templates/: hello-hamlet é pequeno e rápido; eden-2026,
mars-two e outros são maiores). Use uma porta livre; e2e/config.mjs lista portas reservadas para outros harnesses. Pare o servidor
com kill <pid> quando terminar.
Prova rápida de que o aplicativo funciona: o smoke local
Antes de gravar, ou como prova completa de uma alteração T1 que toca o servidor ou aplicativo web, execute o smoke local. Ele inicia seu próprio lançador isolado (mentes falsas, login de desenvolvimento, dados sob o diretório de execução) em uma porta livre, abre a lista de mundos e um mundo, verifica se uma pessoa é desenhada sem erros de console e sem requisições falhas, e para o que iniciou:
node e2e/local-smoke.mjs --port 9950 --clip # --build primeiro em um clone novo; omita --port para qualquer porta livre
# → .local/e2e/<timestamp>/summary.json, shots/1-world-list.png 2-world.png 3-person.png, clip.mp4
node tools/proof/upload.mjs --pr $N .local/e2e/<timestamp>/summary.json .local/e2e/<timestamp>/shots/*.png
status: "pass" em summary.json é a evidência; seu clipe é uma tomada ininterrupta e em velocidade real da jornada smoke. Não é
um clipe da sua alteração: uma alteração T2 ainda grava sua própria jornada abaixo. Detalhes: docs/testing/e2e.md#local-smoke.
2. Escreva os passos
Copie .claude/skills/proof/steps.example.mjs para .e2e/proof/<slug>/steps.mjs e programe a jornada alterada:
faça a coisa, mostre o resultado. Com o início padrão de chegada os passos começam no mundo que chegou (a marca arrived
é 0:00); com --from-load eles começam logo após a navegação. mark('label') carimba um tempo de capítulo no sidecar
para que o PR possa dizer "o painel abre em 0:07". Mantenha-o na jornada que a alteração afeta.
3. Grave
node .claude/skills/proof/record.mjs --url 'http://127.0.0.1:4710/w/proof?dev=1' \
--steps .e2e/proof/<slug>/steps.mjs --out .e2e/proof/<slug>/desktop.mp4 --viewport desktop
# layout ou toque mudou? também:
node .claude/skills/proof/record.mjs --url '...' --steps .e2e/proof/<slug>/steps.mjs \
--out .e2e/proof/<slug>/phone.mp4 --viewport phone
- Navegadores: usa
PLAYWRIGHT_BROWSERS_PATH, senão/opt/pw-browsersquando presente. Se o Chromium estiver faltando:PLAYWRIGHT_BROWSERS_PATH="$PWD/.e2e/browsers" pnpm --filter mars-campus-viewer exec playwright install chromium. - Playwright é resolvido da mesma forma que o harness de playtest (
tools/playtest/lib/platform.mjs: um globalplaywright, senão@playwright/testdemars/viewer). Nenhuma GPU é necessária; WebGL executa no SwiftShader. --max-secondsassume o padrão da políticamax_seconds. Saída 3 significa que a jornada passou dele: encurte-a.- Com ffmpeg: um mp4 com uma barra de progresso na parte inferior e um timecode
hh:mm:ss / total(o timecode precisa de um ffmpeg compilado com drawtext; otimecodedo sidecar indica se está presente). Sem ffmpeg: o.webmbruto; diga isso no PR (players ainda mostram sua própria barra de avanço). - Cada clipe recebe um sidecar
<clip>.json: url, viewport, duração,start,startedAt, marcas (arrivedentre elas),worldTime, erros de console, commit e flag de sujeira. Um Playwright sempage.screencastgrava desde a criação da página mesmo sem--from-load; o sidecar indica isso emstartFallback. - A página tem
--arrival-seconds(padrão 120) para chegar antes da tomada começar; uma página que nunca mostra um mundo falha (saída 1) com uma dica para usar--from-load, uma imagem de onde travou (<clip>.open-failed.png) e um sidecar. Erros de console no sidecar são descobertas: explique ou corrija-os.
A execução leva bem menos do que o limite de 10 minutos em primeiro plano. Para vários clipes de uma só vez, execute-os em segundo plano
(nohup setsid node ... > .e2e/proof/<slug>/rec.log 2>&1 &) e monitore o log.
4. Verifique você mesmo
for t in 1 4 8; do ffmpeg -loglevel error -y -ss $t -i .e2e/proof/<slug>/desktop.mp4 -frames:v 1 .e2e/proof/<slug>/t$t.png; done
Observe os quadros (Leia os PNGs). O estado alterado deve estar visível, o mundo deve ter carregado (não uma tela em branco ou canvas carregando), e nada no quadro deve ser privado. Se não mostrar a alteração, corrija os passos e grave uma nova tomada; não anexe um clipe que não prove a alegação.
5. Anexe ao PR (nunca faça commit)
Verifique cada payload de texto/JSON de saída com node tools/proof/publication.mjs <arquivos de payload> antes da publicação.
Inspecione pixels de mídia, áudio, nomes de arquivo e metadados binários separadamente. Caminhos de dispositivo, nomes de host, IPs de dispositivo/privados,
URLs locais e identificadores operacionais pertencem apenas a registros privados. Use caminhos relativos e rótulos de captura
neutros. O uploader bloqueia vazamentos de texto detectados antes de enviar o lote; isso não pode certificar a privacidade da mídia.
node tools/proof/upload.mjs --pr $N --proof .e2e/journal/$ISSUE/proof.json .e2e/proof/$SLUG/desktop.mp4 .e2e/proof/$SLUG/phone.mp4
- O alvo padrão é a própria pré-release do PR
proofs-<n>(tag no branch padrão, criada no primeiro uso); uma release por PR mantém cada uma abaixo do limite de 1000 ativos do GitHub, que a antiga release compartilhadaproofsatingiu.--release <tag>(ouSIM_PROOF_RELEASE) nomeia outra release. Os ativos são nomeadospr-<n>.<file>, e uma regravação recebe-2,-3, para que tomadas anteriores, incluindo as falhas, permaneçam linkáveis.SIM_PROOF_UPLOAD_URL(+SIM_PROOF_UPLOAD_TOKEN) muda para outro endpoint, como um CDN. - Com
--proof, links de clipe e imagem são escritos emproof.json media[], outros arquivos (logs, JSON) emfiles[], e proof.json é enviado por último. Cole seu link e os links do clipe na seção de Prova do PR. A skilljournalcobre proof.json. - Preview inline. O GitHub serve ativos de release como downloads, então um link mp4 nunca reproduz no PR. Para cada
vídeo,
upload.mjscria um pequeno GIF animado com ffmpeg (<clip>.preview.gif: 6 s em velocidade real a partir da marcaarriveddo clipe, ou a partir de 0 para uma tomada--from-load; no máximo 5 MB), faz upload dele ao lado do clipe, registra-o comomedia[].previewe imprime uma linha como[](…pr-412.desktop.mp4). Cole essa linha: o preview reproduz no PR e clicar nele abre o clipe completo. Quando a alteração aparece mais tarde no clipe, escolha a janela com--preview-start <s>(e--preview-seconds <s>). O preview é um ponteiro, não a prova: o mp4 ininterrupto é. Sempre coloque um link explícito Vídeo ininterrupto em resolução total antes de cada preview, informando as dimensões codificadas reais e a duração. Preserve a resolução da captura e enquadre a ação para que o contato possa ser revisado. Não aumente a escala de uma captura ou use o GIF reduzido para alegar prova de movimento. - Saída 3: o upload está bloqueado (sem
gh, não conectado, sem permissão ou sem rede, como em muitas sessões em nuvem). Informe ao usuário quais clipes estão aguardando e onde. Poste um comentário no PR com a duração de cada clipe, viewport, resumo do sidecar sanitizado (marcas, erros de console, commit), e um comandorecord.mjsrelativo ao repositório e arquivo de passos, para que qualquer um possa regravá-lo. Marque a prova como "upload pendente" no corpo do PR. Nunca cole vídeo como base64, nunca faça commit dele e nunca o envie para outro branch.
gh pr pode não funcionar em sessões em nuvem; comente com
gh api repos/$REPO/issues/$N/comments -F [email protected]/proof/$SLUG/comment.md.
O que escrever ao lado do clipe
Uma linha por clipe: qual jornada, qual template de mundo, viewport, duração, commit e o momento em que a alteração está visível ("0:07 os assentos destacam e a Alma senta"). Além de quaisquer erros de console e por que são aceitáveis.
