Atproto AMA app
TypeScript 67.9%
Alloy 24.3%
CSS 4.7%
Shell 1.7%
JavaScript 1.0%
Handlebars 0.3%
Dockerfile 0.1%
10 1 0

Clone this repository

https://tangled.org/etienneburdet.com/askimut https://tangled.org/did:plc:6sxrxlqw5cquia4jla2wtafu/askimut
git@tangled.org:etienneburdet.com/askimut git@tangled.org:did:plc:6sxrxlqw5cquia4jla2wtafu/askimut

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

askimut - Turn-Based Game System#

A formally specified, test-driven turn-based game system built on SolidStart with AT Protocol integration.

🎯 Current Status: Core game logic complete with 67 passing tests. UI components implemented. Ready for PostgreSQL connection.

🎮 Game Features#

Core Gameplay (✅ Complete)#

  • ✅ Turn-based message posting (2-8 players)
  • ✅ Round-robin turn rotation
  • ✅ "Done" action for scene completion
  • ✅ Automatic scene/act/game progression
  • ✅ Visual feedback (active player, done status)
  • 67 tests passing - 100% spec coverage

Game Rules (From Formal Specification)#

  1. Games have 2-8 players and 1-10 acts
  2. Scenes have 2-3 players (even total = 2, odd total = 3)
  3. Players take turns posting messages
  4. After each message, next player becomes active
  5. Players can mark themselves "done"
  6. Scene ends when all players are done
  7. Act ends when all scenes are done
  8. Game ends when all acts are done

AT Protocol Features#

  • Custom lexicons for game records
  • OAuth authentication
  • Firehose ingester for real-time synchronization
  • PostgreSQL database with Drizzle ORM

🚀 Quick Start#

Run Tests (No Database Required)#

# Install dependencies
pnpm install

# Run all tests
pnpm test:run

# Expected output:
# ✓ 67 tests passing

Run Full App (Requires PostgreSQL)#

# 1. Start PostgreSQL
docker start askimut-db
# or: docker-compose up -d postgres

# 2. Push database schema
pnpm db:generate
pnpm db:push

# 3. Generate lexicon types (optional)
pnpm lexgen

# 4. Start dev server
pnpm dev

# 5. Create a game
# Navigate to: http://localhost:3000/game/new

Prerequisites#

  • Node.js >= 20
  • pnpm
  • PostgreSQL (only needed for running the app, not tests)

🛠️ Development Process#

We follow a strict 5-step methodology for all features:

