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 sales-guru — a Telegram recruitment bot that runs 24/7 qualifying inbound candidates for foreign work contracts.
| 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) |
bun add @chatman-media/storage # Bun
npm install @chatman-media/storage # npm / pnpm / yarnPeer dependencies:
bun add drizzle-orm postgres @chatman-media/salesimport 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)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) { /* ... */ }
}| 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 |
@chatman-media/storage
├── schema.ts 9 Drizzle table definitions (pgTable)
├── pg/
│ └── index.ts All repo classes + createPgRepos()
└── index.ts Public exports
Depends on @chatman-media/sales for repo interfaces and domain types (SelfPlayMatchRecord, SkillAggregate, SkillRow, …).
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string — used by drizzle-kit CLI 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 |
# 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.
MIT — Alexander Kireev / chatman-media