Skip to content

Latest commit

 

History

History
449 lines (365 loc) · 21.2 KB

MILESTONE.md

File metadata and controls

449 lines (365 loc) · 21.2 KB

Codiconomy — Milestones de entrega

Plano passo a passo para levar o bot do estado atual (/ping, /register, /carteira, /trabalhar, /diario, /transferir, /extrato com saldo único em CDC) até a economia completa descrita em Regras funcionais consolidadas (doravante "regras").

Stack mantida: Bun + TypeScript + discord.js 14 + SQLite (bun:sqlite) + Drizzle. Toda regra de economia continua pura em src/shared-domain, e toda mudança de saldo continua passando por src/database.


Visão geral

# Milestone Depende de Resultado visível no Discord
M0 Harness e decisões de transição — Nenhum (docs)
M1 Fundação: tempo, parâmetros, RNG, admin, scheduler M0 /admin parametro
M2 Livro-razão de partidas dobradas M1 /extrato no novo modelo
M3 Conta e banco M2 /cadastrar, /perfil, /depositar, /sacar, /pix
M4 Habilidades M3 Barras no /perfil
M5 Trabalho e quiz M4 /trabalhar, /quiz, admin de perguntas
M6 Notícias e eventos M1, M5 Notícia do dia, /admin evento
M7 Crime, roubo e risco M4, M6 /crime, /roubar
M8 Prisão e fiança M7 /fianca, /trabalhar voluntario
M9 Investimentos M3, M6 /investir, /investimentos
M10 Cassino M4, M6 Roleta, blackjack, caça-níqueis, corrida
M11 Temporadas, wipe, ranking e cofre M2–M10 /ranking, /admin temporada
M12 Balanceamento e hardening Todos Valores finais, reconciliação
M13 Release M12 Bot em produção

M6 pode começar em paralelo a M3–M5, já que só depende do resolver de parâmetros de M1. M9 e M10 são independentes entre si.

Estado atual

# Milestone Estado
M0–M12 Harness, fundação, ledger, conta e banco, habilidades, trabalho e quiz, notícias e eventos, crime, prisão, investimentos, cassino, temporadas, balanceamento e hardening ✅ Implementados e cobertos por bun test (reconciliação limpa)
M13 Release ⏳ Runbook pronto em docs/OPERATIONS.md; faltam as ações no ambiente real (produção, canal de anúncios, início oficial da temporada 1)

Decisões tomadas onde o plano deixava em aberto (detalhes em docs/RULES.md):

  • Moeda CD$; /register→/cadastrar; /carteira→/perfil; /transferir→/pix; /diario removido.
  • Banco recriado do zero (sem migração de dados); migrations antigas apagadas.
  • /perfil próprio é efêmero (sujo e risco); o de outros é público.
  • Roubo atinge apenas as mãos da vítima; ela é notificada por menção na resposta pública.
  • Escala de notícias: cada ponto de peso = ±10% sobre o padrão; 2 notícias por dia.
  • Quiz sem resposta no prazo = erro; sessão interrompida por restart é encerrada sem pagar o restante.
  • XP de Investidor no vencimento; XP de Sorte em toda aposta resolvida.
  • /trabalhar voluntario:true é uma opção do /trabalhar (o Discord não mistura subcomandos e uso direto).
  • No /admin, quiz-categoria e quiz-pergunta são grupos separados (o Discord limita a dois níveis de subcomando).
  • Roleta e blackjack seguem as regras clássicas; o piso de 3% vale para os jogos em que a Sorte atua (caça-níqueis e corrida).

Definição de pronto (vale para todo milestone)

  • bun run check e bun test passando.
  • Regras puras em src/shared-domain/<área> com testes unitários usando relógio e RNG injetados.
  • Persistência em src/database/<área>.ts, testada em SQLite :memory: com as migrations reais.
  • Nenhum await dentro de transação SQLite; nenhuma chamada ao Discord dentro de transação.
  • Toda movimentação de dinheiro passa pelo postTransaction (a partir de M2).
  • Smoke test manual na guild de desenvolvimento. Parcial: o startup, o registro dos 21 comandos e o login foram validados com um banco temporário; os fluxos interativos ainda precisam ser exercitados no Discord.
  • docs/ARCHITECTURE.md, docs/DEVELOPMENT.md e CLAUDE.md atualizados quando contratos mudarem.