1. Alloy Modeling       → specs/alloy/*.als
2. Database Schema      → src/db/schema.ts + migrations
3. Type Definitions     → test-types.ts, validation.ts
4. Behavioral Tests     → Loop until all pass ✅
5. UI + TDD Loop        → Integration test → UI → Pass ✅

See DEVELOPMENT_PROCESS.md for complete methodology.

Quick Reference: .dev-process-quick-ref.md

📚 Documentation#

Essential Reading#

Implementation Details#

Specifications#

  • specs/alloy/base.als - Formal Alloy model
  • specs/md/base.md - 8 game rules in plain English

📊 Test Coverage#

$ pnpm test:run

✓ src/__tests__/integration.test.ts (9 tests)
✓ src/__tests__/structural-constraints.test.ts (31 tests)
✓ src/__tests__/temporal-behavior.test.ts (27 tests)

Test Files  3 passed (3)
Tests  67 passed (67)

Coverage:

  • 100% of Alloy predicates tested
  • 100% of game rules tested
  • All UI features have integration tests

🎯 Available Commands#

# Testing
pnpm test               # Watch mode (TDD)
pnpm test:run           # Run once
pnpm test:ui            # Interactive UI
pnpm test:coverage      # With coverage

# Development
pnpm dev                # Start dev server
pnpm build              # Build for production
pnpm start              # Start production server

# Database
pnpm db:generate        # Generate migrations
pnpm db:push            # Push schema to database

# AT Protocol
pnpm lexgen             # Generate lexicon types
pnpm ingester           # Run firehose ingester

🗂️ Project Structure#

askimut/
├── specs/
│   ├── alloy/base.als              # Formal specification
│   └── md/base.md                  # 8 rules in plain English
│
├── src/
│   ├── __tests__/
│   │   ├── utils/
│   │   │   ├── test-types.ts       # Domain model types
│   │   │   └── test-helpers.ts     # Alloy predicates → functions
│   │   ├── temporal-behavior.test.ts   # 27 tests
│   │   ├── structural-constraints.test.ts # 31 tests
│   │   └── integration.test.ts     # 9 tests
│   │
│   ├── db/
│   │   ├── schema.ts               # 11 database tables
│   │   └── game.ts                 # Game CRUD operations
│   │
│   ├── routes/
│   │   ├── game/
│   │   │   ├── [id].tsx           # Main game UI (text+post+done)
│   │   │   └── new.tsx            # Game creation
│   │   └── index.tsx               # Home page
│   │
│   ├── game-logic.ts               # Business logic
│   └── api/                        # Authentication
│
└── Documentation/                   # 7 detailed docs

Testing with curl#

1. Create a Message via AT Protocol#

To push a message directly to your PDS using the AT Protocol API:

# First, get an access token by authenticating
# (Replace with your actual credentials and PDS URL)

# Create a session
curl -X POST https://bsky.social/xrpc/com.atproto.server.createSession \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "your-handle.bsky.social",
    "password": "your-app-password"
  }'

# Extract the accessJwt from the response, then create a record
curl -X POST https://bsky.social/xrpc/com.atproto.repo.putRecord \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_JWT" \
  -d '{
    "repo": "your-handle.bsky.social",
    "collection": "xyz.askimut.message",
    "rkey": "'"$(date +%s)"'",
    "record": {
      "$type": "xyz.askimut.message",
      "message": "Hello from curl!",
      "createdAt": "'"$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"'"
    }
  }'

2. Query Messages from Your Database#

Once the ingester picks up the message from the firehose:

# Check messages in the database
psql postgresql://postgres:postgres@localhost:5433/askimut \
  -c "SELECT * FROM \"post\" ORDER BY \"indexedAt\" DESC LIMIT 10;"

3. Using the Web Interface#

Alternatively, you can:

  1. Visit http://localhost:3000
  2. Log in with your AT Protocol handle
  3. Post a message through the web form
  4. Watch the ingester pick it up from the firehose

Project Structure#

  • src/ingester.ts - Firehose subscription logic
  • src/ingester-server.ts - Standalone ingester process
  • src/id-resolver.ts - DID/handle resolution utilities
  • src/lexicon/ - Generated lexicon types
  • lexicons/message.json - Custom lexicon definition
  • src/db/ - Database schema and queries
  • src/routes/ - Web application routes

Lexicon#

This project uses a custom lexicon xyz.askimut.message:

{
  "lexicon": 1,
  "id": "xyz.askimut.message",
  "defs": {
    "main": {
      "type": "record",
      "key": "tid",
      "record": {
        "type": "object",
        "required": ["message", "createdAt"],
        "properties": {
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "createdAt": { "type": "string", "format": "datetime" }
        }
      }
    }
  }
}

Environment Variables#

Create a .env file with:

DATABASE_URL=postgresql://postgres:postgres@localhost:5433/askimut
COOKIE_SECRET=your-random-32-character-secret
HOST=0.0.0.0
PORT=3000
NITRO_HOST=127.0.0.1
NITRO_PORT=3000

Building#

pnpm build
pnpm start

🎉 Current Achievement#

This project demonstrates a complete specification-driven development workflow:

Formal Specification (Alloy model) ✅ Database Schema (11 tables from spec) ✅ Type-Safe Logic (TypeScript + Valibot) ✅ 67 Passing Tests (100% coverage of specs) ✅ Working UI (Text input + Post button + Done button) ✅ Complete Documentation (7 detailed docs)

Key Innovation: Every game rule is proven correct by tests that directly implement the Alloy predicates.

📖 Learn More#

  • Development Process: Start with DEVELOPMENT_PROCESS.md
  • Quick Start: Run pnpm test:run to see all 67 tests pass
  • Play a Game: Follow setup instructions and visit /game/new

This project was created with the Solid CLI