Multiplayer step 1/12 — pure schema change, no behavior change.
What changed
players table (id TEXT PK, provider, username, cash REAL, reputation REAL) in the raw DDL (lib/db/client.ts) and Drizzle schema (lib/db/schema.ts; AccountRow → PlayerRow). The singleton account table is gone from fresh DBs.
One-time migration in open(): if a legacy file still has account (sqlite_master guard, same style as the v0.12.0 contracts migration) and players is empty, its single row is copied into a seeded local player — id local, provider local, username kamilkrzywda (joinable to users later) with cash/reputation carried over — then DROP TABLE account. Idempotent; runs exactly once per legacy file.
Fresh worlds: first-run seed check flipped from COUNT(*) FROM account to players; seedEmpty() inserts the local player row directly (same starting values).
Temporary single-player helper (lib/db/localPlayer.ts): getLocalPlayer(inst) + updateLocalPlayerEconomy(inst, {cash?, reputation?}). Every economy read/write now routes through it — snapshot (state.ts), all five actions, engine delivery/top-up/spawn reads, and the three test hooks. Will be parameterized by playerId in a later step.
API unchanged: /api/state keeps its account: {cash, reputation} field name and values; OpenAPI untouched (17 paths).
Tests
Fresh DB asserts the seeded local player row AND no account table; new dedicated migration test builds a minimal legacy file with only an account table → copied, dropped, second open is a no-op.
All existing suites converted to the helper (actions, engine, client, buy/sell/abandon routes); admin rows CRUD's PK-less example moves account → sim_state; tables-list expectation swaps account → players.
No new e2e spec — behavior byte-identical; CI's existing suite covers UI paths and fresh-stack boot exercises the new seed.
Local: tsc --noEmit clean, vitest run 411/411 (38 files), playwright test --list 64 tests in 17 files.
Multiplayer step 1/12 — pure schema change, no behavior change.
## What changed
- **`players` table** (`id TEXT PK`, `provider`, `username`, `cash REAL`, `reputation REAL`) in the raw DDL (`lib/db/client.ts`) and Drizzle schema (`lib/db/schema.ts`; `AccountRow` → `PlayerRow`). The singleton `account` table is gone from fresh DBs.
- **One-time migration** in `open()`: if a legacy file still has `account` (sqlite_master guard, same style as the v0.12.0 contracts migration) and `players` is empty, its single row is copied into a seeded local player — id `local`, provider `local`, username `kamilkrzywda` (joinable to `users` later) with cash/reputation carried over — then `DROP TABLE account`. Idempotent; runs exactly once per legacy file.
- **Fresh worlds**: first-run seed check flipped from `COUNT(*) FROM account` to `players`; `seedEmpty()` inserts the local player row directly (same starting values).
- **Temporary single-player helper** (`lib/db/localPlayer.ts`): `getLocalPlayer(inst)` + `updateLocalPlayerEconomy(inst, {cash?, reputation?})`. Every economy read/write now routes through it — snapshot (`state.ts`), all five actions, engine delivery/top-up/spawn reads, and the three test hooks. Will be parameterized by playerId in a later step.
- **API unchanged**: `/api/state` keeps its `account: {cash, reputation}` field name and values; OpenAPI untouched (17 paths).
## Tests
- Fresh DB asserts the seeded local player row AND no `account` table; new dedicated migration test builds a minimal legacy file with only an `account` table → copied, dropped, second open is a no-op.
- All existing suites converted to the helper (actions, engine, client, buy/sell/abandon routes); admin rows CRUD's PK-less example moves `account` → `sim_state`; tables-list expectation swaps `account` → `players`.
- No new e2e spec — behavior byte-identical; CI's existing suite covers UI paths and fresh-stack boot exercises the new seed.
Local: `tsc --noEmit` clean, `vitest run` 411/411 (38 files), `playwright test --list` 64 tests in 17 files.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Multiplayer step 1/12 — pure schema change, no behavior change.
What changed
playerstable (id TEXT PK,provider,username,cash REAL,reputation REAL) in the raw DDL (lib/db/client.ts) and Drizzle schema (lib/db/schema.ts;AccountRow→PlayerRow). The singletonaccounttable is gone from fresh DBs.open(): if a legacy file still hasaccount(sqlite_master guard, same style as the v0.12.0 contracts migration) andplayersis empty, its single row is copied into a seeded local player — idlocal, providerlocal, usernamekamilkrzywda(joinable touserslater) with cash/reputation carried over — thenDROP TABLE account. Idempotent; runs exactly once per legacy file.COUNT(*) FROM accounttoplayers;seedEmpty()inserts the local player row directly (same starting values).lib/db/localPlayer.ts):getLocalPlayer(inst)+updateLocalPlayerEconomy(inst, {cash?, reputation?}). Every economy read/write now routes through it — snapshot (state.ts), all five actions, engine delivery/top-up/spawn reads, and the three test hooks. Will be parameterized by playerId in a later step./api/statekeeps itsaccount: {cash, reputation}field name and values; OpenAPI untouched (17 paths).Tests
accounttable; new dedicated migration test builds a minimal legacy file with only anaccounttable → copied, dropped, second open is a no-op.account→sim_state; tables-list expectation swapsaccount→players.Local:
tsc --noEmitclean,vitest run411/411 (38 files),playwright test --list64 tests in 17 files.