M0 — Harness e decisões de transição

Objetivo: alinhar documentação e agentes às regras consolidadas antes de escrever código.

Decisões a registrar (o código atual diverge das regras)

Hoje Regras Decisão sugerida
Moeda CDC (Codify Coins) CD$ (Codify Money) Adotar CD$ e renomear formatCdc → formatMoney
/register /cadastrar Renomear; manter upsertUser como sincronização de identidade
/carteira /perfil Substituir por /perfil
/transferir /pix (banco → banco, cooldown 10s) Substituir por /pix
/diario Não existe nas regras Remover, ou incluir nas regras como fonte de emissão
Saldo único em wallets.balance 4 contas por usuário + ledger Migrar em M2
Cooldowns em colunas de wallets Cooldowns configuráveis por admin, notícia e evento Tabela genérica cooldowns em M1

Tarefas

  • Exportar as regras consolidadas para docs/RULES.md (fonte única de verdade funcional).
  • Manter este arquivo (docs/MILESTONE.md) com o progresso.
  • Reescrever a seção "Economia" de docs/ARCHITECTURE.md como visão-alvo: módulos por domínio, ledger, scheduler, resolver de parâmetros.
  • Atualizar CLAUDE.md:
    • Ler docs/RULES.md antes de qualquer mudança de economia.
    • Corrigir a menção desatualizada a "modelo único em users.ts".
    • Regra: dinheiro só se move via postTransaction; nunca UPDATE de saldo direto.
    • Regra: relógio e RNG sempre injetados; proibido Date.now()/Math.random() no domínio.
    • Regra: valores monetários e XP sempre inteiros (centavos / milésimos).
  • Definir convenção de pastas por domínio: src/shared-domain/{money,ledger,time,params,skills,crime,prison,investments,casino,news,season}.

Aceite: docs revisados; nenhuma divergência entre RULES.md e o plano.


M1 — Fundação: tempo, parâmetros, RNG, admin e scheduler

Objetivo: construir os serviços transversais de que todos os módulos dependem.

Tarefas

  • Relógio injetável (Clock), com implementação real e fake para testes.
  • RNG injetável (Rng), com int(min,max), float, chance(p) e pick(weights); implementação com semente para testes.
  • Dia do bot (shared-domain/time):
    • Janelas fixas a partir de uma âncora, com duração configurável.
    • windowAt(instant), nextTurnAt(instant), windowIndex.
    • Mudança de duração só vale na próxima virada (guardar duração pendente + vigência).
  • Parâmetros (shared-domain/params):
    • Catálogo tipado de todos os parâmetros, com default, mínimo e máximo (pagamentos, cooldowns, chances, tetos, prazos, taxas).
    • Tabela parameter_overrides (admin) no banco.
    • Resolver: default → notícia do dia → evento mais recente, com o admin sempre prevalecendo. Notícias e eventos entram em M6; aqui o resolver já aceita as camadas.
  • Cooldowns genéricos: tabela cooldowns(discord_id, action, available_at) e função pura de checagem (as colunas last_work_at/last_daily_at deixaram de existir).
  • Admin: helper isAdmin(interaction) usando interaction.memberPermissions (Administrador ou Gerenciar Servidor). Comandos admin com setDefaultMemberPermissions. Não exige novo intent.
  • Scheduler persistente:
    • Tabela scheduled_jobs(id, kind, due_at, payload, idempotency_key UNIQUE, done_at).
    • Varredura periódica (ex.: a cada 15s) + varredura no startup para recuperar o que venceu com o bot offline.
    • Handler por kind, executado em transação; done_at gravado na mesma transação do efeito.
    • Job recorrente de virada do dia do bot.
  • /admin parametro ver|definir|limpar.
  • Log estruturado de toda alteração administrativa (tabela admin_audit).

Aceite: testes de janela com durações de 6h e 2h, troca de duração no meio da janela e job vencido durante o "offline" sendo executado uma única vez.


M2 — Livro-razão de partidas dobradas

Objetivo: substituir wallets.balance + transactions pelo modelo de contas.

