Skip to content

Commit 1c4d711

Browse files
committed
feat(schema): catalog system + schema meta fields — Phase 3.4 complete. Add optional $schema, description, tags, license to RulesetMeta (types + Zod + JSON Schema). Fix JSON Schema bugs: add missing custom deck preset, initialVariables, tieCondition, autoEndTurnCondition. Update all 5 rulesets with catalog metadata. Create scripts/validate-rulesets.ts and scripts/build-catalog.ts for CLI validation and catalog generation. Add .github/workflows/catalog.yml (oven/bun:slim container) for CI validation + GitHub Pages deploy. Deduplicate cardgame.v1.schema.json (remove stale copy from shared). 15 new schema validation tests. 913 tests green (804 shared + 15 schema + 94 host)
1 parent 08efd61 commit 1c4d711

20 files changed

Lines changed: 711 additions & 206 deletions

.github/workflows/catalog.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Validate & Catalog Rulesets
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "rulesets/**"
8+
- "packages/schema/**"
9+
workflow_dispatch:
10+
11+
jobs:
12+
validate-and-catalog:
13+
runs-on: ubuntu-latest
14+
container: oven/bun:slim
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
19+
- name: Install dependencies
20+
run: bun install --frozen-lockfile
21+
22+
- name: Validate rulesets
23+
run: bun run scripts/validate-rulesets.ts
24+
25+
- name: Build catalog
26+
run: bun run scripts/build-catalog.ts
27+
28+
- name: Upload catalog artifact
29+
uses: actions/upload-artifact@v4
30+
with:
31+
name: catalog
32+
path: catalog.json
33+
34+
deploy-pages:
35+
needs: validate-and-catalog
36+
if: github.ref == 'refs/heads/main'
37+
runs-on: ubuntu-latest
38+
container: oven/bun:slim
39+
40+
permissions:
41+
pages: write
42+
id-token: write
43+
44+
environment:
45+
name: github-pages
46+
url: ${{ steps.deployment.outputs.page_url }}
47+
48+
steps:
49+
- name: Checkout repository
50+
uses: actions/checkout@v4
51+
52+
- name: Install dependencies
53+
run: bun install --frozen-lockfile
54+
55+
- name: Build catalog
56+
run: bun run scripts/build-catalog.ts
57+
58+
- name: Upload Pages artifact
59+
uses: actions/upload-pages-artifact@v3
60+
with:
61+
path: .
62+
63+
- name: Deploy to GitHub Pages
64+
id: deployment
65+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ ios/
3535

