1. O ciclo de vida de um problema
- Nasce privado dentro de uma ORG (o prefixo do id:
apc#fibonacci). Ninguém de fora da org o vê — nem que ele existe. Prova em elaboração não vaza, por construção. - Validar é o portão de qualidade: seções obrigatórias no enunciado, testes pareados, soluções good aceitas. Não publica nada — é exatamente o modo p/ provas.
- Calibrar: o juiz roda as SUAS soluções good em cada máquina e mede o time limit por máquina E por linguagem. Você nunca mais chuta TL.
- Publicar o coloca no treino livre (a org precisa permitir público — trava que protege provas). Em contests de turma você usa problemas ainda privados.
O Painel acompanha cada fase — e avisa, com os commits exatos, quando um problema mudou e precisa recalibrar.
2. A CLI em 60 segundos
curl -fsSL https://moj.naquadah.com.br/moj -o ~/.local/bin/moj && chmod +x ~/.local/bin/moj moj login # o MESMO login desta página — sem git, sem chave SSH
Requisitos: bash, curl, jq. Um arquivo só, auto-contido (a lib comum vem embutida). Quem gere contests ou o parque de juízes também tem /moj-contest e /moj-judges no mesmo endereço.
moj update atualiza a própria CLI (o servidor evolui — um moj velho perde recursos em silêncio); moj version compara o seu build com o do servidor; e quando QUALQUER coisa parecer estranha, rode moj doctor — confere atualização, jq/curl, o checkout do mojtools, o bubblewrap e a sessão, e diz exatamente o que consertar.Dois jeitos de trabalhar, misture à vontade: moj edit abre um menu interativo com todos os campos (enunciado, exemplos, soluções, conf, publicar) — e o jeito arquivos-locais: o pacote é um diretório, você edita com as suas ferramentas e moj push envia.
moj test --run (julgamento local; também exige Linux com bubblewrap real), moj checker, moj interactive e moj fn (instalam os drivers de correção especial). Para esses:
git clone https://github.com/cd-moj/mojtools ~/mojtools export MOJTOOLS_DIR=~/mojtools # no seu .bashrcTODO O RESTO da CLI só precisa de bash, curl e jq.
3. Passo a passo: do zero ao publicado
Passo 1 — o esqueleto
moj new minha-org fibonacci # scaffold completo em ./fibonacci (nome = slug minúsculo [a-z0-9._-]) cd fibonacci && ls # docs/enunciado.md tests/input/ tests/output/ sols/good/ conf author tags
O primeiro argumento é a ORG (o id vira minha-org#fibonacci). Sua org pessoal (seu login) sempre existe e é sempre privada — perfeita p/ rascunhos.
moj mv fibonacci apc2026 — muda o id, nada mais. Na web: “+ Novo problema” na gestão.Passo 2 — o enunciado (docs/enunciado.md)
Markdown com matemática ($O(n \log n)$ vira MathML). Duas regras que o validador cobra e uma que ele não tem como cobrar:
- As seções
## Entradae## Saídasão obrigatórias (## Input/## Outputtambém valem). - O título é um campo (definido no
moj new/editou no formulário web) —% Títulona primeira linha é legado e é removido. - Declare as restrições EXPLICITAMENTE (
1 ≤ N ≤ 10⁵, cabe em 64 bits…). O aluno resolve as restrições, não a estória.
moj preview renderiza EXATAMENTE o HTML que o aluno verá (mesmo renderizador do site). Deixe aberto enquanto escreve.Imagens no enunciado (leia este bloco inteiro uma vez)
Primeiro, o que acontece por baixo: o aluno NUNCA recebe um arquivo de imagem separado. O renderizador (pandoc --embed-resources) converte toda imagem para base64 DENTRO do HTML — o enunciado que o aluno abre é uma página auto-contida. Seu único trabalho é fazer a imagem chegar ao renderizador. Há DOIS jeitos:
Jeito 1 — colar no editor web. Ctrl-V ou arraste a imagem no editor de enunciado: ela vira um data:URI DENTRO do texto (uma linha gigante ). Não existe arquivo separado — a imagem viaja colada no markdown, por qualquer via. Ótimo p/ um print rápido; feio de manter (o texto fica enorme).
Jeito 2 — arquivo em docs/ (o jeito da CLI). A figura vive como arquivo de verdade ao lado do enunciado.md e o texto referencia pelo nome:
cp ~/screenshots/grafo.png docs/ # no enunciado.md: 
moj pusha carrega (campodocs_files— mesma família de round-trip doscripts/);moj clonea traz de volta;moj upload(tar inteiro) sempre carregou tudo.- O Pré-visualizar (botão web e
moj preview) MOSTRA a imagem: a CLI manda as figuras locais junto, e o editor web pede ao servidor as do pacote. - Regras de nome: nomes simples (letras/dígitos/
._-), extensão png/jpg/jpeg/gif/svg/webp, até 2MB cada (figura de enunciado não precisa de mais; reduza screenshots).
docs/, AO LADO do enunciado.md; (2) o nome no  bate EXATAMENTE (maiúsculas incluídas); (3) você rodou moj push (ou upload) depois de adicionar o arquivo; (4) seu moj está atual — moj doctor diz (builds antigos não mandavam imagem no push/preview)..vimrc) e emacs (init.el):" vim — :MojImg ~/screenshots/grafo.png (copia p/ o dir do arquivo atual e insere a linha)
command! -nargs=1 -complete=file MojImg
\ execute '!cp' shellescape(<q-args>) shellescape(expand('%:p:h')) |
\ execute 'normal! o . ')'
;; emacs — M-x moj-img (idem: copia p/ o dir do buffer e insere )
(defun moj-img (file)
(interactive "fImagem: ")
(let ((dest (expand-file-name (file-name-nondirectory file) default-directory)))
(copy-file file dest t)
(insert (format "" (file-name-nondirectory file)))))
Passo 3 — exemplos visíveis (samples)
printf '5\n1 2 3 4 5\n' > tests/input/sample1 # a saída de CADA teste você vai gerar com a sua solução no passo 5
Cada tests/input/sampleX casa com tests/output/sampleX e aparece no enunciado, em ordem. Tudo que NÃO se chama sample* é teste oculto.
Explicando cada exemplo: docs/notes/<sample>.md
Sample bom merece um PORQUÊ. Escreva um arquivo Markdown por exemplo, com o nome do sample que ele explica — markdown normal, quebras de linha de verdade, listas, imagens (mesmas regras do enunciado). Você nunca encosta em JSON; a contabilidade é da plataforma.
mkdir -p docs/notes cat > docs/notes/sample1.md <<'EOF' Neste exemplo temos 4 grupos: - dois grupos de 2 pessoas - dois grupos de 3 pessoas  EOF
A nota renderiza logo abaixo do exemplo dela, no enunciado. No editor web é o campo “explicação” de cada exemplo; no moj edit, a opção [n]ota. O legado sample-notes.json ainda é lido em pacotes antigos, mas nunca mais é escrito — qualquer salvamento converte.
Passo 4 — testes ocultos
É aqui que a nota se decide. Cubra: o menor caso válido, o MAIOR caso (limites das restrições — é ele que derruba o O(n²)), valores que estouram int, empates/duplicatas e formatos degenerados. Um gerador vale mais que arquivos à mão:
for i in $(seq 1 8); do python3 gen.py $i > tests/input/t$i; done
tests/output/* COM a sua solução good (passo 5) — nunca à mão. Uma saída digitada com espaço sobrando reprova a turma inteira.Nota parcial estilo OBI: tests/score passo a passo
- Nomeie os testes ocultos POR GRUPO — é o glob que os amarra:
tests/input/g1_pequeno1 g1_pequeno2 # casos N ≤ 100 tests/input/g2_medio1 g2_medio2 # N ≤ 10^4 tests/input/g3_grande1 g3_grande2 # N ≤ 10^6
- Uma linha por grupo no
tests/score: globs, depois-, depois o peso. O separador ENTRE globs é vírgula+espaço (", ") — o parser exige, dos dois lados (API e juiz):sample* - 0 pontos g1_* - 20 pontos g2_* - 30 pontos g3_* - 50 pontos
- Regras: grupo é tudo-ou-nada (um teste falhou = grupo vale 0); o valor do problema é a SOMA dos pesos (não precisa dar 100); os samples entram com peso 0 p/ aparecerem no relatório sem valer nota; linha começando com
#é comentário; TODO teste tem de cair num grupo (a validação do upload confere). - O que o aluno vê embaixo do veredicto:
✓ Grupo 2 (30/30) · ✗ Grupo 3 (0/50)— feedback explícito por grupo; em contest modo OBI o placar também vira pontos.
moj test mostra a distribuição por grupo e acusa ali mesmo teste órfão (ZERARIA a submissão), linha inválida e grupo de peso>0 sem teste — conserte local, depois moj push.Passo 5 — soluções de referência (sols/)
A extensão define a linguagem (sol.c, sol.cpp, sol.py, Main.java…). Cada subdiretório tem um PAPEL na conferência:
good/— obrigatório (≥1): as corretas. Geram as saídas esperadas, a validação exige que sejam aceitas, e o juiz as cronometra p/ calibrar o TL.wrong/— erradas de propósito: prova que seus testes PEGAM o erro clássico.slow/— lentas de propósito (o O(n²) que você quer reprovar): prova que o TL de fato as derruba.pass/— devem passar raspando: prova que o TL não ficou apertado demais.upcoming/— rascunhos; fora da conferência.
# gere as saídas com a good (exemplo em C): gcc -O2 sols/good/sol.c -o /tmp/sol for f in tests/input/*; do /tmp/sol < "$f" > "tests/output/$(basename "$f")"; done
Passo 6 — conf: limites e opções
MEMLIMITMB— limite de memória (a JVM/-Xmx se dimensiona por ele também).calibrafactor— a folga do aluno sobre o tempo medido da sua good. Mais generoso = mais justo com abordagens alternativas.languages— restringe as linguagens de submissão (vazio = todas as padrão):moj languages ./dir c,cpp,pye depoispush(ouupload— viaja dos dois jeitos).
MEMLIMITMB e a folga — ou forneça uma good em Python e deixe a calibração por linguagem fazer a justiça por você.Opcional — correção especial (checker próprio / interativo)
Quando o diff puro não basta: várias respostas válidas (qualquer ordem topológica, qualquer caminho máximo), tolerância numérica, ou problemas interativos. Os caminhos abaixo COMPÕEM (preenchem SLOTS independentes do scripts/): função + checker especial é combinação normal — moj fn dir && moj checker dir checker.cpp. O único que não mistura é o interativo (dono da execução por linguagem). No editor web, o “+ Adicionar template” também é ADITIVO.
A) testlib (recomendado) — escreva um checker.cpp testlib padrão e instale o driver normalizado:
moj checker ./fibonacci checker.cpp # instala scripts/ (stub canônico + seu checker) moj interactive ./meu-jogo arbitro.cpp # idem p/ problema INTERATIVO (árbitro via FIFOs)
B) na mão — um scripts/compare.sh com este CONTRATO:
#!/bin/bash
# $1 = saída do ALUNO $2 = saída ESPERADA $3 = ENTRADA do teste
# exit 4 = Accepted · 5 = Accepted (Presentation Error) · 6 = Wrong Answer
awk -v a="$1" -v b="$2" 'BEGIN{ ... }' && exit 4 || exit 6
C) submissão de FUNÇÃO (o aluno envia SÓ a função) — o driver do autor (o seu main) vive em scripts/<lang>/compile.sh; instale os templates das 5 linguagens (com a SENTINELA anti-IO embutida — pega função que lê a entrada escondido) e edite as zonas EDIT-ME:
moj fn ./meu-problema --langs c,py # ou o template "Submissão de função" no editor web moj languages ./meu-problema c,py # OBRIGATÓRIO: sem whitelist, trocar de linguagem burla o driver # depois: EDIT-ME nos drivers · sols/good = SÓ a função · todo tests/input termina com 424242
Guia completo (anatomia por linguagem, boas práticas anti-IO, erros comuns): submissao-de-funcao.md nas docs do mojtools.
chmod +x é OBRIGATÓRIO em todo scripts/*.sh (a jaula os executa direto — sem o bit, TUDO vira Compilation Error). E mexer em scripts/ muda o tl-checksum: o Painel vai pedir recalibração — está certo, aceite.Guias: checker-testlib.md, correcao-especial.md, problema-interativo.md e submissao-de-funcao.md nas docs do mojtools (banimento de função incluso).
Passo 7 — teste local, depois envie
moj preview # o HTML do aluno no navegador moj test # pré-voo: estrutura, pareamento, seções moj test --run # JULGA de verdade na sua máquina (Linux + bwrap) moj push # envia — cria/atualiza no servidor (round-trip completo)
Todo push/salvar é um commit git no servidor: a aba histórico do editor (e moj log/moj restore) permite inspecionar e voltar qualquer versão.
Passo 8 — validar, calibrar, publicar
moj validate minha-org#fibonacci # portão + (RE)ENFILEIRA calibração — CONTINUA PRIVADO moj check minha-org#fibonacci # QA read-only: checks, TL por juiz, good sem TL moj publish minha-org#fibonacci # público no treino (a ORG precisa permitir)
validate é o modo prova: conferência completa, exposição zero — e cada chamada RE-ENFILEIRA calibração, então p/ só OLHAR use check/status (read-only). check mostra o veredicto do juiz sobre as SUAS soluções e os TLs calibrados. publish exige a trava de público da org aberta (aba Orgs) — a org pessoal implícita nunca publica.
validate, o resumo mostra “⏳ calibrado: aguardando (calibração na fila)” — isso é normal, não é falha. Rode moj check de novo em ~30 s e vira “calibrado: sim” com os TLs por linguagem. Só fora dessa janela é que “⚠ solução good SEM TL — falhou em TODAS as máquinas de juiz” indica problema de verdade.4. Receitas de professor
Uma lista/prova do zero, em lote
moj mkdir apc2026 # org da disciplina (privada por padrão) for p in soma-vetores busca-bin pilha-min; do moj new apc2026 $p # três esqueletos done # ... edite cada um (moj edit ./$p ou seus arquivos) ... for p in soma-vetores busca-bin pilha-min; do (cd $p && moj push) && moj validate "apc2026#$p" done moj board # a saúde de tudo num tiro só
Os problemas ficam privados até você decidir o contrário; o contest da turma pode usá-los ainda privados. No dia da prova, nada nunca esteve exposto.
Reusar entre semestres
moj clone apc2026#fibonacci fib-2027 # o pacote inteiro, scripts incluídos # ajuste os testes / a estória / os limites ... (cd fib-2027 && moj push) # o histórico git guarda cada versão moj log apc2026#fibonacci -n 5 # e se algo der errado: moj restore apc2026#fibonacci <sha> # volta como um commit NOVO (nada se perde)
Delegar a monitores; organizar o acervo
moj share apc2026 monitor-joao # membro da org: edita TUDO da org moj collection create "APC 2026.1" # coleção = TAG de agrupamento (m:n, cross-org) moj collection add apc2026#fibonacci "APC 2026.1"
ORG = acesso (uma por problema, o prefixo do id). COLEÇÃO = rótulo de navegação/curadoria, várias por problema. O aluno filtra o treino por coleção.
De olho em tudo
moj board # seus problemas: validado? calibrado? precisa revisar? moj check apc2026#fibonacci # o porquê de cada estado, juiz por juiz moj calibrate --all-stale # recalibra TUDO que o painel marcou (lote seguro)
Inspecionar a calibração; testar no juiz REAL
moj calib apc2026#fibonacci # a calibração POR EXTENSO: cada juiz, cada solução,
# cada teste {nome, código, tempo, tl}
moj --json calib apc2026#fibonacci # o mesmo, em JSON — p/ os seus scripts
moj calib-report apc2026#fibonacci # lista os report.html disponíveis (e baixa com --host/--sol)
moj calibrate apc2026#fibonacci --all-judges # calibra em TODOS os juízes online (como na web;
# --hosts j1,j2 escolhe, --per-cpu pega 1 por modelo,
# moj calibrate --judges lista o parque)
moj testrun apc2026#fibonacci sol.c # roda UMA solução avulsa NO JUIZ (jaula e TL reais),
# fora do histórico — devolve o vetor por teste
moj testrun ... --report out.html # e ainda baixa o report.html do julgamento
testrun exige permissão de EDIÇÃO no problema (roda contra os testes ocultos). Se o conf do pacote tem TLOVERRIDE, o TL exibido/julgado é o do override — a calibração continua rodando como referência.
5. ⚡ Comandos rápidos
| Comando | O que faz |
|---|---|
| moj login · whoami | sessão (mostra se você pode criar problemas) |
| moj new <org> <prob> | scaffold completo do pacote em ./prob |
| moj clone <id> [dir] | baixa o pacote INTEIRO (testes, soluções, scripts) |
| moj edit <id|dir> | menu interativo com todos os campos |
| moj preview [dir] | o HTML exato do aluno, no navegador |
| moj test [dir] [--run] | pré-voo local, incl. grupos do tests/score; --run julga de verdade (bwrap) |
| moj push [dir] | envia (cria/atualiza; round-trip completo) |
| moj upload/download <id> | pacote inteiro via tarball (download --sha = versão antiga) |
| moj validate <id> | portão + calibração — CONTINUA privado (modo prova) |
| moj check <id> | QA: checks, TLs por juiz, o porquê da recalibração |
| moj calib <id> | a calibração POR EXTENSO: por juiz/solução/teste (--json p/ scripts) |
| moj calib-report <id> | report.html de uma solução calibrada (sem args = lista) |
| moj testrun <id|dir> <sol> | roda UMA solução avulsa no juiz REAL, fora do histórico (permissão de edição) |
| moj publish <id> · public <id> on|off | público no treino (org precisa permitir) / alternar |
| moj ls · board · info <id> | listas · painel de saúde · tudo de um problema |
| moj log <id> · restore <id> <sha> | histórico git · volta como commit NOVO |
| moj mv <id> <org> | move rascunho p/ outra org (muda o id) |
| moj mkdir <org> · share <org> <login> | cria org · adiciona membro (quem edita) |
| moj collection … | cria/marca/navega coleções (tags de agrupamento) |
| moj calibrate <id> | --all-stale | enfileira calibração / recalibra tudo que precisa |
| moj checker · interactive | instala checker testlib / problema interativo (correção especial) |
| moj doctor · version · update | diagnóstico do ambiente · build local×servidor · auto-atualiza a CLI |
6. 💡 Dicas & armadilhas (aprendidas na prática)
- Exemplos vivem em arquivos, nunca no texto do enunciado — o renderizador os injeta e mantém tudo consistente.
- Saídas esperadas são SEMPRE geradas por uma good. Edição à mão é WA futuro p/ a turma inteira.
- Mudou testes, soluções good, conf ou scripts de correção? O TL ficou velho — o Painel marca (com os commits causadores) e
moj calibrateresolve. Mudança SÓ de enunciado não recalibra. - Justiça por linguagem vem de good por linguagem. Se você permite Python, entregue uma good em Python.
- A privacidade é estrutural: problema privado não aparece em lugar nenhum (nem p/ admin global de fora da org). A trava de público é da ORG — fechá-la despublica tudo em cascata.
- Precisa de checker próprio (várias respostas válidas) ou problema interativo? Os dois são cidadãos de primeira classe:
moj checker/moj interactiveinstalam os drivers normalizados. moj test --runexige Linux com bubblewrap real; sem ele, a validação simplesmente adia a execução p/ o juiz — não é bug.- CLI esquisita (comando que “não faz nada”, recurso que falta)? Rode
moj doctorprimeiro — nove em dez vezes émojdesatualizado;moj updateresolve numa linha.
7. Referências
Formato do pacote (fonte única) · Documentação completa · API · ← voltar à Gestão de Problemas