Modelo

  • seasons(id, starts_at, ends_at, status) — criar a temporada 1 já aqui.
  • accounts(id, season_id, owner_kind, owner_id, kind, currency, balance, frozen)
    • Usuário: limpo_maos, limpo_banco, limpo_investido, sujo_maos.
    • Sistema: cofre, emissao, cassino, custodia_corrida, conversao.
    • CHECK (balance >= 0) para contas de usuário; emissao e conversao isentas.
  • ledger_transactions(id, idempotency_key UNIQUE, kind, actor_id, metadata_json, created_at).
  • ledger_entries(id, transaction_id, account_id, amount) — amount com sinal.

Serviço

  • postTransaction({ key, kind, entries }) em src/database/ledger.ts:
    • Valida que a soma por moeda é zero (limpo e sujo separados).
    • Idempotente: chave repetida devolve a transação existente sem efeito.
    • Atualiza accounts.balance na mesma transação SQLite.
    • Recusa conta frozen para débitos de usuário (prisão).
  • Construtores puros de lançamentos por caso de uso (depositEntries, bailEntries, …) em shared-domain/ledger, todos testados.
  • reconcile(): soma de lançamentos = 0 por moeda; saldo em cache = soma dos lançamentos por conta.
  • Migration de dados do saldo de wallets — dispensada: o banco foi recriado do zero por decisão explícita (sem dados a migrar).
  • Remover wallets e a tabela transactions antiga (removidas; schema novo em uma única migration init).
  • Adaptar /extrato para ler ledger_entries.

Nota SQLite: não existe SELECT … FOR UPDATE. Com bun:sqlite síncrono e um único processo, db.transaction() (de preferência BEGIN IMMEDIATE) já serializa as escritas. A trava por linha das regras só passa a ser necessária se um dia migrar para Postgres.

Aceite: testes de concorrência simulada (duas transações com a mesma chave), saldo negativo recusado pelo banco e reconcile() limpo após toda a suíte.


M3 — Conta e banco

  • /cadastrar: cria usuário, as 4 contas da temporada ativa, barras em 0 e estado criminal zerado. Idempotente.
  • Guard requireAccount: todo comando de economia exige cadastro.
  • /perfil [@usuario]: saldos, barras, investimentos, risco e prisão. Decidir o que é público e o que é efêmero (sugestão: sujo e risco visíveis só para o próprio usuário).
  • /depositar valor e /sacar valor (aceitar tudo).
  • /pix @usuario valor: banco → banco, cooldown de 10s, sem taxa nem limite, recusa bot, auto-transferência e destino sem conta. O preso pode receber, mas não enviar.
  • Parser de valores "10,50" → 1050 centavos, com testes de borda.

Aceite: fluxo completo com dois usuários de teste; /extrato mostra cada operação.


M4 — Habilidades

  • Tabela skills(discord_id, season_id, skill, xp_milli) com CHECK (xp_milli BETWEEN 0 AND 100000).
  • Enum: inteligencia, experiencia_profissional, criminalidade, sorte, investidor.
  • Funções puras gainXp, loseXp (com clamp em 0 e 100.000) e skillFactor(xp), que mapeia a barra para o multiplicador de cada efeito (curva definida em M12).
  • Barras e percentuais no /perfil.
  • Histórico opcional de XP (skill_events) para auditoria.

Aceite: testes de clamp e de arredondamento em milésimos.


M5 — Trabalho e quiz

/trabalhar

  • Reescrever no ledger: emissao → limpo_banco.
  • Faixa vinda do resolver de parâmetros; multiplicador da Experiência profissional; ganho de XP.
  • Reduzir Criminalidade em 0,01% do XP atual.
  • Decidir o destino de /diario (M0).

Quiz

  • Tabelas quiz_categories, quiz_questions (4 alternativas, 1 correta, published).
  • /admin quiz categoria criar|editar|publicar|despublicar|remover.
  • /admin quiz pergunta …, com modal do Discord para enunciado e alternativas.
  • /quiz categoria quantidade:
    • Sessão com botões; 10s por pergunta a partir do envio.
    • Sem resposta no prazo = erro (confirmar nas regras).
    • Acerto: dinheiro (emissao → limpo_banco) + XP de Inteligência. Erro: penalidade de XP.
    • Pagamento por pergunta com chave quiz:{sessão}:{pergunta}.
    • Sessão persistida (quiz_sessions) para não pagar duas vezes após restart; sessão interrompida é encerrada sem pagar o restante.
    • Embaralhar alternativas; não repetir pergunta na mesma sessão.
    • Cooldown configurável e limite de perguntas por sessão.

