Game Design / Bot Infrastructure

Discord bot (Hono + discord.js on Railway), HTML→PNG card renderer, and Cantrip ruleset all read from packages/rules. One rules change ships everywhere.
Five-stat spread (Grit / Vision / Soul / Vibe / Flow) and Fate Path progression playtested before being implemented as a typed rules package.
Zairoo is an original Afrocentric tabletop RPG system that doubles as the flagship ruleset on Cantrip. Two surfaces: a Discord bot (Hono API + discord.js, deployed on Railway) for play-by-message sessions, and a published Cantrip ruleset for full AI-DM-driven play.
The game system uses a five-stat spread (Grit / Vision / Soul / Vibe / Flow) and a Fate Path progression, designed first as a play-tested system, then implemented as a card-renderer package that produces the printable and digital character cards.
Most original TTRPG systems are PDFs. Zairoo had to ship as software from day one: a Discord bot for low-friction sessions, a card-renderer for character art, and a Cantrip ruleset for AI-mediated play. Each surface had to honor the same canonical rules layer without duplicating logic.
Hono + oRPC API and discord.js bot deployed on Railway. Players run sessions in any Discord server; the bot handles character creation, dice, and session state persistence.
Standalone package that renders Zairoo character cards (stats, fate path, starting move) as PNGs for digital sharing or printing. Single source of truth for character visuals across surfaces.
Same canonical rules layer published as a Cantrip ruleset, unlocking AI-DM-driven play in the browser. The Discord bot and Cantrip both read from the same engine.

Most original tabletop systems ship as a PDF. I wanted Zairoo to be software from day one. The trick was making the rules themselves the single source of truth, so a Discord bot, a printable card renderer, and an AI-run Cantrip ruleset all read the same engine and one rules change ships everywhere.
Three surfaces (Discord bot, card renderer, Cantrip ruleset) all need the exact same rules, and three copies of the logic would drift instantly.
I made one canonical typed rules package the single source of truth and had every surface read from it.
Three surfaces stay cheap to feed when the single source is the rules. Make it a shared UI and the cost balloons.
A rule that lives only in a rulebook stays ambiguous until a player, or a bot, does something the prose never anticipated.
I defined the five stats (Grit, Vision, Soul, Vibe, Flow) once as a typed enum and made everything downstream depend on it: the database, the dice API's validator, and the AI narrator's tool schema.
Game design is software design. Because the stat list is declared in exactly one place, a move that points at a stat the game doesn't have is a compile error, a rejected API call, and an impossible AI action all at once. The rules can't drift, because nothing can even express breaking them.
Discord is the lowest-friction way to play, but its UI has hard ceilings. Card galleries and fate-path tracking just don't fit there.
I let each surface do what it's good at instead of forcing parity, pushing the rich visuals to the web.
Accept the surface tradeoffs instead of fighting them. Match the interaction to the medium that actually supports it.