3636
# CouchKit bundled client assets (build artifact)
3737
packages/host/src/www-manifest.json
38+
39+
# Generated catalog (build artifact)
40+
catalog.json

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ card-game-engine/
4242
├── packages/
4343
│ ├── shared/ @card-engine/shared — game engine core (types, expression
4444
│ │ evaluator, interpreter, PRNG)
45+
│ ├── schema/ @card-engine/schema — JSON Schema, Zod validation, types
46+
│ │ (card, ruleset, state)
4547
│ ├── host/ @card-engine/host — Android TV app (Expo + CouchKit host
4648
│ │ + expo-file-system storage)
4749
│ └── client/ @card-engine/client — phone controller (Vite + React +
@@ -53,7 +55,8 @@ card-game-engine/
5355

5456
| Package | Runtime | Key Dependencies |
5557
|---------|---------|------------------|
56-
| `shared` | Pure TypeScript, zero framework deps | Zod |
58+
| `schema` | Pure TypeScript | Zod |
59+
| `shared` | Pure TypeScript, zero framework deps | @card-engine/schema, Zod |
5760
| `host` | Expo + React Native | CouchKit host, expo-file-system |
5861
| `client` | Vite + React 18 | CouchKit client |
5962

@@ -83,14 +86,14 @@ bun run dev:client
8386

8487
### Testing
8588

86-
Tests live in the shared and host packages and use Vitest:
89+
Tests live in the shared, schema, and host packages and use Vitest:
8790

8891
```sh
8992
cd packages/shared
9093
bunx vitest run
9194
```
9295

93-
898 tests across 16 test files cover the engine core (expression evaluator, builtins, interpreter, PRNG, schema validation, player views, game phases, integration scenarios, host bridge) and the host package (storage, importers). The client package is verified via `tsc` type-checking and Vite production build.
96+
913 tests across the shared (804), schema (15), and host (94) packages cover the engine core (expression evaluator, builtins, interpreter, PRNG, schema validation, player views, game phases, integration scenarios), schema meta fields, and the host package (storage, importers). The client package is verified via `tsc` type-checking and Vite production build.
9497

9598
### Build and Deploy
9699

@@ -113,6 +116,18 @@ Type-check the shared and client packages:
113116
bun run typecheck
114117
```
115118

119+
### Scripts
120+
121+
| Command | Description |
122+
|---------|-------------|
123+
| `bun run dev:client` | Start the client Vite dev server with HMR |
124+
| `bun run build:client` | TypeScript check + Vite production build |
125+
| `bun run bundle:client` | Bundle client dist into host's Android assets |
126+
| `bun run build:android` | Bundle client + Expo Android build |
127+
| `bun run typecheck` | Type-check shared and client packages |
128+
| `bun run validate` | Validate all rulesets against the JSON Schema |
129+
| `bun run catalog` | Generate `catalog.json` from all rulesets' metadata |
130+
116131
## Rulesets
117132

118133
A `.cardgame.json` file declaratively defines everything the engine needs to run a card game: metadata, deck composition, zones, roles, phases (FSM), scoring, visibility rules, and UI hints.
@@ -125,18 +140,21 @@ The [`rulesets/`](rulesets/) directory contains example rulesets:
125140
- **`ninety-nine.cardgame.json`** — an accumulation game demonstrating custom variables (`get_var`, `set_var`, `inc_var`), conditional card effects with `if()`, and turn reversal
126141
- **`uno.cardgame.json`** — a shedding game demonstrating `play_card` action effects, custom variables for color choice, declare with params, Skip/Reverse/Draw Two effects, and multi-phase Wild card flow
127142

143+
Rulesets support optional catalog fields (`description`, `tags`, `license`) in their `meta` block. Run `bun run catalog` to generate a `catalog.json` index of all rulesets for browsing and discovery. Run `bun run validate` to validate all rulesets against the schema.
144+
128145
See the [Ruleset Authoring Guide](docs/ruleset-authoring.md) for the full format specification, expression language reference, and annotated examples. The [Engine API Reference](packages/shared/README.md) documents all public functions and builtins.
129146

130147
## Project Status
131148

132-
All four implementation phases are **complete** with **898 passing tests** across 16 test files.
149+
All four implementation phases are **complete** with **913 passing tests** across shared (804), schema (15), and host (94) packages.
133150

134151
| Phase | Status | Tests |
135152
|-------|--------|-------|
136153
| Phase 1 — Engine Core | ✅ Complete | 804 |
137154
| Phase 1.5 — Documentation | ✅ Complete ||
138155
| Phase 2 — Storage & Import | ✅ Complete | 94 |
139156
| Phase 3 — Host Screens & CouchKit Integration | ✅ Complete ||
157+
| Phase 3.4 — Schema Package & Catalog | ✅ Complete | 15 |
140158
| Phase 4 — Client Controller App | ✅ Complete ||
141159

142160
The app builds and deploys to Android TV via `bun run build:android`. The host runs an HTTP+WebSocket server via CouchKit; phones connect by scanning a QR code displayed on the TV.

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/ruleset-authoring.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ sections:
5555

5656
```json
5757
{
58+
"$schema": "../packages/schema/src/schema/cardgame.v1.schema.json",
5859
"meta": {
5960
"name": "string",
6061
"slug": "string (lowercase, hyphens only)",
@@ -63,10 +64,13 @@ sections:
6364
"players": {
6465
"min": "number (integer >= 1)",
6566
"max": "number (integer >= 1)"
66-
}
67+
},
68+
"description": "string (optional — short description of the game)",
69+
"tags": ["string (optional — searchable tags for catalog browsing)"],
70+
"license": "string (optional — license identifier, e.g. MIT, public-domain)"
6771
},
6872
"deck": {
69-
"preset": "standard_52 | standard_54 | uno_108",
73+
"preset": "standard_52 | standard_54 | uno_108 | custom",
7074
"copies": "number (integer >= 1)",
7175
"cardValues": { "...rank-to-value mappings" }
7276
},
@@ -87,25 +91,34 @@ sections:
8791
}
8892
```
8993

90-
All sections are required. The schema enforces this at load time.
94+
| Field | Required | Description |
95+
|---|---|---|
96+
| `$schema` | No | Root-level field referencing the JSON Schema for editor validation and autocompletion. |
97+
| `meta` | Yes | Metadata block (see below). |
98+
| `deck``ui` | Yes | All other top-level sections are required. The schema enforces this at load time. |
9199

92100
For the blackjack ruleset, the `meta` block looks like this:
93101

94102
```json
95103
{
104+
"$schema": "../packages/schema/src/schema/cardgame.v1.schema.json",
96105
"meta": {
97106
"name": "Blackjack",
98107
"slug": "blackjack",
99108
"version": "1.0.0",
100109
"author": "card-engine",
101-
"players": { "min": 1, "max": 6 }
110+
"players": { "min": 1, "max": 6 },
111+
"description": "Classic casino banking game — beat the dealer without going over 21.",
112+
"tags": ["casino", "banking", "classic"],
113+
"license": "public-domain"
102114
}
103115
}
104116
```
105117

106118
- `slug` must be lowercase alphanumeric with hyphens only (regex: `^[a-z0-9-]+$`).
107119
- `version` must be a valid semver string (regex: `^\d+\.\d+\.\d+$`).
108120
- `players.min` must be less than or equal to `players.max`.
121+
- `description`, `tags`, and `license` are optional catalog fields used by the `bun run catalog` script to generate a browsable `catalog.json`.
109122

110123
---
111124

@@ -1175,14 +1188,14 @@ A JSON Schema (draft-07) is also available for editor autocompletion and
11751188
pre-commit validation:
11761189

11771190
```
1178-
packages/shared/src/schema/cardgame.v1.schema.json
1191+
packages/schema/src/schema/cardgame.v1.schema.json
11791192
```
11801193

11811194
You can reference it in your `.cardgame.json` files for editor support:
11821195

11831196
```json
11841197
{
1185-
"$schema": "../packages/shared/src/schema/cardgame.v1.schema.json",
1198+
"$schema": "../packages/schema/src/schema/cardgame.v1.schema.json",
11861199
"meta": { "..." }
11871200
}
11881201
```

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"bundle:client": "cd packages/host && bunx couch-kit bundle --source ../client --output ./android/app/src/main/assets/www --manifest ./src/www-manifest.json",
1010
"build:android": "bun run bundle:client && cd packages/host && npx expo run:android",
1111
"typecheck": "cd packages/shared && bunx tsc && cd ../client && bunx tsc --noEmit",
12-
"build": "bun run typecheck && bun run build:client && bun run bundle:client"
12+
"build": "bun run typecheck && bun run build:client && bun run bundle:client",
13+
"validate": "bun run scripts/validate-rulesets.ts",
14+
"catalog": "bun run scripts/build-catalog.ts"
1315
}
1416
}

packages/schema/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
"main": "src/index.ts",
66
"types": "src/index.ts",
77
"scripts": {
8-
"typecheck": "tsc"
8+
"typecheck": "tsc",
9+
"test": "vitest run",
10+
"test:watch": "vitest"
911
},
1012
"dependencies": {
1113
"zod": "^3.23.0"
1214
},
1315
"devDependencies": {
14-
"typescript": "~5.9.2"
16+
"typescript": "~5.9.2",
17+
"vitest": "^3.2.1"
1518
}
1619
}

0 commit comments

Comments
 (0)