Aceite: quiz completo de 5 perguntas com acerto, erro e timeout; reiniciar o bot no meio não gera pagamento duplicado.


M6 — Notícias e eventos

  • Tabela news_templates(id, text, parameter, direction, weight, active).
  • Definir a escala de weight (ex.: cada ponto = ±X% sobre o default) e quantas notícias são sorteadas por dia.
  • Job de virada do dia: sorteia notícias, grava daily_news(window_index, …) com chave por janela e publica um embed no canal configurado.
  • /admin noticia criar|editar|ativar|desativar|listar.
  • /noticias: mostra as notícias vigentes.
  • Tabela events(id, parameter, value, starts_at, ends_at, created_by).
  • /admin evento criar|encerrar|listar.
  • Ligar as camadas ao resolver de M1 e garantir que cada valor final respeite o mínimo e o máximo do catálogo.
  • Configuração do canal de anúncios (/admin canal noticias).

Aceite: com notícia reduzindo o trabalho e evento fixando o pagamento, o evento vence nesse parâmetro e a notícia continua valendo nos outros.


M7 — Crime, roubo e risco de prisão

  • Tabela criminal_state(discord_id, season_id, risk_milli, last_attempt_window).
  • Domínio puro:
    • arrestChance = min(criminalidade + risco, teto).
    • Ordem de cada tentativa: sortear prisão → resolver sucesso/falha → aumentar o risco (sucesso e falha igualmente).
    • Decaimento na virada: sem tentativa na janela → risco × 0,85.
    • XP: sucesso ganha; falha perde 5% do XP que ganharia.
  • /crime: sucesso = emissao (sujo) → sujo_maos.
  • /roubar @usuario:
    • Cooldown (admin > notícia).
    • Percentual sorteado separadamente para limpo e sujo da vítima (1% até o teto da notícia).
    • Limpo roubado passa por conversao e chega sujo ao ladrão.
    • Recusar vítima sem conta, presa, bot ou o próprio usuário.
  • Notificar a vítima (DM ou menção) — decidir.
  • Job de decaimento ligado à virada do dia.

Aceite: simulação com seed: 1.000 tentativas e chance de prisão crescendo conforme esperado; decaimento correto após uma janela sem crime.


M8 — Prisão e fiança

  • Tabela prison_terms(id, discord_id, season_id, starts_at, ends_at, base_value, bail_value, victim_id, status).
  • Ao prender, numa única transação:
    • Confisco: sujo_maos → conversao → cofre (limpo).
    • limpo_maos.frozen = true.
    • Risco zerado.
    • Pena sorteada: mínimo 40% do dia; máximo min(4h, dia).
    • Agendar job prison:release:{id}.
  • Fiança: max(valor, 100,00) × 5; em tentativa falha, o "valor" é o sorteio do que teria ganho.
  • /fianca: debita limpo_maos e depois limpo_banco (débito permitido apesar do congelamento); 20% para a vítima e 80% para o cofre (100% no /crime).
  • /trabalhar voluntario: −1% a 3% do tempo restante; cooldown de 20% do dia; reagenda o job de soltura.
  • Guard global requireNotJailed: bloqueia todos os comandos, exceto /fianca, /trabalhar voluntario e /perfil.
  • Soltura: descongela, marca o termo como served, notifica o usuário.

Aceite: prisão durante o "offline" do bot termina corretamente; pagar fiança e o job de soltura disparando no mesmo instante não liberam duas vezes nem cobram duas vezes.


M9 — Investimentos

  • Tabela investments(id, discord_id, season_id, principal, rate_bp, term, starts_at, matures_at, status).
  • /investir valor prazo (curto/médio/longo):
    • Mínimo CD$ 10,00; teto total investido ≤ 50% × (banco + investido).
    • Taxa sorteada na faixa da notícia vigente; Investidor puxa para o topo, sem ultrapassá-lo; taxa congelada na aplicação.
    • Prazo em dias do bot; lançamento limpo_banco → limpo_investido.
    • Agendar investment:mature:{id}.
  • Vencimento: limpo_investido → limpo_banco (principal) + emissao → limpo_banco (rendimento), creditado mesmo com o usuário preso.
  • /investimentos: lista os ativos com data de vencimento (formatDiscordTimestamp).
  • Ganho de XP de Investidor ao aplicar ou ao vencer — decidir.

