MOJ MOJ Melhor Online Judge BETA

Criando problemas no MOJ — um guia para professores que vivem no terminal

Tudo neste guia também existe no editor web. Se você prefere o terminal, a CLI moj transforma a autoria de problemas em arquivos no seu $EDITOR. Os comandos entram em scripts e loops. O git guarda cada versão. Este é o roteiro de professor para professor.

1. O ciclo de vida de um problema

rascunho privado → validar → calibrar → publicar
  • 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 e jq. A CLI é um arquivo só, com a biblioteca comum embutida. Se você gere contests ou o parque de juízes, baixe /moj-contest e /moj-judges no mesmo endereço.

Mantenha a CLI atualizada. O comando moj update atualiza a própria CLI. O comando moj version compara o seu build com o do servidor. Quando algo parecer errado, rode moj doctor. Ele confere a atualização, o jq e o curl, o checkout do mojtools, o bubblewrap e a sessão, e diz o que consertar. A CLI também avisa sozinha quando está desatualizada.

Há dois jeitos de trabalhar. Misture os dois como quiser. O comando moj edit abre um menu interativo com todos os campos: enunciado, exemplos, soluções, conf e publicar. No jeito de arquivos locais, o pacote é um diretório: edite com as suas ferramentas e envie com moj push.

Dentro da pasta de um problema, ou de uma subpasta dela, o id é opcional. A CLI lê o id do arquivo .moj-id. Exemplo: moj check, moj publish e moj log não precisam do id ali. Um nome sem a org também funciona: moj publish fibonacci usa a org da pasta. Exceção: moj rm e moj mv sempre pedem o id completo.
Atenção: quatro comandos rodam o maquinário de julgamento na SUA máquina e precisam do checkout do mojtools: 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 comandos:
git clone https://github.com/cd-moj/mojtools ~/mojtools
export MOJTOOLS_DIR=~/mojtools     # no seu .bashrc
Todos os outros comandos precisam só 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.

Rascunhe na org pessoal e mova depois com 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 ## Entrada e ## Saída são obrigatórias (## Input/## Output também valem).
  • O título é um campo (definido no moj new/edit ou no formulário web) — % Título na 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.
NUNCA cole exemplos no texto. O renderizador os injeta sozinho a partir dos arquivos de sample (próximo passo) — exemplo colado envelhece e diverge.
moj preview renderiza EXATAMENTE o HTML que o aluno verá (mesmo renderizador do site). Deixe aberto enquanto escreve.

Formatação e fórmulas, com exemplos para copiar (frações, somatórios, casos, matrizes, o que evitar e como cada uma sai no PDF da prova): Escrevendo o enunciado.

Enunciado em mais de um idioma (opcional)

O português é o texto principal e é obrigatório. Para oferecer o problema em inglês ou espanhol, escreva a tradução ao lado, com o código do idioma no nome do arquivo:

docs/enunciado.md          # português (obrigatório)
docs/enunciado.en.md       # inglês   — mesmas seções: ## Input / ## Output
docs/enunciado.es.md       # espanhol — ## Entrada / ## Salida
docs/notes/sample1.en.md   # explicação do exemplo 1 em inglês (sem ela: a PT)
docs/solucao.en.md         # editorial em inglês (entra no documento de editorial em EN)
moj title . --lang en "Sum of Two"   # o título da tradução (fica no .moj-id, campo titles)
  • Os exemplos vêm dos testes e aparecem em todos os idiomas, com os rótulos do idioma (Exemplos / Entrada / Saída / Explicação; Examples / Input / Output / Explanation).
  • Exemplo sem explicação traduzida mostra a explicação em português. O validador avisa (nota-sem-traducao) e não bloqueia.
  • O validador confere cada tradução como o português: ela tem de renderizar e tem de ter as seções de entrada e de saída.
  • No editor web: os chips PT · EN · ES acima do enunciado. Cada idioma tem título, editor e campos de explicação próprios. O botão Pré-visualizar renderiza o idioma ativo. O botão ✂ Separar em seções funciona em todo idioma: os cabeçalhos saem no idioma (## Input / ## Output, ## Entrada / ## Salida). A aba Resolução tem os mesmos chips, com + EN e + ES: dá para escrever o editorial em outro idioma sem traduzir o enunciado. Ela tem um botão Pré-visualizar próprio.
  • moj preview --lang en renderiza a tradução. moj clone e moj push levam todos os idiomas. Remover o docs/enunciado.en.md e dar push remove a tradução no servidor.
  • No treino livre a página do problema mostra um chip por idioma. Em um contest, o admin ou o juiz-chefe escolhe os idiomas que a sanfona oferece.

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 ![](data:image/png;base64,…)). 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:
![O grafo do exemplo 1](grafo.png)
  • moj push a carrega (campo docs_files — mesma família de round-trip do scripts/); moj clone a 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).
