Harness thin para criar, evoluir e produzir games com IA. Compartilha conceitos, processo, seleção de contexto, marcos de produção e evidência. Cada jogo continua usando sua engine, suas regras, seus assets e seus validadores.
Fácil de começar: um pedido vira um ciclo jogável, sem nove templates vazios. Difícil de rebaixar: feel, áudio, pacing e receita de conteúdo fazem parte do recorte, não de um “polimento depois”. “AAA” aqui é só o piso de acabamento da slice — não tier de publisher, orçamento nem adjetivo de trailer. O alvo honesto com IA é AA / Triple-I nesse piso.
Não é um motor. Não publica sozinho. Não mede diversão. Não promove marcos.
Executor para macOS/Linux, Python 3.10+; biblioteca padrão, sem instalação de
dependências. Testes do harness usam também Node/npm quando exercitam
package.json.
Playground: games.alanicolas.com/framework
Chegar num laboratório que já tem jogos
O framework pode ser usado por vários workspaces com uma única implementação.
Cada laboratório mantém um link framework/core para este checkout e um
encaminhador curto em framework/scripts/game.py. Regras e evidências locais
ficam no laboratório; recipes, packages, templates e testes compartilhados ficam
aqui. Ligação e personalização.
Este é o caso normal: a raiz de trabalho não está vazia. O primeiro movimento é revisar o que existe, não criar mais um.
python3 scripts/game.py doctor --root /caminho/do/laboratorio
python3 scripts/game.py discover --root /caminho/do/laboratorio
python3 scripts/game.py next /caminho/do/laboratorio/um-dos-jogos --focus feel
discover lê cada jogo da raiz e devolve o que os distingue: quantas das nove
áreas mínimas têm candidato, quantas estão em rascunho, se há um passo registrado
para retomar e onde, o piso de acabamento que o projeto declara e quantas
dimensões ainda não têm linha, quais validadores existem, quantos papéis de
áudio estão declarados e vazios, se o feel tem constante e recibo de
observação, se o achado de playtest tem forma, se alcance, save e orçamento
estão declarados no código, se a
direção de arte aparece no disco, se o conteúdo saiu do código e se existe
passo de empacotar. signals nomeia os mesmos flags que o next usa
para o primeiro ciclo, o ofício, o feel sem recibo, o achado sem forma,
o convite, a origem sem recibo e as lacunas de dimensão — sem propor e
sem ranquear. Se o package declara os scripts, o discover nomeia os scripts que o package já declara. Lista no disco não é passo executado. Sem chave scripts. Se o roteiro recusa que o documento comprove qualidade, o discover nomeia a qualidade que o roteiro já recusa. Conta no disco não é acabamento. Sem chave qualidade. Sinal
verdadeiro não é partida jogada. Lista de arquivo sem recibo não é
licença. Lista de chave ausente não é alcance observado. É essa diferença que
uma listagem de caminho e tipo apagava — três jogos em estados incomparáveis saíam
iguais. --plain volta ao caminho e tipo, sem ler documento nenhum.
A ordem é a do disco e não muda: o harness não classifica os jogos por urgência, porque nada aqui observa qual importa mais. Ele conta e lê; escolher continua sendo trabalho de quem olha. Um projeto ilegível aparece com o motivo, em vez de derrubar a revisão ou desaparecer da lista.
Começar um jogo novo
Com starter (REUSE de infraestrutura já testada):
python3 scripts/game.py start --idea "atravessar estilhaços para guardar a corrente"
python3 scripts/game.py start /caminho/do/laboratorio/meu-jogo --starter canvas-arcade --idea "atravessar estilhaços para guardar a corrente"
Sem caminho, --idea nomeia e cria a pasta (ao lado do framework se o start corre de dentro desta árvore; no diretório atual se corre de fora). guide --idea continua só no comando, não no disco. O JSON devolve open (o comando de agora, igual a play), then.note (o próximo comando do harness depois de uma partida), os mesmos steps do guide com o passo 1 feito e, se o starter declara, cycle (verbo, porta, teclas, cluster de uma mão, toque, controle e as queries de look, chuva, par, seed, relógio e convite). Se o projeto — ou o starter, antes do destino existir — declara as ferramentas, then também nomeia pair, look, table e sfx. Se o tools/new-pair.* nasce look e chuva, o start nomeia o par que o pair já nasce. Ferramenta no disco não é alguém de fora. Sem chave pair no recibo. Se declara session, then a aponta e o prompt a nomeia (Sessão:). Não executa e não observa. Se o disco tem last-run com seed, then aponta a seed e o convite; nomear o endereço não observa. O note do mapa sugere o autor do git ou do ambiente; não é quem jogou. Nomear o ofício não pinta. Depois de um recibo de observação, o prompt aponta esses três em vez de repetir só o primeiro ciclo. Não executa o jogo. --idea entra na abertura se houver data/copy.json. O start não planta os rascunhos; --docs os cria. O init continua plantando. A frase na tela não muda o verbo. runtime lê o node do PATH se o play pede npm ou node; sem 20+ o prompt avisa. Nomear não serve. Depois do init, ?look=dusk ou ?look=calm troca a paleta (campo e a página), ?spawn=dusk ou ?spawn=calm troca a chuva, ?mood=calm ou ?mood=dusk troca o par e ?invite=1 some a tabela — as quatro sem recomeçar o projeto. ?seed=<n> abre essa partida e ignora o hold. ?speed=0.75 dilata o relógio da partida; 1 e fora da faixa somem. A página nomeia o par no select. Look ou chuva explícitos vencem o mood no próprio eixo. Trocar a chuva do par recomeça a partida; trocar só o look não. Ferramenta no disco não é alguém de fora nem mix ouvido.
guide (também sem subcomando: python3 scripts/game.py) mapeia os três passos — start → jogar → note — sem executar nenhum. open é o comando de agora (o start se o destino ainda não existe, o play se já existe); prompt o nomeia para colar e também sai em stderr — o JSON fica no stdout. Se o starter declara o verbo e as teclas, o prompt as nomeia — inclusive a porta — antes do destino existir. Se o manifesto declara o relógio, o guide nomeia o relógio que o manifesto já declara. Frase no disco não é partida observada. Sem chave speed. Se o roteiro recusa que o mural seja onboarding, o guide nomeia o onboarding que o roteiro já recusa. Texto no disco não é a primeira ação. Sem chave onboarding. Se a receita recusa que o screenshot comprove feel, o guide nomeia o screenshot que a receita já recusa. Recibo no disco não é peso percebido. Sem chave screenshot. Se o processo recusa que o comando abra o jogo, o guide nomeia a abertura que o processo já recusa. Nome no disco não é partida. Sem chave abertura. Sem destino, a frase nomeia a pasta no comando do start (ao lado do framework se você está dentro desta árvore; no diretório atual se está fora). Não grava a frase nem cria a pasta. Sem destino, se o diretório atual é um jogo fora deste repositório, o mapa usa esse caminho. Na raiz deste repositório, sem --idea e sem caminho, o mapa recusa — o mesmo sem destino do start. A recusa nomeia o start --idea que este README já imprime. Nomear não cria. Não devolve mais start '<destino>'. De uma subpasta (o starter) ou de um jogo, o comando sem argumentos continua o mapa. O convite a começar na raiz leva a frase:
python3 scripts/game.py --idea "atravessar estilhaços para guardar a corrente"
python3 scripts/game.py guide /caminho/do/laboratorio/meu-jogo --idea "atravessar estilhaços para guardar a corrente"
Perdeu o JSON do start? play (também open) aponta de novo o comando que abre o jogo, sem executar. Sem caminho, o único jogo do laboratório basta; dois pedem o caminho.
python3 scripts/game.py play
python3 scripts/game.py play /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py open /caminho/do/laboratorio/meu-jogo
play e open são o mesmo verbo. Se o play pede npm, o package.json tem dependências e node_modules falta, then.install nomeia npm install. Sem dependências a chave some. Nomear não instala. Se o serve recusa produção, o play nomeia a produção que o serve já recusa. Serve no disco não é publicação. Sem chave produção. url nomeia a superfície pedida (http://localhost:8080/ quando o script é serve e não há PORT). Nomear não serve. Se o serve tenta abrir o navegador, o prompt nomeia a tentativa. Sem o marcador, pede Abrir. Nomear não abre. Com tela, o avanço abre a porta. Depois de uma partida, a página grava o recibo se você escrever; o próximo comando do harness continua note. O prompt nomeia o playtest que o AGENTS.md já cita. Só lê. Sem os quatro não é achado. Sem then.playtest. Nomear o leitor não observa. note, next, feel e playtest sem caminho usam o mesmo resolvedor. Se o disco tem last-run com seed, then.seed aponta /?seed=<n> e, se o candidato nomeou a chuva ou o look, junta a mesa e a paleta. then.invite aponta o convite com os mesmos eixos. Nomear o endereço não observa. Vestir a query não grava. A chuva da query não retoma o hold. Na porta e no fim a região viva nomeia a mesa e o look que a chuva já veste — spawn e normal somem. Na porta o telefone vê Jogar: toque sem ter apertado. Depois do tap a porta não chama o avanço de cima. executed fica false. runtime lê o node do PATH se o play pede npm ou node; sem 20+ o prompt avisa. session aponta a partida simulada se o manifesto a declara; o prompt a nomeia. Não executa e não observa. Nomear não serve. Não cria pasta e não serve. Achar o único jogo do laboratório também não executa e não sente. A frase de agora sai em stderr; encanar o stdout continua o recibo.
guide não cria o projeto. next sai do caminho feliz: só entra em then.lost, quando o ciclo já correu e você não sabe o que falta. Com destino existente, preenche o comando que abre o jogo e o kind do passo de jogar. executed fica false. Sem start, o caminho em dois passos continua valendo:
python3 scripts/game.py init /caminho/do/laboratorio/meu-jogo --starter canvas-arcade --idea "atravessar estilhaços para guardar a corrente"
python3 scripts/game.py next /caminho/do/laboratorio/meu-jogo --focus feel
doctor observa Python, Node, git, ffmpeg, presença dos arquivos do framework (receitas,
templates, referências e pacotes), raiz, projetos reconhecidos, estudos, acervo
sonoro, starters disponíveis e os atalhos de skill do host — vigente, desatualizado
ou ausente, comparando conteúdo.
Sem jogo reconhecido e com starter, then.guide aponta o mapa ideia→ciclo com --idea. Se o README imprime o exemplo, o doctor nomeia o exemplo que o README já imprime. Frase no then não é pasta criada. Sem chave exemplo. Se a ambição recusa que o harness seja motor, o doctor nomeia o motor que a ambição já recusa. Convite no then não é runtime. Sem chave motor. Se a skill recusa que AAA seja tier de publisher, o doctor nomeia o publisher que a skill já recusa. Atalho no disco não é orçamento. Sem chave publisher. Se o mapa recusa que a ausência seja evidência negativa, o doctor nomeia a ausência que o mapa já recusa. Lista no disco não é laboratório. Sem chave ausência. Sem frase a raiz recusa.
Se o package pede Node, o doctor nomeia o engines que o package já declara. Pedido no disco não é binário no PATH. Sem chave engines. Nomear não instala. Se o manifesto declara as trocas, o doctor nomeia as substituições que o manifesto já declara. Manifesto no disco não é projeto criado. Sem chave substitutions.
Não cria e não executa. Sem starter, o aviso nomeia start --idea, não init.
Symlink apontando para o SKILL.md deste repositório conta como vigente: é o
atalho que não tem como ficar para trás. Não escreve nada; sinaliza bloqueio pelo
código de saída, e a correção que ele sugere roda como está — inclusive criando a
pasta do atalho.
init copia um starter, troca pelo nome do projeto os valores que o
starter.json dele declara e cria em docs/ os rascunhos que ainda faltam —
brief, gdd, mda, tdd, devlog e qa — além de AGENTS.md na raiz. Se o package declara o módulo, o init nomeia o módulo que o package já declara. Tipo no disco não é runtime instalado. Sem chave type. O MDA
nomeia a porta; brief e GDD já falavam e o cenário do MDA começava no
campo. Nomear a abertura não observa. Documento
vigente que o starter já trouxe (o art-bible do canvas-arcade) não é
reescrito: o template recusaria o destino e a decisão vigente sumiria.
Os seis rascunhos mais o art-bible cobrem sete das nove áreas mínimas que
scan cobra; as outras duas, origem e execução, ficam com o README e o
CREDITS do starter, então depois do init as nove têm candidato. Com
--no-docs, o art-bible permanece e os rascunhos não são criados; README,
CREDITS e art-bible cobrem três áreas. --idea escreve a frase da fantasia
no brief e em data/copy.json; o brief continua rascunho e a frase na tela
não muda o verbo. Os demais templates do ciclo entram depois, com template, quando a
etapa chegar. Não instala dependências, não toca no starter de origem e recusa
destino ocupado. scan reconhece o resultado no mesmo turno; o primeiro comando
que init aponta é o que serve o jogo. open, url e prompt nomeiam
a mesma superfície do start — o prompt também sai em stderr. Nomear
não serve. verify roda os validadores do starter onde houver Node. Se o roteiro recusa aprovar a criatividade, o verify nomeia a criatividade que o roteiro já recusa. Recibo verde não é aprovação. Sem chave criatividade. Se a ambição recusa que o recibo comprove diversão, o verify nomeia a diversão que a ambição já recusa. Log no disco não é experiência. Sem chave diversão.
next deriva uma proposta do estado no disco e ordena por dependência: módulo não baixado → sem
destino → sem entrypoint → área não localizada → ciclo jogável ainda sem partida
→ segundo ciclo de par, look, chuva e voz
→ papéis de áudio vazios → feel ainda sem observação → convite para quem nunca viu o jogo → achado sem forma →
acessibilidade sem opção
→ save sem versão → orçamento ausente → direção de arte ausente → conteúdo
ainda no código → empacotar ainda sem passo → artefato incompleto → artefato de outro HEAD → árvore pronta para servir → rascunho → documento sem versão
vigente → continuidade → sem instruções para o agente → validadores → origens sem
recibo → gate → ofício → barra. Se o processo pede uma ação recomendada, o next nomeia a ação que o processo já pede. Proposta no disco não é autorização. Sem chave ação. Se o roteiro recusa que o comando crie o jogo, o next nomeia a criação que o roteiro já recusa. Proposta no disco não é pasta criada. Sem chave criação. Se o processo recusa fabricar tarefa para cumprir o formato, o next nomeia a fabricaç��o que o processo já recusa. Lista no disco não é backlog. Sem chave fabricação. Depois de um init fresco — nove áreas com
candidato, seis ainda rascunho (o art-bible do starter já vem vigente), e um script que abre o jogo — a primeira proposta
é jogar o ciclo, não preencher os templates. Depois de um start — sem os seis rascunhos — a primeira proposta continua jogar o ciclo: jogo que abre não espera área localizada. Depois do recibo, se o projeto
declara pair/look/table/sfx e ainda não nasceu look, chuva ou voz deslocada,
o next aponta o segundo ciclo de par, look, chuva e voz — ferramenta no disco
não é alguém de fora. O verbo mudo vem em seguida: papéis
declarados sem arquivo. Depois, se o código nomeia perdão e hitstop e ninguém
registrou uma observação no projeto, o next pede esse recibo — constante
nomeada não é peso percebido. Depois do recibo, se o achado não nomeia
problema, evidência, hipótese e medição, o next pede a forma — nota de
partida não é métrica. Sem opção de alcance no código, sem versão de
save ou sem artefato de orçamento, esses ramos vêm antes dos rascunhos. Sem
paleta ou art-bible vigente, com conteúdo só no código, ou com manifesto e
nenhum passo de empacotar, esses ramos também vêm antes dos rascunhos. O gate tem três ramos: linha
de gate malformada, pergunta de valor e critério pendente — nessa ordem, porque
terminar o que talvez não devesse existir é o desperdício que um gate existe para
interromper. O ofício tem dois: linha de ofício malformada e checklist pendente.
A barra tem quatro, na ordem: linha de
degrau malformada, dimensão sem linha, duas linhas em conflito e — só então —
subir a dimensão mais baixa. Num projeto sem tabela, portanto, a última proposta
é declarar os degraus, não subir um deles. Devolve
também as alternativas descartadas. executed permanece false: o harness propõe,
quem decide é o agente ou você.
Sem starter, em qualquer engine, um documento único de design cobre as nove áreas de um jogo pequeno:
python3 scripts/game.py template game-design --project /caminho/do/laboratorio/meu-jogo --output /caminho/do/laboratorio/meu-jogo/docs/game-design.md
python3 scripts/game.py context /caminho/do/laboratorio/meu-jogo --focus create --stage game-design --root /caminho/do/laboratorio
O template game-design, preenchido, é reconhecido pelo scan como cobertura das
nove áreas. context entrega o recorte de leitura e a checagem documental. Se o processo recusa que o hash seja leitura, o git nomeia a leitura que o processo já recusa. Identidade no disco não é inspeção. Sem chave leitura. Itens
optional do doctor não bloqueiam. --root pode vir antes ou depois do subcomando.
No Codex ou no Claude, invoque $game-dev com o projeto e a mudança desejada:
$game-dev crie um conto jogável em Canvas a partir do acervo existente
$game-dev desenvolva o Game Brief e o GDD desta ideia, usando MDA
$game-dev monte o plano de produção e diga em que marco estamos
$game-dev o pulo ainda não tem peso; ajuste o feel e o áudio dessa ação
A fonte é SKILL.md. Copie-a para o atalho do host
(.agents/skills/game-dev/SKILL.md ou .claude/skills/game-dev/SKILL.md), ou
aponte um symlink para ela e nunca mais pense nisso; doctor avisa quando a cópia
ficou para trás.
Comandos da skill
A skill roteia por intenção, no modelo da skill impeccable de frontend. O
SKILL.md é uma entrada curta: resolve contexto uma vez, escolhe a
rota e carrega o piso de execução somente antes de
editar o jogo. Navegação sem subcomando usa sinais do laboratório para liderar
com duas ou três recomendações e depois mostrar o menu completo
(rota); direção ou jogo realmente novos usam um contrato
separado (trabalho novo). Os contratos minuciosos do
harness continuam no manual operacional.
São vinte e três sub-comandos em seis categorias, cada um com uma referência
própria em commands/. $game-dev critique <jogo> carrega
commands/critique.md e segue seu fluxo; texto livre cai no comando mais próximo
pela situação. Binding ou contexto divergente é reportado, nunca reparado como
efeito colateral.
| Categoria | Comandos |
|---|---|
| Construir | craft, shape, teach, document, init |
| Avaliar | critique, audit, playtest |
| Refinar | polish, feel, audio, harden, onboard, distill |
| Ampliar | juice, visual, content |
| Corrigir | adapt, optimize, clarify |
| Produzir | next, produce, release |
O catálogo é commands/commands.json: categoria,
descrição, dica de argumentos, focos e leituras canônicas de cada comando. Três
comandos do harness o servem:
python3 scripts/game.py commands --root /caminho/do/laboratorio
python3 scripts/game.py pin critique --root /caminho/do/laboratorio
python3 scripts/game.py unpin critique --root /caminho/do/laboratorio
commands imprime o catálogo em JSON, com o caminho de cada referência e se ela
existe. pin cria um atalho próprio do host (/critique passa a invocar
$game-dev critique) em cada diretório de skills onde a game-dev já está
instalada; o arquivo leva um marcador, e uma skill sua com o mesmo nome nunca é
sobrescrita. unpin remove só o que tem o marcador. doctor ganhou a checagem
commands: catálogo, arquivo de referência e linha na tabela do SKILL.md
precisam concordar, senão o menu manda o agente ler um arquivo que não existe.
Uma referência de comando é um orquestrador fino, não uma receita nova: diz qual
context rodar, qual receita ler, onde parar para o usuário, o que prova conclusão
e o que não fazer (contrato). As receitas continuam sendo
selecionadas por --focus; o comando acrescenta o fluxo.
Contexto por foco
python3 scripts/game.py discover --root /caminho/do/laboratorio
python3 scripts/game.py context /caminho/do/jogo --focus create --root /caminho/do/laboratorio
python3 scripts/game.py scan /caminho/do/jogo --root /caminho/do/laboratorio
python3 scripts/game.py context /caminho/do/jogo --focus architecture --stage tdd --root /caminho/do/laboratorio
python3 scripts/game.py context /caminho/do/jogo --focus feel --root /caminho/do/laboratorio
python3 scripts/game.py context /caminho/do/jogo --focus audio --root /caminho/do/laboratorio
Focos: create, mechanics, lifecycle, content, visual, audio, feel,
network, architecture, performance, accessibility, persistence, release,
production. Jogo novo começa em create. Acabamento do verbo usa feel e audio;
contrato: ambição. --root é aceito antes ou depois do
subcomando.
A escala de ambição (jam, product, aa) é o "register" da skill: governa
quantidade de artefatos e de conteúdo, nunca o piso do verbo. context a devolve
em scale: --scale declarado na conversa vence; sem ele, um campo Escala: num
documento do projeto só sugere, com arquivo e linha; sem nenhum dos dois, o
campo vem nulo e a skill infere uma vez e pede para gravar no brief. "AAA" escrito
num brief é lido como aa, porque é o único sentido que este harness aceita para
a palavra. O comando lê o campo; não classifica o jogo.
O contexto entrega caminhos para leitura, registros já existentes, catálogos de
estudo (se um irmão Games-Frameworks existir, ou GAMES_FRAMEWORKS_ROOT),
menções locais de pause/reset/seed, foundation (nove áreas documentais),
production_bar (as dimensões de acabamento pertinentes ao foco), finish (perfil
do checklist de piso) e o acervo shared/sfx da raiz informada. Não executa o jogo. Se o mapa recusa que o catálogo ouça o starter, o context nomeia a escuta que o mapa já recusa. Acervo no disco não é mix ouvida. Sem chave ouve. Se a guia recusa preencher o checklist, o context nomeia o checklist que a guia já recusa preencher. Guia no disco não é observação. Sem chave checklist.
mentioned não é verified. candidate_found não prova suficiência, atualidade
nem aprovação.
Descoberta percorre até três níveis, reconhece Unity, Godot, Unreal (.uproject),
Defold, GameMaker (.yyp), Construct (.c3proj), RPG Maker (.rmmzproject/.rpgproject),
Ren'Py, Roblox/Rojo, PICO-8 (.p8), Haxe, Flutter (pubspec.yaml), .NET (.sln/.csproj),
package.json, Cargo, CMake, Python (pyproject.toml), Love2D (main.lua) e HTML, e
para na raiz de cada projeto. Marcadores próprios de engine vencem manifestos que ela
carrega junto (RPG Maker MZ tem package.json; Unity gera .csproj). shared/ e
pastas de build das engines não entram como jogo.
Pacotes de plataforma e gênero
O núcleo é agnóstico. Quando o projeto tem engine identificável, context acrescenta
o pacote de plataforma correspondente logo após a receita; quando
o gênero é declarado, acrescenta o pacote de gênero:
python3 scripts/game.py context /caminho/do/jogo --focus feel --genre platformer --root /caminho/do/laboratorio
Plataformas (18): web, unity, godot, unreal, defold, gamemaker, construct,
rpgmaker, renpy, roblox, pico8, haxe, flutter, dotnet, cpp, cargo,
python, lua. Gêneros (23): narrative, adventure, platformer, action-adventure,
shooter, fighting, stealth, horror, racing, sports, rhythm, turn-based,
deckbuilder, strategy, tower-defense, puzzle, simulation, survival-crafting,
rpg, roguelike, multiplayer-competitive, idle, casual. Cada pacote traz comandos
reais de execução/teste, ciclo de vida, pipeline, ferramentas de medição e riscos do
gênero — como convenções a confirmar no projeto, não como capacidade certificada. Se o pacote recusa que teste unitário prove o navegador, o context nomeia o navegador que o pacote já recusa provar. Pacote no disco não é comportamento no aparelho. Sem chave navegador. Se o índice recusa que o pacote certifique capacidade, o context nomeia a capacidade que o índice já recusa. Pacote no disco não é comportamento. Sem chave capacidade. Se o mapa recusa que o pacote seja extração, o context nomeia a extração que o mapa já recusa. Convenção no disco não é repositório executado. Sem chave extração. Se o mapa recusa que a menção seja mecânica obrigatória, o genre_mentions[n] nomeia a mecânica que o mapa já recusa. Campo no disco não é regra do jogo. Sem chave mecânica. Se a receita recusa que o nome seja API, o context nomeia a API que a receita já recusa. Vocabulário no disco não é runtime. Sem chave api. Se a barra recusa que o determinismo seja capacidade, o capabilities desconhecido nomeia o determinismo que a barra já recusa. Lista no disco não é ciclo demonstrado. Sem chave determinismo.
Um campo Gênero: em documento do projeto aparece em packs.genre.suggested; só
--genre carrega o pacote. context.packs explica cada seleção.
Barra de acabamento
O que separa um protótipo de um jogo tratado como produto não é orçamento: é acabamento por dimensão de ofício. A escada tem cinco degraus — protótipo, jogável, fatia, publicável, carro-chefe — em dez dimensões: feel, legibilidade, direção de arte, mixagem, ritmo, confiança de estado, performance, acessibilidade, escala de conteúdo e release.
O degrau percebido é o mínimo entre as dimensões, não a média. Esse é o modo de falha típico de um estúdio assistido por IA: texto, quantidade de conteúdo e variação visual sobem sozinhos, enquanto feel, mixagem, estabilidade de quadro e confiança de estado ficam para trás e definem a leitura final.
context seleciona as dimensões pertinentes; nenhum comando atribui um degrau.
Cada critério é observável, mas a observação é trabalho humano ou do agente, com
condição e autor declarados.
O que o projeto declara é outra coisa, e essa o harness lê:
python3 scripts/game.py bar /caminho/do/laboratorio/meu-jogo
bar procura, nos documentos do projeto — README, qa, devlog, gdd, art-bible —
uma tabela com uma linha por dimensão: degrau atual, degrau seguinte e o critério
que falta. É o formato que o README do
starter já usa. O comando devolve o
piso, quais dimensões estão nele e o degrau percebido — este último só quando
as dez tiverem linha, porque dimensão não declarada não é dimensão alta. Se a prosa declara o mínimo, o bar nomeia o mínimo que a barra já declara. Degrau no disco não é acabamento observado. Sem chave mínimo. Se a barra recusa que o degrau seja prazo, o bar nomeia os prazos que a barra já recusa. Linha no disco não é calendário. Sem chave prazos. Se a barra recusa que o nome seja uma das dez, o bar nomeia a dimensão que a barra já recusa. Linha no disco não é acabamento. Sem chave dimensão. Se a barra recusa que duas linhas discordantes se resolvam por precedência, o conflicts[n] do bar nomeia a precedência que a barra já recusa. Linha no disco não é acabamento. Sem chave precedência.
Ele confere a forma da declaração, não o jogo, e relata em problems o que
encontra com arquivo, linha e motivo: dimensão fora das dez (o caso típico é erro
de digitação), degrau fora dos cinco, alvo que não é o degrau imediatamente
seguinte, alvo ausente. Uma tabela bem formada e otimista sai daí intacta,
porque o degrau é afirmação de quem escreveu. O ganho não é aferição, é que a
dimensão mais baixa passa a ter nome — e next propõe subir exatamente ela,
citando o critério escrito no documento e a linha de onde veio, em vez de listar
as dez. Havendo linha malformada, ele propõe corrigi-la primeiro: ela é a causa da
dimensão que aparece como não declarada.
Gates
A barra descreve onde o jogo está. Um gate diz o que ainda não pode
passar. Os dez não foram inventados: cada um formaliza
uma linha **Pronto para…** que já existia em prosa no ciclo criativo, com o
critério de saída da etapa. Faltava alguém ler essas linhas e alguém recusar.
python3 scripts/game.py gate /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py gate /caminho/do/laboratorio/meu-jogo --gate deliver
Um gate tem nome do que você está pedindo, não da etapa que acabou: design,
test, prototype, close, implement, build, scale, evaluate,
conclude, deliver. A ordem é a do ciclo, e o ciclo tem retorno — reprovar em
scale devolve para build, o que é uso normal.
O projeto declara uma linha por critério, em README.md ou num qa.md,
devlog.md, release.md ou prd.md em qualquer subpasta de documentação
(o Rabisco Boom guarda o seu em docs/planning/; sources na saída diz o que foi lido).
Se a tabela declara o gate, o gate nomeia o gate que a tabela já declara. Linha no disco não é passagem concedida. Sem chave gate. Se o roteiro recusa que o silêncio seja aprovação, o gate nomeia o silêncio que o roteiro já recusa. Linha vazia no disco não é passagem. Sem chave silêncio. Se o roteiro recusa que must_meet seja dispensável, o gate nomeia a dispensa que o roteiro já recusa. Linha no disco não é passagem. Sem chave dispensa. Se o roteiro recusa que fora de escopo seja dispensa, o gate nomeia o escopo que o roteiro já recusa. Linha no disco não é passagem. Sem chave escopo:
| Gate | Critério | Estado | Evidência |
| --- | --- | --- | --- |
| `deliver` | `runbook` | `met` | Ana construiu do zero em 2026-09-02, log em /tmp/qa-07 |
| `deliver` | `foreign_machine` | `unmet` | só rodou na máquina de dev |
| `deliver` | `save_migration` | `out_of_scope` | jogo sem save — Alan, 2026-09-05 |
As três saídas de um gate são passar, cortar escopo e abandonar. A terceira é
a que costuma faltar: abandonar não é falha do gate, é uma das respostas dele — o
ciclo já dizia isso na etapa poc, e aqui vale para todas. Um processo que só
admite “passou” e “ainda não” empurra escopo morto para frente até ele custar
caro demais para matar. As três correspondem a Go / Recycle / Kill do método
stage-gate, e a correspondência foi encontrada depois, não copiada antes
(levantamento).
Um critério pergunta uma de duas coisas, e a diferença muda quem responde.
readiness pergunta se o trabalho está feito, e falhar devolve para a etapa
anterior. must_meet pergunta se isto ainda vale o que custa, e falhar mata o
escopo — não se resolve trabalhando mais. Três critérios são desse tipo
(close.decision, implement.worth_building, scale.worth_scaling) e next
pergunta o valor antes de pedir mais trabalho no mesmo gate.
Dispensa é estado de primeira classe, porque produção real dispensa requisito com
assinatura — mas exige motivo escrito, senão é o critério apagado da lista.
Quatro critérios não são dispensáveis, e não por escolha do harness: a prosa
da etapa não deixa terceira opção (licença desconhecida bloqueia a entrega;
prioridade não remove exigência explícita do usuário; teste com pessoa não se
registra onde houve só simulação; origem de referência é declarada ou a ausência
é explícita). Os três must_meet também recusam dispensa, por outro motivo: um
“No” num must-meet decide sozinho, sem compensação.
out_of_scope não é dispensa. Dispensar é deixar de cumprir o que incide, e
um jogo sem save não “dispensa” a migração de save. Contar os dois juntos
inflaria a conta de dispensas justamente onde ela deveria doer, então o estado é
separado, exige motivo escrito igual, e é recusado nos sete critérios que sempre
incidem.
Critério sem linha conta como pendente, nunca como cumprido: silêncio não é
aprovação. met sem nada escrito ao lado é recusado. Duas linhas discordantes
mantêm o estado mais fraco e o conflito fica listado. next propõe resolver o
critério pendente do primeiro gate declarado — um gate que o projeto não
mencionou não está sendo pedido.
O campo se chama held_by_declaration, não passed: ele diz que o projeto afirma
cumprir, não que alguém conferiu. Nenhum comando concede passagem (granted
é sempre false), e uma tabela bem formada e otimista sai daí intacta, como sai
da barra.
Origens
deliver.licensing é um dos quatro critérios que a prosa não deixa dispensar, e
até aqui o harness só lia a linha da tabela. Uma frase otimista fechava o gate.
origins percorre o disco:
python3 scripts/game.py origins /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py origins /caminho/do/laboratorio/meu-jogo --declare textures/hero.png --origin "foto própria" --author "Ana" --license CC0-1.0
Lista arquivos de mídia embarcados (som, imagem, fonte, vídeo, modelo) e cruza
com recibos: sources.json, licenses.json, CREDITS e sidecar
.credits.txt. JSON sem origem, autor e licença não declara. Sidecar sem
os três rótulos também não. Se o sidecar declara Consumidor:, o origins nomeia o consumidor que o sidecar já declara. Consumidor no disco não é licença válida. Sem chave consumer. Se o roteiro recusa que o sidecar sem rótulos declare, o origins nomeia os rótulos que o roteiro já recusa. Recibo no disco não é licença. Sem chave rótulos.
Nomeia a mídia que o recibo lista e o disco
perdeu. Nomear não devolve o arquivo. Entra em
textures/, fonts/, models/ e videos/ — pastas
que o scan de documentos ignora de propósito. Nomeia form e fields
(origem, autor, licença). --declare escreve o sidecar. Sem then.
Não valida a licença. Não
consulta titular, não interpreta texto jurídico e não distingue licença válida
de inválida. O que falta é recibo de origem; o que o recibo afirma continua
sendo alegação de quem escreveu. Recibo no disco não é licença válida.
granted e validated são sempre false. Se o projeto declara
deliver.licensing como met e o disco ainda tem arquivo sem recibo, a saída
marca contradicts_licensing. next aponta --declare — ou tirar o
arquivo do embarque — antes de seguir o restante do gate.
Ofício
Os gates perguntam se o trabalho está feito e se ainda vale o que custa. Isso não cobre paleta, janela de perdão, definição de percentil nem regra de parada de playtest. Esses checklists vêm do levantamento de critérios observáveis §7 — o único conjunto que a pesquisa chamou de “não precisa de autoridade externa”: conformidade com o que o próprio projeto declarou.
python3 scripts/game.py craft /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py craft /caminho/do/laboratorio/meu-jogo --gate scale
| Check | Estado | Evidência |
| --- | --- | --- |
| `palette` | `met` | paleta em docs/art-bible.md; cores de src/game/render.js listadas lá — Ana |
| `playtest_stop` | `unmet` | regra de parada ainda não escrita |
Nenhum checklist cita dígito. Um limiar aqui seria o harness afirmando, para
este jogo, o que ninguém verificou. Se a tabela declara saída de escopo, o craft nomeia a saída de escopo que a tabela já declara. Linha no disco não é ofício observado. Sem chave out_of_scope. Se a pesquisa recusa ser escada de acabamento, o craft nomeia a escada que a pesquisa já recusa. Pesquisa no disco não é ofício observado. Sem chave escada. Se a pesquisa recusa que o número sem definição seja critério, o craft nomeia a definição que a pesquisa já recusa. Pesquisa no disco não é ofício observado. Sem chave definição. observed e granted são sempre false.
next só levanta um checklist do gate que o projeto declarou — o mesmo
silêncio dos gates: quem não pediu a permissão não recebe a lista.
Starters
assets/starters/ guarda projetos de referência completos para o passo REUSE.
Hoje há um:
canvas-arcade — jogo em Canvas 2D com loop de passo fixo, RNG semeado,
hash de estado, abstração de entrada (teclado, ponteiro, gamepad, remapeável),
mixer com barramentos/ducking/limite de vozes/legendas, save versionado com
migração e gravação verificada, e renderizador com alto contraste e redução de
movimento. Expõe pause, reset, seed, observe, act, advance, capture
e dispose, e exercita as oito em testes headless (npm test) — com uma
ressalva: capture só na guarda de ausência de tela, porque toDataURL não
existe em headless. A chuva compacta o array vivo e reusa um poço de
entidades; evento, telegraph e o gerador da chuva também reusam. Também tem npm run budget para o orçamento das cenas title.attract e playing.run (mostra da porta e partida + draw num
canvas stub) — relata o reuso, sem teto — e npm run size para os bytes de dist/. O
README do starter declara em que degrau cada dimensão está, incluindo as que
ainda não subiram.
"Gravação verificada" é literal e menos do que atômica: escreve em chave de
estágio, relê, compara e grava na real. localStorage não tem substituição, então
a última escrita é comum. A receita de persistência
explica o que isso compra e o que não compra.
Um starter é referência executável: cd assets/starters/canvas-arcade && npm run serve abre o jogo antes de qualquer init. É por isso que os arquivos
carregam valores reais em vez de {{TOKEN}} — um token no <title> apareceria na
aba do navegador — e por isso que cada starter tem um starter.json dizendo quais
valores init troca e em quais arquivos. O escopo por arquivo é o que impede uma
troca de nome de alcançar um import ou um caminho relativo que só se parece com o
nome. Manifesto fora de sincronia com os arquivos é bloqueio em doctor e recusa
em init, antes de qualquer cópia.
Checagem e continuidade
Todo context já corre scan. Também existe sozinho:
python3 scripts/game.py scan /caminho/do/jogo --root /caminho/do/laboratorio
Nove áreas: Brief/PRD; GDD; MDA; arquitetura/TDD; design system / Art Bible; Devlog; QA/playtest; como executar; origem de código e assets. Um documento pode cobrir várias. O scanner só lê nomes, títulos e campos; não segue symlink nem escreve arquivo.
Se faltar base, foundation.audit.required pede ao agente avisar e documentar
sem esperar um segundo pedido. Restrição explícita na conversa continua valendo.
Exceção: ciclo fresco que já abre (audit.deferred) — o next pede jogar
primeiro; o context não manda preencher template. --event direction-approved
e --stage audit continuam pedindo a base. O scanner não executa a auditoria
(audit.executed: false). Se o README aponta o serve, o scan nomeia o serve que o README já aponta. Página no disco não é partida jogada. Sem chave serve. Se o sistema recusa que o scanner certifique tokens, o scan nomeia os tokens que o sistema já recusa. Documento no disco não é aprovação artística. Sem chave tokens. Se o roteiro recusa que o recibo presente seja licença válida, o scan nomeia a licença que o roteiro já recusa. Área no disco não é concessão. Sem chave licença. Se o roteiro recusa prescrever quantas pessoas, o scan nomeia as pessoas que o roteiro já recusa. Área no disco não é censo. Sem chave pessoas. Se o roteiro recusa que reconstruir documentos comprove intenções, o scan nomeia as intenções que o roteiro já recusa. Candidato no disco não é autoria. Sem chave intenções. Se o roteiro pede documentar sem consentimento, o context nomeia o audit que o roteiro já pede. Roteiro no disco não é base escrita. Sem chave audit. Se o roteiro recusa que a checagem seja daemon, o audit nomeia o daemon que o roteiro já recusa. Roteiro no disco não é interceptação. Sem chave daemon. Se o roteiro recusa que o local não percorrido seja inexistente, o scan nomeia a inexistência que o roteiro já recusa. Contagem no disco não é inventário. Sem chave inexistente. Se o mapa recusa que a cobertura desigual seja acidente, o issues[n] da coverage nomeia o acidente que o mapa já recusa. Recorte no disco não é falha. Sem chave acidente. Se a guia recusa que preencher linhas certifique o jogo, o non_current_documents[n] nomeia as linhas que a guia já recusa. Documento no disco não é o jogo. Sem chave linhas. Se o processo recusa que a etapa certifique o progresso, o context nomeia o progresso que o processo já recusa. Contexto no disco não é degrau. Sem chave progresso.
Eventos de conversa, interpretados pelo agente — o comando não concede aprovação:
python3 scripts/game.py context /caminho/do/jogo --focus visual --event direction-approved --root /caminho/do/laboratorio
python3 scripts/game.py context /caminho/do/jogo --focus mechanics --event resume --root /caminho/do/laboratorio
direction-approved sincroniza a base mínima no mesmo turno. resume localiza
fontes de continuidade; o agente resolve o próximo passo. O harness deixa
next_step: null e executed: false. Se o processo nega que documento pronto seja PoC, o context nomeia a PoC que o processo já nega. Fonte no disco não é jogo implementado. Sem chave process. Se o processo recusa que sources_found comprove a fila, o sources[n] do continuity nomeia a fila que o processo já recusa. Fonte no disco não é backlog. Sem chave fila.
Fonte encontrada não é tarefa validada — e fonte em rascunho não é nem passo. Num
projeto recém-criado, as fontes que scan lista são os campos de template
("Próxima ação: [...]"), todas com status: "draft"; next não propõe retomar
nenhuma delas, porque não há nada escrito para retomar. Ele volta a propor quando
alguma fonte deixa de ser rascunho.
Processo
context --event initialize prepara uma análise profunda e documental quando esse
for o pedido ou a convenção do workspace. O evento não cria um jogo nem executa a
auditoria. Inicialização.
delivery_review orienta a conferência do pedido, artefato, prova e continuidade.
O comando python3 scripts/game.py gauntlet <projeto> --objective "recorte definido" prepara um prompt de
continuidade; duração é opcional e preparação não inicia execução.
Continuidade · Revisão de entrega.
Pré-produção: Game Brief → GDD/MDA ↔ protótipo/PoC e playtest → PRD/TDD → vertical slice → produção/MVP → QA → release. Orientação de dependências, não esteira rígida. Um jogo pequeno pode reunir essas decisões em um documento.
Dez templates do ciclo: brief, mda, gdd, poc, prd, tdd, vertical-slice, mvp, qa,
release. Quatro complementos: art-bible, devlog, audit, aaa (checklist de piso;
o context expõe finish — núcleo / produto / promessa / mercado; slice, QA, create,
feel e audio carregam a guia; template aaa não certifica). Três de consolidação e
produção: game-design (documento único), production-plan, milestone.
O complemento agents gera a memória persistente em AGENTS.md a partir do disco: o comando que abre, o note, o playtest e o que ainda não foi plantado. Sem rascunhos do ciclo, não lista GDD. O playtest só lê. Sem os quatro não é achado. Nomear o leitor não observa. O start já escreve o mesmo arquivo; template agents e o next em agent_context.not_located não voltam ao molde que fingia brief. Se a memória recusa o adjetivo, o scan nomeia o AAA que a memória já recusa. Memória no disco não é acabamento. Sem chave agents.
python3 scripts/game.py context /caminho/do/jogo --focus content --stage gdd --root /caminho/do/laboratorio
python3 scripts/game.py template brief --project meu-jogo
python3 scripts/game.py template art-bible --project meu-jogo --output /tmp/meu-jogo-art.md
python3 scripts/game.py template aaa --project meu-jogo
python3 scripts/game.py context /caminho/do/jogo --stage aaa --root /caminho/do/laboratorio
Sem --output, template só imprime. Com ele, cria um rascunho novo e recusa
sobrescrita, inclusive de symlinks. Gerar template audit não executa auditoria. Gerar
template aaa não certifica acabamento nem publisher. Gerar template release não
concede autorização de publicação. Se o molde recusa publicar, o template nomeia a publicação que o molde já recusa. Molde no disco não é autorização. Sem chave publicar.
REUSE → ADAPT → CREATE. CREATE só entra com lacuna explícita.
O contrato JSON formaliza uma decisão nova;
check-plan valida a forma, não o mérito. Se o processo recusa garantir o mérito, o check-plan nomeia o mérito que o processo já recusa. Forma no disco não é adequação. Sem chave mérito. Se a guia recusa que a checagem seja validador semântico, o metadata_issues[n] nomeia o semântico que a guia já recusa. Parse no disco não é o jogo. Sem chave semântico. O arquivo de exemplo é um formulário em
branco, de propósito: rodá-lo no validador devolve os oito campos que faltam, que
é a lista do que preencher.
python3 scripts/game.py check-plan caminho/do/trabalho.json --root /caminho/do/laboratorio
Quatorze receitas: criar, mecânicas,
ciclo de vida, conteúdo,
visual, áudio, feel,
rede, arquitetura,
performance, acessibilidade,
persistência, release e
produção. --focus architecture ou --stage tdd carrega a
receita de arquitetura. Se a receita recusa que o harness infira dependências, o scan nomeia as dependências que a receita já recusa. Receita no disco não é decisão. Sem chave dependências. --focus feel e --focus audio carregam acabamento do verbo;
--focus production, --stage production-plan ou --stage milestone carregam a de
produç��o. A skill aplica quando a mudança pede; o CLI só seleciona referências.
Produção e acabamento
Produção trata marcos como critérios de evidência — first playable → vertical slice → alpha → beta → gold → live — com lentes de disciplina (design, arte, animação, áudio, feel, UX/acesso, técnica, conteúdo, localização, QA, plataforma), orçamentos medidos na plataforma alvo, pipeline de conteúdo e estabilidade. Os marcos são o calendário; a barra diz onde o jogo está em cada dimensão e os gates o que ainda não pode passar. “AAA” aqui é padrão de acabamento observável, não orçamento.
python3 scripts/game.py context /caminho/do/jogo --focus production --stage production-plan --root /caminho/do/laboratorio
python3 scripts/game.py template milestone --project meu-jogo --output /tmp/meu-jogo-alpha.md
O plano de produção entra em continuity.sources quando existe. Nenhum comando mede
orçamento, executa soak, promove marco ou certifica requisito de plataforma; a
passagem é declarada por pessoa com a prova ligada (record --kind milestone).
Exemplo: da trilha ao capítulo acabado.
Áudio
O starter declara os papéis do verbo, o orbe perdido, o fecho, a prática, a guarda e a cama (const SOUNDS) e já traz
design original em public/sfx/<papel>.wav. A cama entra em loop no
barramento de música; no over ela solta com fade. roles lê a declaração — inclusive o duckMs que a tabela já lista — e cruza com
arquivos em public/sfx (e equivalentes). Sem duck a chave some. Nomear não é mix ouvida. Se o tools/mix.* soma as vozes, o roles nomeia a soma. Soma no disco não é mix ouvida. Sem chave mix. Se o tools/design-sfx.* desloca a voz, o roles nomeia a voz que o sfx já desloca. Arquivo no disco não é mix ouvida. Sem chave sfx. Se o tools/wav.* lê o PCM, o roles nomeia o PCM que o wav já lê. Bytes no disco não são mix ouvida. Sem chave wav. Se a receita recusa que áudio AAA seja quantidade de arquivos, o roles nomeia a quantidade que a receita já recusa. Lista no disco não é mix. Sem chave quantidade. Papel vazio continua lacuna:
python3 scripts/game.py roles /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py roles /caminho/do/laboratorio/meu-jogo --fill
python3 scripts/game.py roles /caminho/do/laboratorio/meu-jogo --fill --apply --root /caminho/do/laboratorio
heard e approved são sempre false: arquivo presente não é mixagem ouvida.
next propõe audio.roles quando um papel está vazio. Se o processo recusa o reuso automático, o roles --fill nomeia o reuso que o processo já recusa. Arquivo no disco não é licença. Sem chave reuso. roles --fill sugere
um id do acervo ou a ficha do stem do starter; --apply copia o id do
acervo ou o stem do starter para public/sfx/<papel> com recibo —
e recoloca o WAV se o recibo já está e origem e licença casam.
sfx copy continua o caminho explícito. Copiar não é ouvir.
O starter carrega esse arquivo no mixer. npm run mix soma cama e vozes
de uma partida simulada com a mesma taxa da corrente; isso também não é
mix ouvida. Primeiro resultado da
busca não é mixagem. Silêncio deliberado é o papel fora da declaração, não
o slot sem arquivo.
Se o laboratório tiver shared/sfx com sons na raiz passada em --root:
python3 scripts/game.py sfx search passos --root /caminho/do/laboratorio
python3 scripts/game.py sfx copy ID --to /caminho/do/jogo/public/sfx --root /caminho/do/laboratorio
Sem esse acervo, sfx search não inventa id: o catálogo vem vazio e a
busca nomeia o stem do starter que casa com o termo. Se o tools/design-sfx.* desloca a voz, o sfx search nomeia o deslocamento que o sfx já oferece. Arquivo no disco não é mix ouvida. Sem chave sfx. sfx info
lê a chave e nomeia o stem que o recibo lista e o disco perdeu.
sfx copy e sfx export levam bytes e créditos
desse stem. Se o sidecar declara licença, o sfx copy nomeia os créditos que o copy já leva. Créditos no disco não são mix ouvida. Sem chave sidecar. sfx export nomeia o stem que o recibo lista
e o disco perdeu — exportar não inventa bytes.
sfx verify nomeia os stems sem cruzar o que não
existe e nomeia o stem que o recibo lista e o disco perdeu. Se o check cruza a integridade, o sfx verify nomeia a integridade que o check já cruza. Hash no disco não é mix ouvida. Sem chave sha256.
sfx summary lista todos. Se o tools/peak.* relata o pico do arquivo, o sfx summary nomeia o pico que o peak já relata. Relato no disco não é mix ouvida. Sem chave peak. sfx serve recusa — não
há o que ouvir no acervo.
Com sons, sfx serve abre a página de escuta; se shared/sfx/ui
faltar, o harness gera a lista. Tocar nessa página não é mix
ouvida no jogo. Arquivo no disco não é mix ouvido. Crescer o
acervo é arquivo local com recibo:
python3 scripts/game.py sfx import /caminho/do.wav --metadata /caminho/meta.json --root /caminho/do/laboratorio
python3 scripts/game.py sfx seed --root /caminho/do/laboratorio
python3 scripts/game.py sfx info passo-madeira-01 --root /caminho/do/laboratorio
python3 scripts/game.py sfx export passo-madeira-01 --to /caminho/do/jogo/public/audio --root /caminho/do/laboratorio
Se a receita recusa improvisar licença, o sfx import nomeia a improvisação que a receita já recusa. Importar no disco não é licença. Sem chave improvisar.
sfx import exige ffmpeg e um JSON com id, título, categoria, estilo,
tags, processamento e fontes (licença CC0 ou CC-BY). sfx seed lê
shared/sfx/selection.json com local_path já no disco. Sem seleção,
o seed recusa. sfx summary (também sem subcomando) lê o acervo, os atalhos
e os stems do starter em public/sfx — arquivo no disco não é mix ouvido.
sfx verify cruza bytes e fichas do acervo; sem acervo nomeia os
stems do starter e não cruza. Se o check cruza a integridade, o sfx verify nomeia a integridade que o check já cruza. Hash no disco não é mix ouvida. Sem chave sha256. Nomeia o stem que o recibo lista e
o disco perdeu. Nomeia o som que o catálogo lista e o disco
perdeu — não despeja errno. Não ouve. sfx info lê a ficha
do acervo ou a chave do stem do starter. O recibo que lista um
stem e o disco perdeu não é id desconhecido. Se o inspect já mediu
o pico, o sfx info nomeia o pico que o inspect já mede. Pico no
recibo não é mix ouvida. Arquivo no disco não é
mix ouvido. sfx export de um id do acervo copia
bytes, manifest.json e CREDITS.txt para uma pasta fora do acervo. Se o export recusa processamento, o sfx export nomeia o processamento que o export já recusa. Bytes no disco não são mix ouvida. Sem chave processamento.
De uma chave do starter, copia o WAV, o .credits.txt e anexa
sources.json. O recibo que lista um stem e o disco perdeu
não é id desconhecido; exportar não inventa bytes.
Copiar não é mix ouvido.
Importar e exportar não é mix ouvido. O primeiro ciclo já tem voz
no starter (public/sfx/<papel>.wav). shared/sfx é ADAPT, não
pré-requisito. Piso: gravação licenciada ou design contemporâneo.
8-bit, chiptune, jsfxr e Kenney arcade não são o padrão. Este
repositório não inclui o acervo shared/sfx do laboratório.
Feel
O starter nomeia perdão, graça, hitstop, buffer de guardar, punch de
câmera, rumble e o peso do passo no CONFIG. As janelas da chuva (prática, folga, fecho) moram
na mesa. O coil do dash marca o rumo no corpo. Se o laço declara
attractMove, o feel nomeia o corpo que a porta já desloca.
Pose no disco não é peso percebido. Sem chave attract.
Se o laço declara lookAhead, o feel nomeia a inclinação que o lookAhead já marca.
Lean no disco não é peso percebido. Sem chave lookAhead.
Se o tools/probe.* exercita as janelas de perdão, o feel nomeia o perdão que o probe já exercita.
Conta no disco não é peso percebido. Sem chave probe.
Se o laço senta a guarda, o feel nomeia o sit que a guarda já senta. Pose no disco não é peso percebido. Sem chave bank.
Se o laço emite o término, o feel nomeia o land que o dash já emite. Pose no disco não é peso percebido. Sem chave land.
Se a receita recusa que o autor sugerido seja quem jogou, o feel nomeia o autor que a receita já recusa. Recibo no disco não é sessão. Sem chave autor.
Se a receita recusa que o valor seja constante universal, o feel nomeia o universal que a receita já recusa. Número no disco não é lei. Sem chave universais.
Constante nomeada não é peso percebido. feel lê as
constantes — inclusive o pulso e o passo — essas janelas e o rumo, procura um record.json de observação no projeto e nomeia
then.play e then.note sem executar. Com last-run, nomeia then.seed
e then.invite — o mesmo endereço que play / guide. Sem comando de
abrir, a chave some. Sem last-run, seed e invite somem. Não tem
prompt. felt é sempre falso:
python3 scripts/game.py feel
python3 scripts/game.py feel /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py note --author "NOME" --note "o que o verbo sentiu"
python3 scripts/game.py note /caminho/do/laboratorio/meu-jogo --author "NOME" --note "o que o verbo sentiu"
# o mapa preenche --author com git ou o ambiente; NOME só se os dois faltarem
Sem caminho, o único jogo do laboratório basta; dois pedem o caminho.
next e playtest usam o mesmo resolvedor. Achar o jogo não é ter
sentido nem assistir.
note grava o recibo de observação em docs/playtest/<utc>/ com cenário e
papel por omissão. --from-run anexa docs/playtest/last-run.json (resumo
e, se houver, a curva) como candidato de medição e não fecha o achado.
Nomeia finding (os quatro no recibo), form e needed. Sem then.
Recibo sem os quatro não é achado. Os quatro no disco não observam. Se o disco tem last-run e o comando veio sem --from-run, o note nomeia o last-run que o disco já guarda. Sem o arquivo a frase some. Nomear não anexa.
Não joga. felt é sempre
false. next propõe feel.unobserved quando há constante e não há
recibo; o comando que ele aponta é o mesmo note que then.note —
com --from-run se last-run existir. O harness não atribui peso.
Alcance, save e orçamento
Três leituras do disco, no mesmo formato honesto: o que o código declara, não o que alguém observou.
python3 scripts/game.py access /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py save /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py budget /caminho/do/laboratorio/meu-jogo
access procura highContrast, reducedMotion, captions, remapeamento
(a página do starter lista as seis ações do teclado; toque e controle não entram),
uiScale, preset de uma mão (o ciclo nomeia IJKL + P/O quando o starter declara hand), assistência, velocidade da partida, tinta estável, região viva e pulso no aparelho. Na porta e no fim o canvas nomeia a lacuna do som que o painel já mostra. Se a casca declara :focus-visible, o access nomeia o foco que a receita já pede. Outline no disco não é sessão com o teclado. Sem chave focus. Se o tools/contrast.* amostra o stub, o access nomeia o contraste. Stub no disco não é sessão com o modo ativo. Sem chave contrast. Se o live anuncia o perigo à frente, o access nomeia o perigo que o live já anuncia. Texto no DOM não é sessão. Sem chave threat. Se o disco declara paintCommands, o access nomeia as teclas que a tabela já lista. Tabela no disco não é sessão. Sem chave commands. Se a porta lê a legenda que o mixer ainda guarda, o access nomeia a legenda que a porta já lê. Texto no disco não é sessão. Sem chave caption. Se a pesquisa recusa que acessibilidade seja gate de certificação, o access nomeia a certificação que a pesquisa já recusa. Opção no disco não é certificação. Sem chave certificação. verified é sempre false. Trocar no stub não é sessão observada. save procura
armazenamento, PROGRESS_SCHEMA/migrate e se o disco nomeia sessão volátil
(persistLine, title_volatile, title_unsaved) ou preferências
ilegíveis (settings_recovered, settings.broken); relata warned. Se o
canvas pinta settingsLine, o save nomeia a recuperação. A pausa não.
Texto no disco não é aba fechada. Sem chave recovery. Se o disco
escuta beforeunload, o save nomeia o fechamento que o disco já grava.
Gancho no disco não é aba fechada. Sem chave beforeunload. Se o disco verifica a gravação, o save nomeia a gravação que o storage já verifica. Escrita no disco não é aba fechada. Sem chave storage. Nomear
não é aba fechada. trusted é sempre false.
budget procura script budget/bench, tools/budget.* ou
record --kind budget. Se o tool declara title.attract, o
budget nomeia a porta que a receita já cronometra. Stub no
disco não é dispositivo. Sem chave door. Se o tools/size.*
declara sem teto, o budget nomeia os bytes que o size já relata.
Bytes no disco não são o quadro medido. Sem chave size. Se o
tools/budget.* relata o pior percentil, o budget nomeia o percentil que a receita já pede.
Relato no disco não é dispositivo. Sem chave percentile.
measured é sempre false. O starter declara
os três; um canvas sem opção de alcance recebe access.missing antes da
barra.
Arte, conteúdo e empacotar
Três dimensões que separam protótipo de produto, no mesmo formato: o que o disco declara, não o que alguém aprovou.
python3 scripts/game.py art /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py content /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py ship /caminho/do/laboratorio/meu-jogo
art procura const PALETTES, tokens.json, data/palettes.json, docs/art-bible.md vigente
e mesas de chuva (intervalTicks e fallSpeed em data/, tables/ ou content/).
Se o tools/new-look.* nasce o look, o art nomeia o look que o disco já nasce.
Ferramenta no disco não é comparação em movimento. Sem chave look.
Se o look recusa contraste, o art nomeia o contraste que o look já recusa. Alcance no disco não é comparação em movimento. Sem chave contrast.
Se o canvas declara drawTelegraph, o art nomeia o trilho que o telegraph já marca.
Marca no disco não é comparação em movimento. Sem chave telegraph.
Se o canvas declara drawVignette, o art nomeia a vinheta que o recorte já marca.
Recorte no disco não é comparação em movimento. Sem chave vignette.
Se o sistema recusa que a paleta compartilhada seja o contrato, o art nomeia a paleta que o sistema já recusa. Lista no disco não é contrato. Sem chave paleta.
Se a receita recusa que a mesa seja volume, o art nomeia o volume que a receita já recusa. Lista no disco não é comparação. Sem chave volume.
consistent é sempre false. Mesa no disco não é volume. Rascunho do init não conta. content
procura dado em data/, levels/ (e equivalentes) ou .ldtk/.tmx/.ink.
palettes.json e tokens.json não contam — o art lê esses manifestos.
Se o disco declara listMoods, o content nomeia o par. Nome no
disco não é volume. Sem chave moods.
Se o tools/new-table.* nasce a mesa, o content nomeia a mesa que o disco já nasce.
Ferramenta no disco não é volume. Sem chave table.
Se o disco declara migrateTable, o content nomeia a migração que as mesas já compartilham.
Arquivo no disco não é volume. Sem chave migrate.
enough é sempre false. ship procura script build/export/package/
release, docs/release.md vigente ou CI. Se dist/VERSION.json
existe, relata nome e versão. Se dist/ de um jogo web existe, relata
se a árvore jogável está completa e se o HEAD do artefato é o HEAD
atual. Nomeia a árvore que perdeu o src/ que o projeto já tem. Se o
tools/size.* declara sem teto, o ship nomeia o tamanho. Bytes no
disco não são outra máquina. Sem chave size. Se o tools/serve.*
nomeia a árvore exportada, o ship nomeia o banner que o serve já imprime.
Banner no disco não é outra máquina. Sem chave serve. Se o
tools/export.* declara o empacote, o ship nomeia o passo que o export já declara.
Empacotar no disco não é outra máquina. Sem chave export. Se o
tools/export.* recusa file://, o ship nomeia o file:// que o export já recusa. Recusar no disco não é outra máquina. Sem chave file. Se a receita recusa que a identidade seja outra máquina, o tree do ship nomeia a identidade que a receita já recusa. Árvore no disco não é entrega. Sem chave identidade. Se a receita recusa que o teste no editor demonstre o jogo exportado, o artifact do ship nomeia o editor que a receita já recusa. Manifesto no disco não é o jogo exportado. Sem chave editor. Nomear
não devolve o jogo. shipped e elsewhere são sempre false. Árvore
incompleta recebe ship.incomplete; artefato de outro commit recebe
ship.stale.
HTML estático sem manifesto já é o artefato; manifesto sem passo de
empacotar recebe ship.unpacked. O starter declara paleta em
data/palettes.json, escolhe o look por ?look= / settings.look
(dusk já é o segundo; contrast é alcance, não look), nasce o
próximo com look --from / --as, extrai a chuva
para data/spawn.json e data/dusk.json, escolhe o perfil por ?spawn=,
nasce a próxima com table --from / --as e session --spawn, e
empacota com npm run build — look no disco, ferramenta que desloca knobs
ou tokens e um export na máquina de quem construiu não são direção
consistente, escala nem entrega.
Playtest
Observação sem os quatro campos é impressão. playtest lê se o disco tem
problema, evidência, hipótese e medição — num documento ou no próprio
recibo:
python3 scripts/game.py playtest /caminho/do/laboratorio/meu-jogo
python3 scripts/game.py playtest /caminho/do/laboratorio/meu-jogo --invite
observed e outsider são sempre false. --invite escreve
docs/playtest/invite.md e aponta href (/?invite=1 ou, com seed
no disco, /?invite=1&seed=<n>, com chuva nomeada
&spawn=<mesa>, com look nomeado &look=<paleta>, e com
relógio nomeado e ≠ 1 &speed=<relógio>), onde a tabela de
comandos some; depois do fim a página do maker aponta o convite
desta partida se a seed ficou no recibo — copiar o endereço não
grava — e, no convite, mostra seed, pontos, eixos, a curva que o last-run já traçou e se o candidato foi simulado (nearest-orb vira simulada; played some; a faixa não leva a conta nem o relógio) e oferece
os quatro nomes para copiar ou gravar. Se o serve prende o bind, o convite nomeia o bind que o serve já prende. Bind no disco não é alguém de fora. Sem chave HOST.
Simulada não é alguém de fora. Depois do fim a página
rola até o painel. Rolar não é alguém de fora. Número na faixa não
preenche os quatro. O achado copiado e gravado leva a faixa
do last-run. Sem tally nem relógio. Markdown no disco não é
alguém de fora. Copiar não grava. O Copiar nomeia o
destino. Gravar já virava Achado no disco; o botão calava.
Nomear não é alguém de fora. Sem a área de
transferência, o Copiar baixa o markdown. Baixar não grava.
Gravado vira
docs/playtest/<utc>-achado.md se os quatro tiverem texto.
Se last-run existir, o serve anexa
docs/playtest/<utc>-achado.run.json. playtest relata esses
anexos em finding_attachments. Anexo não é sessão observada.
Página no disco, texto na área de transferência, markdown
baixado e markdown preenchido não são alguém de fora e não sobem pacing.
Esqueleto vazio não é achado. next propõe playtest.invite
depois do recibo de quem fez (e depois do segundo ciclo, se houver).
next propõe playtest.unstructured quando há recibo de observação
(ou um docs/qa.md vigente) e o achado ainda não tem forma. A
proposta aponta a página (finding_href / finding_open,
/?invite=1#finding ou a url do serve com o convite) e
note --field. O comando nomeia o endereço; o serve nu não
abre o painel. Sem o convite o âncora some. playtest só lê.
Nomeia finding_open (a url do serve com o convite, ou o mesmo
endereço sem serve), form (o esqueleto dos quatro nomes) e
fields. Sem then. Esqueleto no disco
não é achado. Se o arquivo docs/qa.md
existir, o recibo o nomeia em qa. Escrever não é sessão
observada. Se
docs/playtest/last-run.json existir, playtest o relata
como candidate e, se nomear a seed, como candidate_seed.
Se nomear a chuva, como candidate_spawn. Se nomear o look,
como candidate_look. ?seed=<n> abre essa partida e ignora
o hold; se o candidato nomeou a chuva ou o look, junta a mesa
e a paleta. O convite usa os mesmos eixos. O next aponta
note --from-run. A partida no serve grava esse arquivo;
npm run session grava a simulação.
Se o tools/session.* grava a simulação, o playtest nomeia a simulação.
Traço no disco não é alguém de fora. Sem chave session.
Se o tools/serve.* grava o recado, o playtest nomeia o recado que o serve já grava. Texto no disco não é alguém de fora. Sem chave note.
Nenhum dos dois é sessão observada. Se a pesquisa recusa que cinco playtesters sejam critério, o candidate_tally nomeia o cinco que a pesquisa já recusa. Conta no disco não é sessão observada. Sem chave cinco. Se a receita recusa que o aperto seja curva observada, o candidate_curve nomeia o aperto que a receita já recusa. Número no disco não é sessão. Sem chave aperto. Se o candidato
tiver curva, o note a anexa. Número no disco não é causa. A tabela de
ofício que descreve o formato não conta como
achado. O harness não assiste à sessão e não conta jogadores.
Sem esse acervo, o catálogo vem vazio. A origem e a licença continuam obrigatórias. Estilos, fornecedores excluídos e piso técnico são configuração do workspace, preservando a direção sonora de cada jogo. Este repositório não inclui os arquivos de som.
Verificar
Inspecione os scripts retornados por context. Escolha os validadores e a ordem
do próprio jogo.
python3 scripts/game.py verify /caminho/do/jogo --script test --output /tmp/jogo-qa-01 --root /caminho/do/laboratorio
python3 scripts/game.py verify /caminho/do/jogo --output /tmp/jogo-qa-02 --root /caminho/do/laboratorio --command python3 tools/verify.py
--command vai por último. Não há shell implícito. Cada execução cria uma pasta
inédita. Destino existente é recusado. Build verde não prova arte, feel, áudio, reinício, rede
nem que o jogo é divertido. experience_status continua not_assessed.
--script aceita scripts de package.json (npm/pnpm/yarn/bun conforme declaração
ou lockfile) e, em projetos com Cargo.toml, os alvos check, build e test.
Unity, Godot e Unreal não têm CLI padronizada; use --command com o executável real.
context só sabe dizer mentioned sobre as oito capacidades conhecidas — pause,
reset, seed, observe, act, advance, capture, dispose — porque lê arquivos sem
executá-los. --proves não promove nenhuma delas a verificada; o harness não
tem como saber se um comando exercita pause.
python3 scripts/game.py verify /caminho/do/jogo --script test --output /tmp/jogo-qa-03 --proves pause --proves reset --proves seed
O que ele acrescenta é uma alegação com autor, data, argv e log: claimed quando
os comandos passaram, unsupported quando falharam. Em vez de sumir na prosa, a
afirmação fica anexada a um recibo e pode ser contestada por quem ler. A declaração
é de quem executa, nunca do repositório: nenhum arquivo do projeto seleciona
capacidade, e claimed continua não sendo verified. Se o processo recusa que claimed seja verified, o verify nomeia a verificação que o processo já recusa. Alegação no disco não é cobertura. Sem chave verified.
Registrar evidência declarada
O que o verify não cobre — observação de pessoa em movimento, medição de orçamento,
decisão de marco — entra por record, em pasta inédita e ligado ao HEAD do projeto:
python3 scripts/game.py record /caminho/do/jogo --kind observation --author "Alan" --note "Completou a volta sem instrução." --field scenario=travessia --field role=human --attach /tmp/playtest.mp4 --output /tmp/jogo-obs-01 --root /caminho/do/laboratorio
python3 scripts/game.py record /caminho/do/jogo --kind budget --author "Alan" --note "Travessia completa" --field metric=frame_p99 --field value=14.2 --field unit=ms --field platform=tablet-ref --field tool=devtools --output /tmp/jogo-budget-01 --root /caminho/do/laboratorio
python3 scripts/game.py record /caminho/do/jogo --kind milestone --author "Alan" --note "Critérios com evidência ligada." --field milestone=alpha --field decision=declared --field declared_by=Alan --field role=human --output /tmp/jogo-alpha-gate --root /caminho/do/laboratorio
Campos obrigatórios por tipo: observation → scenario, role (human/agent);
budget → metric, value numérico, unit, platform, tool; milestone →
milestone, decision (declared/denied/deferred), declared_by, role. Anexos
entram por caminho e SHA-256. O recibo guarda o que foi declarado; não valida, não
mede e não aprova. Se o roteiro recusa medir os critérios, o record nomeia a medição que o roteiro já recusa. Recibo no disco não é observação. Sem chave mede. Se o roteiro recusa que o screenshot isolado comprove animação, o record nomeia a animação que o roteiro já recusa. Anexo no disco não é controle. Sem chave animação. Se o roteiro recusa que o HEAD substitua o julgamento, o version nomeia o julgamento que o roteiro já recusa. Identidade no disco não é avaliação. Sem chave julgamento. role=agent é avaliação do agente, não aprovação do usuário.
Três camadas
- IA: interpreta a intenção, consulta fontes e propõe a mudança. Não depende de um fornecedor.
- Harness: recorta o contexto, varre a base, valida a forma do contrato, monta projetos a partir de starters, corre os comandos escolhidos com recibo e registra evidência declarada.
- Memória: brief, decisões, plano de produção, estudos e evidência ficam nos locais canônicos de cada jogo.
O que este repositório não é
Não há engine comum, API universal de ações, avaliação automática de diversão, medição automática de performance ou publicação automática. “AAA” neste texto é piso de acabamento observável, não tier de publisher, orçamento nem certificado de mercado; o alvo honesto com IA é AA / Triple-I nesse piso. Os jogos do playground continuam com a própria engine; este harness não reivindica tê-los produzido.
A barra de acabamento descreve o que observar; ela não observa. Nenhum comando
promove um jogo a um degrau, e cumprir todos os critérios não garante que o jogo
interesse a alguém — acabamento é condição necessária, não suficiente. Se a barra recusa promover o degrau, o context nomeia a promoção que a barra já recusa. Guia no disco não é acabamento. Sem chave promove.
Os oito frameworks externos foram estudados em recortes; seus testes não foram executados. Os conceitos são adaptações desses estudos, não garantias universais. Mapa: sources.md.
Recibos brutos de execução e o acervo sonoro ficam no laboratório.
Testes
python3 -m unittest discover -s tests -v
cd assets/starters/canvas-arcade && npm test && npm run budget && npm run size
Histórico 0.1–0.9: adoção.
Comments