@chatman-media/storage
v1.38.0
Published
PostgreSQL storage adapters for @chatman-media/sales — Drizzle ORM implementations of all engine interfaces.
Maintainers
Readme
@chatman-media/storage
PostgreSQL storage adapters for @chatman-media/sales. Drizzle ORM implementations of all engine repository interfaces — users, conversations, leads, skills, ELO ratings, self-play matches, pairwise comparisons, and shadow evaluations.
Built from the production persistence layer of the Lead Engine platform — multi-tenant SaaS with Postgres RLS.
What's inside
| Repo class | Interface | What it does |
|------------|-----------|-------------|
| PgUsersRepo | IUsersRepo | Upsert Telegram user accounts by telegramId |
| PgConversationsRepo | IConversationsRepo | Create conversations linked to a user + style slug |
| PgLeadsRepo | ILeadsRepo | Create sales leads within a conversation |
| PgSkillsRepo | ISkillsRepo | Fetch all skill definitions for a given style |
| PgSkillOutcomesRepo | ISkillOutcomesRepo | Record per-lead skill outcomes; aggregate win/loss/draw counts |
| PgStyleRatingsRepo | IStyleRatingsRepo | Get/set ELO ratings per style (default 1500, upserted on change) |
| PgSelfPlayMatchesRepo | ISelfPlayMatchesRepo | Insert & query self-play match records with full transcripts |
| PgPairwiseMatchesRepo | IPairwiseMatchesRepo | Record head-to-head comparisons between two self-play matches |
| PgShadowEvaluationsRepo | IShadowEvaluationsRepo | Track evaluation run state (status, decision, pair counts, error) |
Install
bun add @chatman-media/storage # Bun
npm install @chatman-media/storage # npm / pnpm / yarnPeer dependencies:
bun add drizzle-orm postgres @chatman-media/salesQuick start
import postgres from "postgres";
import { drizzle } from "drizzle-orm/postgres-js";
import { createPgRepos, schema } from "@chatman-media/storage";
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client, { schema });
const { users, conversations, leads, skills, outcomes, ratings, matches, pairwise, shadowEvals } =
createPgRepos(db);
// upsert a Telegram user
const { id: userId } = await users.upsert({ telegramId: 123456789, username: "alice" });
// start a conversation and a lead
const { id: convId } = await conversations.create({ userId, styleSlug: "marina-prime-v1" });
const { id: leadId } = await leads.create({ conversationId: convId });
// record a skill outcome
await outcomes.record({ skillSlug: "mirroring", styleSlug: "marina-prime-v1", leadId, outcome: "won", source: "judge" });
// read ELO rating
const elo = await ratings.getRating(42); // → 1500 (default)Storage interfaces
All classes implement interfaces from @chatman-media/sales — swap the implementation without touching engine code:
import type { ISelfPlayMatchesRepo } from "@chatman-media/sales";
// Implement for any backend (SQLite, in-memory, …):
class MyMatchesRepo implements ISelfPlayMatchesRepo {
async insert(match) { /* ... */ }
async byId(id) { /* ... */ }
async list(opts) { /* ... */ }
}Database schema
| Table | Description |
|-------|-------------|
| users | Telegram user accounts (telegram_id unique) |
| conversations | Conversations linked to a user and a style slug |
| leads | Sales leads within a conversation |
| skill_outcomes | Per-lead skill performance records (won / lost / draw) |
| style_ratings | ELO ratings per style (default 1500), upserted on change |
| skills | Skill definitions — slug, family, prompt fragment, applicable stages (JSONB) |
| self_play_matches | Single-style match records with full transcript (JSONB) |
| pairwise_matches | Head-to-head comparisons between two self-play matches |
| shadow_evaluations | Evaluation run state — status, decision, pair counts, error |
Architecture
@chatman-media/storage
├── schema.ts 9 Drizzle table definitions (pgTable)
├── pg/
│ └── index.ts All repo classes + createPgRepos()
└── index.ts Public exportsDepends on @chatman-media/sales for repo interfaces and domain types (SelfPlayMatchRecord, SkillAggregate, SkillRow, …).
Environment variables
| Variable | Description |
|----------|-------------|
| DATABASE_URL | PostgreSQL connection string — used by drizzle-kit CLI commands |
Commands
| Command | Description |
|---------|-------------|
| npm run build | Bundle src/index.ts → dist/ with type declarations |
| npm run typecheck | TypeScript type check without emitting |
| npm run check | Lint source with Biome |
| npm run format | Auto-format source with Biome |
| npm run db:generate | Generate Drizzle migration files from schema changes |
| npm run db:migrate | Apply pending migrations to the database |
| npm test | Run tests with bun |
Migrations
# After changing src/schema.ts, generate a new migration:
npm run db:generate
# Apply all pending migrations:
DATABASE_URL=postgres://... npm run db:migrateMigration files are written to ./migrations/ and tracked in version control.
License
MIT — Alexander Kireev / chatman-media