Imagem não aparece? Checklist na ordem: (1) o arquivo está em 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).
Macro de editor p/ copiar a imagem p/ o lugar e inserir o markdown num golpe — vim (no .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![](' . fnamemodify(<q-args>, ':t') . ')'
;; emacs — M-x moj-img  (idem: copia p/ o dir do buffer e insere ![](arquivo))
(defun moj-img (file)
  (interactive "fImagem: ")
  (let ((dest (expand-file-name (file-name-nondirectory file) default-directory)))
    (copy-file file dest t)
    (insert (format "![](%s)" (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.

2–3 samples PEQUENOS e didáticos: um caso típico, uma borda que o aluno deva notar. Grande e exaustivo é papel dos ocultos.

Problema sem exemplo: SAMPLE=no

Em alguns problemas, entrada e saída de exemplo não fazem sentido para o aluno: submissão de função (a entrada é o formato interno do driver), problema interativo (a entrada é o cenário secreto do árbitro), linguagem própria e outros. Nesses problemas:

  1. Não crie tests/input/sample*.
  2. Ponha a linha SAMPLE=no no conf. No editor web, é a opção “este problema não tem exemplos” da aba Limites. Na CLI: moj edit → 8 (conf) → 6. O moj interactive já grava a linha.
  3. Explique o exemplo no texto do enunciado, numa seção ## Exemplo: uma figura, uma chamada da função e o que ela devolve, a transcrição da conversa com o árbitro.

Com SAMPLE=no, o enunciado não mostra a caixa de exemplos e nenhum exemplo pode ser baixado, nem de arquivos sample* que existam (eles continuam corrigindo, como qualquer teste). A validação exige uma das duas coisas: pelo menos um sample*, ou SAMPLE=no. Teste oculto nunca aparece como exemplo. A linha SAMPLE não pede recalibração.

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

![Como as mesas ficam ocupadas](mesas.png)
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
Gere TODO 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.

Validador de entrada: prove que os testes seguem o enunciado

Um teste fora dos limites do enunciado é um bug silencioso: a saída esperada pode ser lixo (caso real: n=1296 num problema de N ≤ 1000, e a solução de referência tinha um vetor de 1001). Escreva scripts/validator.cpp com a testlib (o padrão do Polygon) e rode na sua máquina:

moj validator . validator.cpp    # instala em scripts/ e roda sobre tests/input/*
moj validator                    # (na pasta do problema) roda de novo depois de mexer nos testes

Cada entrada ganha ✓ ou a mensagem da testlib (qual linha, qual valor, qual intervalo). A calibração completa roda o mesmo validador no juiz: o resultado aparece como Entradas no editor, no Painel e no moj check, e entrada inválida deixa o problema não pronto. O editor web tem o template “Validador de entrada (testlib)”. Mexer no validador não pede recalibração. Guia: mojtools/docs/validador-testlib.md.

Nota parcial estilo OBI: tests/score passo a passo

  1. 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
  2. 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
  3. 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).
  4. 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.
Confira ANTES de enviar: 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, o juiz as cronometra p/ calibrar o TL, e cada uma tem de ser aceita em todos os testes dentro do tempo-limite que o juiz cobra (com o TLOVERRIDE).
  • wrong/ — erradas de propósito: têm de ser REPROVADAS, de preferência por WA. Provam que seus testes PEGAM o erro clássico.
  • slow/ — lentas de propósito (o O(n²) que você quer reprovar): têm de tomar TLE em pelo menos 1 teste e ser aceitas nos outros. Provam que o TL de fato as derruba.
  • pass/ — devem passar raspando: aceitas em todos os testes dentro do TL. Provam que o TL não ficou apertado demais.
  • upcoming/ — rascunhos; não rodam.

A calibração (botão Calibrar, moj calibrate, publicar) roda todas as soluções num juiz e confere cada uma contra a categoria dela. O editor, o Painel, o moj calib e o moj check mostram o resultado por solução: ✓ conforme; ≈ conforme, outro motivo (uma wrong reprovada só por TLE, sem WA — confira se era esse o erro que você queria pegar; uma slow que também deu WA); ✗ divergente (good ou pass reprovada ou mais lenta que o TL, slow sem TLE, wrong aceita); ✗ não rodou (erro de compilação, erro do juiz, linguagem indisponível).

Regra de ouro: uma good POR LINGUAGEM que você quer permitir. O TL é medido por linguagem — linguagem sem good própria herda um limite que pode ser injusto (o tempo da sua solução C não serve p/ Python).
# 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,py e depois push (ou upload — viaja dos dois jeitos).
  • CPUNEEDED — problema PARALELO (OpenMP/MPI/threads): CPUs que CADA teste precisa (1..64). O juiz junta esse nº de CPUs por teste, mede o tempo-limite assim (um teste por vez) e entrega o número à jaula como MOJ_TEST_CPUS/OMP_NUM_THREADS — um run.sh de MPI faz mpirun -np "$MOJ_TEST_CPUS", nunca um -np fixo. SAMENUMA=y pede o mesmo nó NUMA. Web: aba Limites › Problemas paralelos; CLI: moj edit → 8 → 7/8. Templates paralelo-openmp / paralelo-mpi. Guia: problema-paralelo.md nas docs do mojtools. Não confundir com ALLOWPARALLELTEST/MAXPARALLELTESTS (vários TESTES ao mesmo tempo, otimização do juiz que nunca muda o tempo-limite).
Entrada pesada em Python? Dê espaço: suba 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

O moj fn também grava a linha FUNCTION_LANGS=c,py no conf. Essa linha diz ao editor do aluno (treino e contest com o módulo de esqueleto de código) que nessas linguagens ele escreve SÓ a função: o editor abre vazio, sem o main do esqueleto. Se você tirar o driver de uma linguagem, tire a linguagem da linha também. Pela CLI: moj edit → 8 (conf) → 10. No editor web: aba Limites, campo Submissão de função. A validação reprova linguagem listada sem scripts/<lang>/compile.sh.

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.

Alguém mudou o problema na web? moj pull

cd fibonacci
moj pull           # traz para a pasta o que mudou no servidor desde o seu último clone/pull/push
moj pull --force   # a pasta tem mudanças suas: guarda a pasta em fibonacci.local-AAAAMMDD-HHMMSS e traz a versão
  1. moj pull troca só os arquivos do pacote. Os outros arquivos da pasta ficam (por exemplo, um gerador.py). Um arquivo que o servidor removeu some da pasta também.
  2. Se a pasta tem mudanças que você não enviou, o moj pull recusa e lista os arquivos. Envie com moj push, ou use moj pull --force.
  3. O moj pull --force copia a pasta inteira antes de trocar os arquivos. Compare a cópia com diff -r e aplique as suas mudanças de novo.
Atenção: o moj push recusa quando o problema mudou no servidor depois do seu último clone, pull ou push. O editor web ou outro autor salvou o problema. Nada é enviado. A mensagem diz quem mudou e quando. Rode moj pull para trazer a versão nova, ou rode moj push --overwrite para enviar a sua versão por cima. O editor web tem a mesma trava: ele mostra quem mudou o problema, com os botões Recarregar e Salvar por cima. O moj upload tem a mesma trava e o mesmo --overwrite.

Uma pasta clonada antes de o moj pull existir não tem linha de base. O primeiro moj pull compara a pasta com o servidor. Se forem iguais, ele grava a linha de base. Se forem diferentes, ele recusa e sugere --force.

Passo 8 — validar, calibrar, publicar

moj validate minha-org#fibonacci   # confere o PACOTE + (RE)ENFILEIRA calibração — CONTINUA PRIVADO
moj check    minha-org#fibonacci   # QA read-only: pacote, TL por juiz, soluções, entradas, PRONTO?
moj publish  minha-org#fibonacci   # público no treino (a ORG precisa permitir)

validate é o modo prova: exposição zero. Ele confere o PACOTE (estático: enunciado, exemplos, testes emparelhados; não roda solução) e RE-ENFILEIRA a calibração, que roda as soluções no juiz. P/ só OLHAR use check/status (read-only). check mostra o pacote, os TLs calibrados, cada solução contra a categoria dela, o validador de entrada e a linha final: “pronto: SIM” ou a lista do que falta. publish exige a trava de público da org aberta (aba Orgs) — a org pessoal implícita nunca publica. Se o problema ainda não está pronto, o publish mostra as pendências e pede confirmação (--yes mostra e segue).

A calibração é ASSÍNCRONA: o juiz mede o time limit uns 30–50 s depois. Logo após o 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"

Duas pessoas no mesmo problema: cada uma roda moj pull antes de editar e moj push depois. Se a outra pessoa enviou antes, o seu push recusa e diz quem foi. Rode moj pull e envie de novo.

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.

Revisar em equipe: issues

moj issues new apc2026#fibonacci "Teste 7 fora do limite" -m "n=1296, o enunciado diz N <= 1000"
moj issues apc2026#fibonacci            # as abertas (--all inclui as fechadas)
moj issues comment apc2026#fibonacci 1 -m "conferi: é o gerador"
moj issues close apc2026#fibonacci 1 -m "t7 regerado"   # dentro da pasta, o id é opcional

Issue é uma nota de que o problema precisa de conserto: um teste suspeito, uma solução que diverge, uma frase ambígua. Todos os membros da org veem, comentam e fecham — na CLI ou na aba 🐞 Issues do editor web. Enquanto houver issue aberta, o problema não está pronto (o moj check e o Painel dizem isso). Issues não fazem parte do pacote: não viajam no moj push/clone. Texto longo: -F arquivo.txt (-F - lê a entrada padrão).

De olho em tudo

moj board                    # seus problemas: pacote? calibrado? soluções? PRONTO?
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.

No editor web, é a sub-aba 🧪 testar no juiz de Soluções & Correção. Escolha um arquivo ou cole o código; a linguagem vem da extensão do arquivo. O botão 🧪 de cada solução testa a versão que está no editor, sem salvar. As execuções ficam na lista com o veredicto, os testes e o report.html.

5. ⚡ Comandos rápidos

ComandoO que faz
moj login · whoamisessão (mostra se você pode criar problemas)
[<id>]opcional dentro da pasta do problema (o id vem do .moj-id)
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] [--overwrite]envia (cria/atualiza; round-trip completo). Recusa se o servidor mudou depois do seu último clone/pull/push; --overwrite envia por cima
moj pull [dir] [--force]traz para a pasta as mudanças do servidor (web, outro autor); --force guarda a sua pasta numa cópia
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: pacote, TLs por juiz, soluções, entradas, pronto ou o que falta, o porquê da recalibração
moj issues [<id>] · new · comment · close · reopenas issues do problema (revisão da banca); issue aberta = não está pronto
moj calib [<id>]a calibração POR EXTENSO: por juiz/solução/teste, cada solução com esperado × obtido (--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). Na web: Soluções & Correção › 🧪 testar no juiz
moj publish [<id>] · public [<id>] on|offpú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-staleenfileira calibração / recalibra tudo que precisa
moj validator [<dir>] [validator.cpp]instala e roda o validador de entrada testlib sobre tests/input/*
moj checker · interactiveinstala checker testlib / problema interativo (correção especial)
moj doctor · version · updatediagnó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 calibrate resolve. 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 interactive instalam os drivers normalizados.
  • moj test --run exige 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 doctor primeiro — nove em dez vezes é moj desatualizado; moj update resolve numa linha.

7. Referências

Formato do pacote (fonte única) · Escrevendo o enunciado (Markdown e fórmulas) · Documentação completa · API · ← voltar à Gestão de Problemas