Aceite: vencimento com o bot offline pago uma única vez; teto de 50% validado com várias aplicações seguidas.


M10 — Cassino

Base comum

  • placeBet({ limpo, sujo }): debita das duas contas para cassino.
  • payout(valor): cassino (limpo) → limpo_maos — lavagem total.
  • Sujo perdido: cassino (sujo) → conversao → cofre.
  • Apostas mínima e máxima por jogo (parâmetros).
  • Sorte afeta apenas caça-níqueis e corrida, com piso de vantagem da casa de 3%.
  • Ganho de XP de Sorte (definir quando: por aposta ou por vitória).

Jogos

  • Roleta (europeia, pagamentos clássicos).
  • Blackjack (pagamentos clássicos, sessão com botões pedir/parar/dobrar, sessão persistida e timeout que encerra a mão).
  • Caça-níqueis (tabela de símbolos e pagamentos configurável, com vantagem da casa calculada).
  • Corrida de cavalos:
    • /cavalos valor cavalo abre o lobby; /cavalos entrar valor cavalo.
    • Apostas vão para custodia_corrida na entrada.
    • /cavalos start pelo criador ou largada automática em 20s (job agendado).
    • Chances por cavalo com base nos participantes e na notícia.
    • Taxa de 10% para o cofre → vencedores recebem aposta + 20% do pote → excedente rateado proporcionalmente → sobra para o cofre.
    • Animação simples por edição de mensagem (fora da transação).
  • Script de simulação Monte Carlo por jogo para validar o retorno esperado (RTP) e o piso de 3%.

Aceite: simulação de 1 milhão de rodadas mostra vantagem da casa ≥ 3% nos jogos com Sorte máxima; corrida com 1, 3 e 6 apostadores fecha os lançamentos em zero.


M11 — Temporadas, wipe, ranking e cofre

  • /admin temporada iniciar duracao [evento], encerrar e status.
  • Duração default de 2 semanas; job de fim de temporada.
  • Wipe seletivo (dinheiro / habilidades / risco e prisões / quiz stats):
    • Dinheiro: novas contas na nova temporada; investimentos ativos encerrados sem pagamento.
    • Habilidades: copiar ou não as barras para a nova temporada.
    • Histórico da temporada anterior preservado (nada de DELETE).
  • /ranking: top 10 por limpo_maos + limpo_banco da temporada ativa.
  • /admin cofre saldo e /admin cofre transferir @usuario valor (premiação manual entre temporadas), com auditoria.

Aceite: wipe com prisões e investimentos ativos; o ranking da temporada anterior continua consultável.


M12 — Balanceamento e hardening

  • Planilha/arquivo de parâmetros com valores iniciais para tudo que ficou sem número:
    • XP por ação e penalidades; curva skillFactor.
    • Faixas de trabalho, quiz e crime.
    • Teto de chance de prisão e incremento de risco por tentativa.
    • Teto default do percentual roubado.
    • Cooldowns default de trabalho, quiz, crime e roubo.
    • Duração default do dia do bot.
    • Escala de peso das notícias e lista inicial de notícias.
  • Simulador econômico (script Bun): N jogadores com perfis distintos ao longo de uma temporada; medir emissão, inflação (emissao), crescimento do cofre e dispersão do ranking.
  • Job periódico de reconcile() com alerta no log.
  • [~] Backup do arquivo SQLite: removido por decisão (sem backups automáticos).
  • Rate limit global por usuário contra spam de interações.
  • Revisão de mensagens de erro: nunca vazar estado interno.
  • Teste de carga leve: comandos concorrentes do mesmo usuário.

Aceite: simulação de uma temporada sem saldos explosivos; reconcile() limpo após teste de carga.


M13 — Release

  • Ambiente de produção (VPS ou container) com volume persistente para o SQLite.
  • Registro de comandos: avaliar continuar por guild (rápido) — o bot é single-guild, então manter.
  • Runbook em docs/OPERATIONS.md: deploy, rollback, rodar migration, iniciar e encerrar temporada.
  • Canal de anúncios e permissões do bot configurados no servidor Codify.
  • Temporada 1 oficial iniciada; acompanhamento dos primeiros dias com ajustes via /admin parametro, sem deploy.