All files / web/src/db/schema number-line-moments.ts

100% Statements 108/108
100% Branches 4/4
100% Functions 0/0
100% Lines 108/108

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 1092x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 39x 39x 39x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 39x 39x 39x 2x 2x 2x 2x 2x 37x 2x 2x 2x 37x 37x 37x 37x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x  
import { createId } from '@paralleldrive/cuid2'
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { relations } from 'drizzle-orm'
import type { MomentSnapshot } from './number-line-postcards'
 
// ── Sessions table — one row per voice call ──
 
export const numberLineSessions = sqliteTable(
  'number_line_sessions',
  {
    id: text('id').primaryKey(), // cuid generated client-side at call start
 
    playerId: text('player_id').notNull(),
    callerNumber: integer('caller_number', { mode: 'number' }).notNull(),
 
    /** Has the post-call LLM cull pass completed? */
    isCulled: integer('is_culled', { mode: 'boolean' }).notNull().default(false),
 
    /** Background task ID for the cull job */
    cullTaskId: text('cull_task_id'),
 
    /** LLM-generated 1-2 sentence summary of the call from the number's perspective */
    sessionSummary: text('session_summary'),
 
    /** Number of moments marked during this session */
    momentCount: integer('moment_count').notNull().default(0),
 
    createdAt: integer('created_at', { mode: 'timestamp' })
      .notNull()
      .$defaultFn(() => new Date()),
 
    /** Set on hang_up or inferred from last moment + timeout */
    endedAt: integer('ended_at', { mode: 'timestamp' }),
  },
  (table) => ({
    playerCallerIdx: index('nl_sessions_player_caller_idx').on(table.playerId, table.callerNumber),
    isCulledIdx: index('nl_sessions_is_culled_idx').on(table.isCulled),
  })
)
 
// ── Moments table — individual bookmarked moments ──
 
export const numberLineMoments = sqliteTable(
  'number_line_moments',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => createId()),
 
    playerId: text('player_id').notNull(),
    callerNumber: integer('caller_number', { mode: 'number' }).notNull(),
    sessionId: text('session_id').notNull(),
 
    caption: text('caption').notNull(),
    category: text('category').notNull(), // question|discovery|game|exploration|conversation|conference
 
    /** Agent's in-the-moment significance score (1-10) */
    rawSignificance: integer('raw_significance').notNull(),
 
    /** LLM post-cull score (1-10), null until culled */
    longTermSignificance: integer('long_term_significance'),
 
    /** Survives culling? Default true until cull pass runs */
    keep: integer('keep', { mode: 'boolean' }).notNull().default(true),
 
    /** Viewport state at the moment */
    snapshot: text('snapshot', { mode: 'json' }).$type<MomentSnapshot>(),
 
    /** 2-4 transcript lines around the moment */
    transcriptExcerpt: text('transcript_excerpt'),
 
    createdAt: integer('created_at', { mode: 'timestamp' })
      .notNull()
      .$defaultFn(() => new Date()),
  },
  (table) => ({
    playerCallerIdx: index('nl_moments_player_caller_idx').on(table.playerId, table.callerNumber),
    sessionIdx: index('nl_moments_session_idx').on(table.sessionId),
  })
)
 
// ── Relations ──
 
export const numberLineSessionsRelations = relations(numberLineSessions, ({ many }) => ({
  moments: many(numberLineMoments),
}))
 
export const numberLineMomentsRelations = relations(numberLineMoments, ({ one }) => ({
  session: one(numberLineSessions, {
    fields: [numberLineMoments.sessionId],
    references: [numberLineSessions.id],
  }),
}))
 
// ── Types ──
 
export type NumberLineSession = typeof numberLineSessions.$inferSelect
export type NewNumberLineSession = typeof numberLineSessions.$inferInsert
export type NumberLineMoment = typeof numberLineMoments.$inferSelect
export type NewNumberLineMoment = typeof numberLineMoments.$inferInsert
 
export type MomentCategory =
  | 'question'
  | 'discovery'
  | 'game'
  | 'exploration'
  | 'conversation'
  | 'conference'