Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions docs/about-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,88 @@ pnpm test:stdb-local-scenarios:full
- The frontend provider contract still includes `submitMove()` as the UI-facing action, but the SpacetimeDB provider implements it by committing and revealing under the hood.
- `packages/shared/src/constants.ts` defines `MAX_ROUNDS = 9`, while `apps/spacetime/spacetimedb/src/index.ts` defines `MAX_ROUNDS = 5`. This may be intentional or may cause behavior differences between mock/shared logic and real backend.
- The backend duplicates parts of game logic instead of importing `packages/shared`, so matrix and energy changes must be kept synchronized.

## Architecture Analysis

### Dependency Graph

```
@elmental/shared
└── consumed by: apps/tma, apps/server
└── not consumed by: apps/payments (independent domain, no shared dep)

apps/tma
└── depends on: @elmental/shared, spacetimedb SDK, socket.io-client, zustand, telegram-apps/sdk

apps/server (legacy)
└── depends on: @elmental/shared, express, socket.io, ioredis, pg

apps/payments
└── depends on: spacetimedb SDK, undici
└── no dependency on apps/tma, apps/server, or @elmental/shared

apps/spacetime/spacetimedb
└── single-file module, cannot import workspace packages
└── generates bindings consumed by: apps/tma, apps/payments
```

No app imports from another app. No circular dependencies exist.

### Layer Separation

The monorepo has four distinct runtime layers:

| Layer | App | Responsibility |
|-------|-----|----------------|
| Frontend | `apps/tma` | React UI, Telegram SDK, provider contract |
| Authoritative backend | `apps/spacetime/spacetimedb` | Game state, reducers, matchmaking, settlement — this is the server |
| Payments | `apps/payments` | Stars invoicing, refunds, wallet history, admin |

`apps/server` is a deprecated Express/Socket.io experiment that predates the SpacetimeDB architecture. It is not part of any active flow. TMA has no references to it; it is absent from `docker-compose.selfhost.yml`. Do not route gameplay through it. All server-side game logic — matchmaking, move resolution, balance mutations, timeouts — lives in SpacetimeDB reducers. If a new server-side component is needed, extend `apps/spacetime/spacetimedb` or create a dedicated new app.

### What Is Properly Shared

`packages/shared` is the correct and only place for game rules. It contains:

- `types.ts`: canonical enums and interfaces (`MoveId`, `GameMode`, `RoundResult`, `MatchState`)
- `constants.ts`: energy values, move costs, regen tables, ELO parameters, economy constants
- `game-logic.ts`: `resolveRound`, `calculateElo`, `calculateEnergy`, `resolveOverclock`

Frontend UI uses `@elmental/shared` for rendering decisions (move costs, labels). The server-side SpacetimeDB module duplicates equivalent logic because it cannot import workspace packages — this is an architectural constraint, not a design choice.

### Known Duplications

**1. Game constants and logic — `packages/shared` vs `apps/spacetime/spacetimedb/src/index.ts`**

SpacetimeDB modules compile to a single self-contained file and cannot consume npm workspace packages. As a result, move costs, energy constants, regen values, and the outcome matrix are defined in both places. Any change to game rules requires a matching update in both files. `scripts/check-matrix-parity.mjs` exists to catch divergence in the move matrix, but does not cover all constants. The `MAX_ROUNDS` discrepancy (9 in shared, 5 in spacetime) is an example of what can silently drift.

**2. Telegram `initData` validation — `apps/server/src/auth/index.ts` and `apps/payments/src/telegramInitData.ts`**

Both implement HMAC-SHA256 validation of Telegram `initData`. The logic is identical. Because `apps/payments` does not depend on `apps/server` (and should not), this duplication is currently necessary. If `@elmental/shared` ever gains a browser/Node-compatible crypto utility layer with no external dependencies, this validation could move there.

**3. SpacetimeDB `module_bindings` — `apps/tma/src/module_bindings` and `apps/payments/src/module_bindings`**

These are auto-generated from the same SpacetimeDB schema and are expected to be identical at any given schema version. They are not manually authored. Do not consolidate them into a shared package — they must stay co-located with each consumer so that `pnpm stdb:generate` can target each app independently.

### Layer Violation Checks

No violations were found at the time of this analysis:

- `apps/tma` does not import from `apps/server` or `apps/payments`.
- `apps/payments` does not import from `apps/tma` or `apps/server`.
- `apps/server` does not import from `apps/tma` or `apps/payments`.
- All three apps consume `@elmental/shared` only through the workspace package name, not via relative paths.
- Generated `module_bindings` are not imported outside their owning app. `spacetimeProvider.ts` is the only non-generated file in `apps/tma` that may import from `src/module_bindings`.

### Synchronization Risks

The following pairs must stay in sync manually and are not enforced by the type system:

| What | Location A | Location B | Guard |
|------|-----------|-----------|-------|
| Move outcome matrix | `packages/shared/src/game-logic.ts` | `apps/spacetime/spacetimedb/src/index.ts` | `pnpm test:matrix-parity` |
| Energy and move cost constants | `packages/shared/src/constants.ts` | `apps/spacetime/spacetimedb/src/index.ts` | None — manual |
| `MAX_ROUNDS` | `packages/shared/src/constants.ts` (= 9) | `apps/spacetime/spacetimedb/src/index.ts` (= 5) | None — diverged |
| SpacetimeDB schema | `apps/spacetime/spacetimedb/src/index.ts` | `apps/tma/src/module_bindings`, `apps/payments/src/module_bindings` | `pnpm stdb:generate` (run after every schema change) |

When changing game rules, update both `packages/shared` and `apps/spacetime/spacetimedb/src/index.ts`, then run the full verification sequence described in **Common Commands**.