brink
by JavaDerek
README.md
# brink
A single-player, cinematic geopolitical roleplay: **you are the supreme executive of a great power,
handling only the geopolitical brief** — taking calls from world leaders, hearing your advisors,
making decisions — in a persistent world grounded in current real-world geopolitics.
Built as a fork of [DMCP](https://github.com/shawnrushefsky/dmcp) (LLM-as-DM, MCP server as
persistent state + tools), with a geopolitical rules core ported from the `BalanceOfTerror` engine.
**To play it, read [docs/MANUAL.md](docs/MANUAL.md).**
**The design brief is [REBOOT-DESIGN-AND-REQUIREMENTS.md](REBOOT-DESIGN-AND-REQUIREMENTS.md)** and it
is the authority on everything below. Read it first if you're working on the code.
## The shape of it
- **The LLM is the Game-Master**: it classifies a fuzzy action into which mechanic fires, narrates,
and calls tools. It **never adjudicates**.
- **The MCP server is referee + ledger**: authoritative resolution, persistence, and the handful of
rules the GM cannot talk its way out of.
### Three hard rules that constrain every change here
1. **Show, don't tell.** If the player-facing surface ever states a disposition or a raw value —
`DOCTRINE: HAWK`, `cohesion 80.0`, `(DOVE advisor)` — that is a **bug**, not a style preference.
The player deduces state from behavior, dialogue, and consequence.
2. **Resolve, then narrate.** The GM never narrates an outcome before the server returns it. It
calls the resolve tool, receives an authoritative outcome payload carrying an explicit narration
constraint, and narrates *from* that payload. The fiction can be florid about *the how*; it can
never lie about *the what*.
3. **No credential in the working tree.** The GM runs keyless, on a Claude subscription (OAuth).
There is no standing pay-per-token API key anywhere in this repo, and development works against a
mock GM backend. See §2.4 of the brief — this is a requirement, not guidance.
## Fork discipline (§10 of the brief)
Changes sort into three buckets, and **generic mechanism must never share a commit with geopolitical
content**. That separation is what keeps the offer-back-upstream workflow mechanical instead of
archaeological.
| Bucket | What | Where it lives |
|---|---|---|
| **Use as-is** | DMCP's persistence spine, factions, relationships-with-history, resources, locations, secrets/visibility, dice/checks, pause/resume, web UI | untouched |
| **Offer back upstream** | Generic, game-agnostic mechanism: atomicity, the declarative constraint layer, conserved resources, on-expiry consequences | `offer/*` branches — see [docs/IMPROVEMENTS-TO-DMCP.md](docs/IMPROVEMENTS-TO-DMCP.md) |
| **brink-private** | Geopolitical rules content, GM persona, situation-room UI, current-events refresh, avatar/voice | `main` |
The line: *"a resource can be declared conserved"* is a DMCP feature; *"prestige is conserved across
three great powers"* is our game.
**[docs/IMPROVEMENTS-TO-DMCP.md](docs/IMPROVEMENTS-TO-DMCP.md)** is the full account of what changed in
DMCP and which parts are generic enough to lift — written for DMCP users deciding whether any of it is
worth taking, as much as for upstream.
### Branch convention
- `upstream` remote tracks DMCP. `git fetch upstream && git merge upstream/main` pulls their changes.
- Work intended for upstream branches from `upstream/main` as **`offer/<topic>`**, touches **only
DMCP-generic files**, and carries no geopolitical content — so it cherry-picks cleanly.
(`offer/`, not `upstream/`: a local branch named `upstream/foo` collides with the `upstream` remote's
own ref namespace, and git will happily let you create the ambiguity.)
- Everything else branches from `main`.
- Upstream's own README is preserved in history: `git show upstream/main:README.md` (setup
instructions and the full ~170-tool inventory).
## Development
```bash
npm ci # install
npm run build # tsc + client build
npm run dev # run the server
npm run test:run # vitest
npm run lint # eslint (CI gate)
npm run typecheck # tsc --noEmit (CI gate)
```
Local MCP testing: `npx @modelcontextprotocol/inspector node dist/index.js`
**Write lots of tests.** DMCP arrived with essentially no coverage of business logic, so there is no
upstream safety net under the invariants this game depends on. TDD is mandatory — failing test first.
See [CLAUDE.md](CLAUDE.md) for the test harness and the codebase gotchas worth knowing before you start.
## Status
**Tranche 1 is complete.** The text-first vertical slice runs:
```bash
npm run build:server # once
npm run play -- --real # Claude as the game-master
npm run play # placeholder game-master, spends nothing
```
Graphics amplify a loop that already works; they cannot rescue one that doesn't — so this had to
exist before anything cinematic.
What's in place:
- **The referee.** All three enforced rules from §4.2 have real mechanisms: prestige is a `conserved`
constraint set moved only through an atomic transfer; DEFCON is `bounded` *and* `resolve_only`, so
it has exactly one writer; declared on-expiry consequences are applied by the server itself, so a
countdown lands regardless of the fiction.
- **The rules core**, ported from the `BalanceOfTerror` Python engine as pure functions with injected
RNG, verified against that engine's own test suite.
- **The resolve path**, which makes resolve-then-narrate enforceable: the payload carries an explicit
narration constraint, and tests re-read the database after every call to prove prose and ledger
cannot disagree.
- **The GM layer**: system prompt, persona seeds whose dispositions exist only as behavior, a
`GMBackend` seam with a deterministic offline mock, and **the real game-master** — Claude Code driven
headlessly under a subscription, with **no credential anywhere in the tree**.
Verified live: the GM streams an outcome-neutral reaction, calls the referee itself, and narrates only
after the payload lands. DEFCON moved 5→4 with a matching audit row — and since DEFCON carries a
`resolve_only` constraint, that write had no other route available to it.
Tracked in [the issues](https://github.com/JavaDerek/brink/issues).
## Contributing, and security
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — how to get set up, and the two rules that catch people out.
- **[SECURITY.md](SECURITY.md)** — how to report something, what leaves your machine, and why there is
no API key in this repository.
- **[docs/PUBLISHING.md](docs/PUBLISHING.md)** — the repository topology, and the two routes work takes
out of here: a tree copy to the public repo, and `offer/*` branches to DMCP.
## License
MIT. Copyright is shared: DMCP's original notice is retained alongside brink's own — see
[LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues